Browse Source

feat: page

xutongzee 1 year ago
parent
commit
082c700d1a

+ 9 - 8
TODO.md

@@ -7,15 +7,16 @@
 * [ ] 审批详情 底部的 各种功能ICONS 需要设计师给予
 * [ ] 审批详情 顶部 ICON需要 设计切图
 
-### 需要后端处理 才能做的
-
-* [ ] 我的申请记录缺少时间查询字段
-* [x] 个人申请详情无法查看
 * [ ] 我的出差-详情页面缺少 发起人字段
-* [ ] 我的出差-详情页面 流程中缺少发起人数据
-* [ ] 抄送人是否查看的字段
 
 
-## 今日完成
+#### 个人中心页面
+
+* [ ] 个人中心`tag`Icons需要补充
+* [ ] 个人中心头像和签名图片是走钉钉。 尚未自测
 
-* 用车申请表单页 60%
+### 需要后端处理 才能做的
+
+* [x] 个人申请详情无法查看
+* [ ] 我的出差-详情页面 流程中缺少发起人数据
+* [ ] 个人中心-用户详情 缺少学校字段

+ 15 - 7
src/App.vue

@@ -11,6 +11,8 @@ import TabBar from '@/components/TabBar'
 // import store from './store';
 // import NavBar from '@/components/NavBar'
 
+import { platform } from '@/utils/dingtalk.js'
+
 
 export default {
   components: {
@@ -50,14 +52,20 @@ export default {
     }
   },
   created() {
-    let list = this.$store.state.enum.evectionTypeList
-    if (!list.length) {
-      this.$store.dispatch('enum/getTypeList')
-    }
-
     try {
-      // NOTE: 静默获取用户token
-      this.$store.dispatch('user/login')
+      // NOTE: 获取各种类型枚举
+      let list = this.$store.state.enum.evectionTypeList
+      if (!list.length) {
+        this.$store.dispatch('enum/getTypeList')
+      }
+
+
+      // NOTE: 只有钉钉环境下才能获取静态Code
+      if (platform !== 'notInDingTalk') {
+        // NOTE: 静默获取用户Token
+        this.$store.dispatch('user/login')
+      }
+
       
     } catch (e) {} /* eslint-disable-line */
 

+ 4 - 55
src/api/common.js

@@ -9,59 +9,8 @@ export const getTypeList = () => (request({
     url: "common/get_type_list"
 }))
 
-/*
-// 获取出差类型(室内室外)
-export const getAwayType = () => (request({
-    method: 'POST',
-    url: "common/get_evection_type_list",
-}))
-
-
-// 模块列表
-export const getModuleList = () => (request({
-    method: 'POST',
-    url: 'get_module_list'
-}))
-
-// 缓急程度列表
-export const getDegreeList = () => (request({
-    method: 'POST',
-    url: 'get_degree_list'
-}))
-
-// 采购类型列表
-export const getApplyTypeList = () => (request({
-    method: 'POST',
-    url: 'get_apply_type_list'
-}))
-
-// 采购支付方式列表
-export const getApplyPayTypeList = () => (request({
-    method: 'POST',
-    url: 'get_apply_pay_type_list'
-}))
-
-// 呈批类型列表
-export const getOfferTypeList = () => (request({
-    method: 'POST',
-    url: 'get_offer_type_list'
-}))
-
-// 请假类型列表
-export const getLeaveTypeList = () => (request({
-    method: 'POST',
-    url: 'get_leave_type_list'
-}))
-
-// 维修类型列表
-export const getMaintainTypeList = () => (request({
-    method: 'POST',
-    url: 'get_maintain_type_list'
-}))
-
-// 合同类型列表
-export const getContractTypeList = () => (request({
-    method: 'POST',
-    url: 'get_contract_type_list'
+// 获取前端鉴权心要字段
+export const getAuthentication = () => (request({
+    method: "POST",
+    url: ""
 }))
-*/

+ 9 - 1
src/api/member.js

@@ -22,5 +22,13 @@ export const getUserinfo = () => (request({
 export const putUserInfo = (data) => (request({
     data,
     method: 'POST',
-    url: '/member/edit'
+    url: 'member/edit'
+}))
+
+/**
+ * @description 获取用户审批待处理统计
+ */
+export const getUserApproveCount = () => (request({
+    method: 'POST',
+    url: 'member/get_count',
 }))

+ 20 - 3
src/router/index.js

@@ -27,18 +27,20 @@ const routes = [
     name: 'Applyfor',
     component: () => import(/* webpackChunkName: "index" */ '../views/applyfor/index.vue')
   },
+
   {
+    // NOTE: 自己书写外部出差人员
     path: '/applyfor/peers-out-form',
     name: 'PeersOutForm',
     component: () => import(/* webpackChunkName: "index" */ '../views/applyfor/peersOutForm.vue')
   },
+
   {
     path: '/applyfor/type6-before',
     name: 'AskForLeave',
-    component: () => import(/* webpackChunkName: "type6" */ '../views/applyfor/')
+    component: () => import(/* webpackChunkName: "type6" */ '../views/applyfor/askForLeaveType.vue')
   },
 
-
   // NOTE:我的审核状态
   {
     path: '/apply-state',
@@ -86,14 +88,29 @@ const routes = [
   {
     path: '/user-info',
     name: 'Userinfo',
-    component: () => import(/* webpackChunkName: "personnel" */ '../views/Userinfo.vue')
+    component: () => import(/* webpackChunkName: "personnel" */ '../views/personal/Userinfo.vue')
   },
   {
     path: '/user-info/single-info',
     name: 'Singleinfo',
     component: () => import(/* webpackChunkName: "personnel" */ '../views/personal/single-info.vue')
   },
+  {
+    path: '/update/nickname',
+    name: 'Nickname',
+    component: () => import(/* webpackChunkName: "personnel" */ '../views/personal/nickname.vue')
+  },
 
+  {
+    path: '/update/mobile',
+    name: 'Mobile',
+    component: () => import(/* webpackChunkName: "personnel" */ '../views/personal/phone-number.vue')
+  },
+  {
+    path: '/update/signature',
+    name: 'Signature',
+    component: () => import(/* webpackChunkName: "personnel" */ '../views/personal/signature.vue')
+  },
   
 ]
 

+ 1 - 0
src/store/getter.js

@@ -1,5 +1,6 @@
 const getter = {
     name: state => state.user.name,
+    school: state => state.user.schoolName,
     token: state => state.user.token,
     tabbarAction: state => {
         return state.app.tabbarAction

+ 12 - 2
src/store/modules/ddtalk.js

@@ -1,9 +1,19 @@
-
+import { getAuthentication } from "@/api/common" 
 const state = {
     name: 'unknow'
 }
 const mutations = {}
-const actions = {}
+const actions = {
+    authentication (params) {
+        return new Promise((resolve) => {
+            getAuthentication(params).then(result => {
+                if (result.code === 1) {
+                    resolve(result.data)
+                }
+            })
+        })
+    }
+}
 
 export default {
     namespaced: true,

+ 18 - 1
src/store/modules/enum.js

@@ -36,12 +36,29 @@ const state = {
     ]
 }
 
+
+/**
+ * 根据id在数据中返回相应的枚举值
+ * @param {array} arrs 数据列表{name,id}[]
+ * @param {number} id 需要获取枚举的id
+ * @returns {string}
+ */
+function getKeyToTxt(arrs, id) {
+    let target = arrs.filter(row => row.id === id)
+    if (!target.length) return 'UNKNOW'
+    return target[0].name
+}
+
 const getters = {
     // NOTE: 审核流程枚举
     getApproveFlowPathEnum: state => (status) => {
         if (![1, 2, 3, 4].includes(status)) return 'unknow'
         return state.approveFlowPathEnum[status]
-    }
+    },
+    // NOTE: 获取模块列表的名称; 根据ID去获取
+    getModuleText: state => id => getKeyToTxt(state.moduleList, id),
+    // NOTE: 获取缓急程度枚举
+    getDegreeText: state => id => getKeyToTxt(state.degreeList, id),
 }
 
 const mutations = {

+ 50 - 27
src/store/modules/user.js

@@ -1,51 +1,79 @@
-import { getRequestAuthCode, platform } from '@/utils/dingtalk'
+import { getRequestAuthCode } from '@/utils/dingtalk'
 import { login } from '@/api/login'
 import { getUserinfo } from '@/api/member'
 import { Toast } from 'vant'
 
-
-// import { init } from 'dingtalk-mock-sdk'
-
-// init({
-//   token: 'U3if31VghziIj3VCnSwgeHO0CVlKs7Z4',
-//   jsapiMock: true,
-//   httpMock: false,
-// })
+const signatureStateEnum = ['待审核', '已通过', '已驳回']
 
 const state = {
-    name: '刘壹手',
+    name: '',
+    schoolName: '深圳市第二特殊教育学校', // FIXME: 后端尚未提供
+    mobile: '',
+    userinfo: {},
+    signatureStateText: '',  // 个签
     token: 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1aWQiOiI4NSIsImlzcyI6Imh0dHBzOlwvXC96YWluLmNvbSIsImF1ZCI6Imh0dHBzOlwvXC96YWluLmNvbSIsImlhdCI6MTY5ODMxMjI4NSwibmJmIjoxNjk4MzEyMjg1LCJleHAiOjE3MjQyMzIyODV9.ziAXs6DiZKGGjtPuaCZ6Vfpv6Ki_deZhFPnDDLjAJUg',
 }
 
+const getters = {
+    getTags(state){
+        if (!state.userinfo.title) return []
+        let title = state.userinfo.title
+        return title.split(',')
+    },
+    getDepartments (state) {
+        let userinfo = state.userinfo
+        if (!userinfo.department) return ''
+        return userinfo.department.split(',').join('、')
+    },
+
+    // NOTE: 获取组织成员信息
+    teamMemberInfomation (state, getters) {
+        let values = [
+            state.schoolName,
+            state.name,
+            getters.getTags.join('、')
+        ]
+        let labels = [
+            '企业/组员',
+            '姓名',
+            '职务'
+        ]
+        return labels.map((label, index) => ({
+            label,
+            val: values[index]
+        }))
+    }
+}
+
 const mutations = {
     UPDATE_USER_DATA: (state, payload) => {
-        const { phone, name } = payload.data
-        state.phone = phone
-        state.name = name
+        const { mobile, name } = payload.data
         state.userinfo = payload.data
+        state.mobile = mobile
+        state.name = name
+        state.signatureStateText = signatureStateEnum[payload.data.signature_status] || ''
     },
     // 更新用户Token
     UPDATE_USER_TOKEN: (state, payload) => (state.token = payload.token),
 }
+
 const actions = {
     async getUserInfo ({ commit }) {
         try {
             const result = await getUserinfo()
             if (result.code === 1) {
                 const data = result.data
-
-                commit({
-                    type: 'UPDATE_USER_DATA',
-                    data
-                })
-
-                // phone	int			手机号  
+                // mobile	int			手机号  
                 // name	string			姓名  
                 // nickname	string		昵称  
                 // headimg	string			头像地址  
                 // title	string			职称  
                 // department	string		部门(多个部门以逗号分隔)  
                 // signature	string		个人签名  
+                commit({
+                    type: 'UPDATE_USER_DATA',
+                    data
+                })
             }
         } catch (error) {
             Toast({
@@ -57,13 +85,7 @@ const actions = {
 
     // 登录获取token
     async login ({ dispatch, commit }) {
-        try {
-            if (platform === 'notInDingTalk') {
-                Toast({
-                    message: '当前应用不在钉钉使用无法登录'
-                })
-                return
-            }
+        try { 
             const code = await getRequestAuthCode()
             const result = await login(code)
             if (result.code === 1) {
@@ -82,6 +104,7 @@ const actions = {
 export default {
     namespaced: true,
     state,
+    getters,
     mutations,
     actions
 }

+ 1 - 1
src/styles/variables.less

@@ -1,7 +1,7 @@
-
 @text-h1-size: 38px;
 @text-h2-size: 30px;
 @text-h3-size: 24px;
+
 @text-common-size: 16px;
 @text-secondery-size: 14px; // 小号文字. 例如选择附件的 "附件"模块
 @text-xsecondery-color: rgba(25, 26, 30, 1); // 小号颜色. 例如选择附件的 "附件"模块

+ 81 - 1
src/utils/approve-item.js

@@ -2,9 +2,49 @@
  * @description 设置审核Row的数据内容等
  */
 
+import store from "@/store"
+
+const degree2Txt = store.getters['enum/getDegreeText']
+
 export function formatApproveItemRow(data, type) {
     let arrs = []
     switch (type) {
+        case 1:
+            arrs = [
+                {
+                    label: '申请事由',
+                    val: data.reason
+                },
+                {
+                    label: '申购类型',
+                    val: data.type
+                },
+                {
+                    label: '申购商品',
+                    val: ''
+                }
+            ]
+            break
+        case 2:
+            arrs = [
+                { label: '申请标题', val: data.reason },
+                { label: '呈批类型', val: data.type },
+                { label: '缓急程度', val: degree2Txt(data.desc) }
+            ]
+            break
+        case 3:
+            arrs = [
+                { label: '物品名称', val: '' }
+                // { label: '', val: '' },
+                // { label: '', val: '' },
+            ]
+            break
+        case 4:
+            arrs = [
+                { label: '领用物品', val: '' },
+                { label: '物品用途', val: data.reason }
+            ]
+            break
         case 5: // 出差申请展示内容
             arrs = [
                 {
@@ -21,6 +61,46 @@ export function formatApproveItemRow(data, type) {
                 }
             ]
             break
+        case 6:
+            arrs = [
+                { label: '请假类型', val: data.type },
+                { label: '开始时间', val: `${data.start_time} ${data.start_am}` },
+                { label: '结束时间', val: `${data.end_time} ${data.end_am}` }
+            ]
+            break
+        case 7:
+            arrs = [
+                { label: '用车地点', val: data.reason },
+                { label: '出发时间', val: data.start_time },
+                { label: '返回时间', val: data.end_time }
+            ]
+            break
+        case 8:
+            arrs = [
+                { label: '维修地点', val: data.reason },
+                { label: '具体内容', val: data.desc }
+            ]
+            break
+        case 9:
+            arrs = [
+                { label: '合同编号', val: data.reason },
+                { label: '拟稿日期', val: data.createTime },
+                { label: '缓急程度', val: degree2Txt(data.desc) }
+            ]
+            break
+        case 10:
+            arrs = [
+                { label: '来文单位', val: data.reason },
+                { label: '文件名称', val: data.desc }
+            ]
+            break
+        case 11:
+            arrs = [
+                { label: '文件名称', val: data.reason },
+                { label: '拟发文时间', val: data.apply_date },
+                { label: '缓急程度', val: degree2Txt(data.desc) }
+            ]
+            break
     }
     return arrs
-}
+}

+ 3 - 0
src/utils/constant.js

@@ -5,3 +5,6 @@
 //定义文件大小全局化
 export const FileSize = 50
 export const PicSize = 10
+
+// 手机号的基础正则
+export const phoneRegexp = /^1[0-9]{10}$/i

+ 81 - 7
src/utils/dingtalk.js

@@ -6,23 +6,23 @@ import setTitle from 'dingtalk-jsapi/api/biz/navigation/setTitle';
 import requestAuthCode from 'dingtalk-jsapi/api/runtime/permission/requestAuthCode'
 import requestOperateAuthCode from 'dingtalk-jsapi/api/runtime/permission/requestOperateAuthCode'
 
+import chooseImage from 'dingtalk-jsapi/api/biz/util/chooseImage'
+
 import { getENV } from "dingtalk-jsapi/lib/env";
 import { compareVersion } from "dingtalk-jsapi/lib/sdk";
 
-// import { init } from 'dingtalk-mock-sdk'
+import * as dd from 'dingtalk-jsapi/lib/sdk/sdkLib'
+import store from '@/store';
 
-// init({
-//   token: 'U3if31VghziIj3VCnSwgeHO0CVlKs7Z4',
-//   jsapiMock: true,
-//   httpMock: false,
-// })
+// import {} from 'dingtalk-jsapi/lib/packages'
 
 // 公司CorpId
 const CorpId = 'dingf1b2e9ddf9d214e224f2f5cc6abecb85' /* eslint-disable-line */
 
 // 公司APIToken
-const PpiToken = '43c2466a53613d9088e6364d554500cd' /* eslint-disable-line */
+const ApiToken = '43c2466a53613d9088e6364d554500cd' /* eslint-disable-line */
 
+// 微应用ID
 const AppId = '2796673629' /* eslint-disable-line */
 
 export const { platform, version, appType } = getENV()
@@ -150,4 +150,78 @@ export function getRequestOperateAuthCode () {
          
         })
     })
+}
+
+
+/**
+ * 
+ * @tips: 需要鉴权
+ * @param {Object} options 配置`chooseImage`的选项
+ * @returns 选中的图片
+ */
+export function getChooseImages (options) {
+    const {
+        count = 1,
+        secret = false,
+        sourceType =  ['camera'],
+        position = 'back'
+    } = options
+    // sourceType =  ['camera','album']
+    return new Promise((resolve, reject) => {
+        chooseImage({
+            count, // 可以选择几张图片
+            secret,
+            sourceType, // 选择图片或者拍照
+            position,
+            onSuccess: (res) => {
+                resolve(res)
+            },
+            onFail:(err) =>{
+                reject(err)
+            }
+        })
+    })
+}
+
+/**
+ * @description dingtalk 鉴权
+ * @param {object} options jsApiList, errorCallback, readyCallback
+ */
+export function waitAuthentication (options) {
+    (async function(d, ops){
+        "use strict";
+
+        const {
+            jsApiList = [],
+
+            errorCallback,
+            readyCallback,
+        } = ops || {}
+
+        const {
+            timeStamp,
+            nonceStr,
+            signature
+        } = await store.dispatch('ddtalk/authentication', {
+            href: window.location.origin // 网站源网址, 不包含#号与后边内容
+        })
+
+        d.config({
+            agentId: AppId, // 必填,微应用ID
+            corpId: CorpId,//必填,企业ID
+            timeStamp, // 必填,生成签名的时间戳
+            nonceStr, // 必填,自定义固定字符串。
+            signature, // 必填,签名
+            type: 0,   //选填。0表示微应用的jsapi,1表示服务窗的jsapi;不填默认为0。该参数从dingtalk.js的0.8.3版本开始支持
+            jsApiList // 必填,需要使用的jsapi列表,注意:不要带dd。
+        })
+
+        d.ready(() => {
+            readyCallback && readyCallback()
+        })
+
+        d.error((error) => {
+            errorCallback && errorCallback(error)
+        })
+    })(dd, options);
 }

+ 11 - 4
src/utils/util.js

@@ -1,7 +1,7 @@
 /**
  * @Descriptions util 工具
  */
-
+import { phoneRegexp } from './constant'
 
 /**
  * @description 判断操作系统是Android/iOS
@@ -30,8 +30,6 @@ export const fZero = n => (n > 9 ? `${n}` : `0${n}`)
  */
 export const getStringTye = variable => (Object.prototype.toString.call(variable, variable).split(' ')[1].slice(0, -1))
 
-
-
 /**
  * 获取静态资源基本信息
  * @param {String} url 静态资源链接
@@ -103,4 +101,13 @@ export const getByteShowSize = size => {
         return (size / Math.pow(num, 3)).toFixed(2) + "G"; //G
     return (size / Math.pow(num, 4)).toFixed(2) + "T"; //T
 
-}
+}
+
+const checkRegexp = regexp => (str) => regexp.test(str)
+
+/**
+ * 检查手机号是否符合规范
+ * @param {String} phonenumber
+ * @returns {boolean}
+ */
+export const checkMobile = checkRegexp(phoneRegexp)

+ 151 - 68
src/views/Approve.vue

@@ -1,13 +1,14 @@
 <template>
   <div class="approve-container flex flex-col">
-    <van-tabs v-model="activeName">
+    <van-tabs v-model="tabVal"
+      :before-change="handleTabBeforeChangeEvnet"
+    >
       <van-tab
         v-for="(tab, idx) in tabs"
         :key="idx"
         :title="tab.title"
         :name="tab.name"
         color="#000"
-        @before-change="handleTabBeforeChangeEvnet"
         />
     </van-tabs>
     <div class="filter-container p-h-12 flex flex-row flex-row-aic">
@@ -31,20 +32,35 @@
       </div>
     </div>
     <div class="approve-main">
-      <!-- TODO:需要一个滚动框架 是否需要采用 `mescroll.js` -->
-      <approve-item
-        approve-type="xx"
-        title="刘辉提出的申请"
-        time="15:00"
-        :rows="example"
-        wait-personal="刘德华"
-        :wait-status="1"
-        :approved-status="2"
+      <van-list
+        v-model="listLoading"
+        :finished="finished"
+        :finished-text="finishedText"
+        @load="onLoadData"
+      >
+        <approve-item
+          v-for="(item, idx) in tableData"
+          :key="idx"
+          approve-type="xx"
+          :title="item.__title__"
+          :time="item.apply_date"
+          :rows="item.__rows_item__"
+          :person="tabVal != 3 ? item.approve_one.user.name : ''"
+          flag="approve"
+          :flag-state="Number(tabVal)"
+          @click="handleGoInfo(item)"
+        />
+      </van-list>
+
+      <my-empty
+          v-show="showEmpty"
+          tip="暂无数据"
       />
 
-      <div class="btnbox" @click="goexamine">jumptoexamine-pass</div>
+      <!-- useless. -->
+      <!-- <div class="btnbox" @click="goexamine">jumptoexamine-pass</div>
       <div class="btnbox" @click="goexamine2">jumptoexamine-refuse</div>
-      <div class="btnbox" @click="goDetail">goDetail</div>
+      <div class="btnbox" @click="goDetail">goDetail</div> -->
 
       <!-- <my-empty tip="暂无待处理的" /> -->
     </div>
@@ -57,6 +73,7 @@
       :style="{ height: '90%' }"
       closeable
       close-icon-position="top-left"
+      @click-close-icon="handleClosePopup"
     >
       <div>
         <div class="popup__title">全部筛选</div>
@@ -64,12 +81,16 @@
             <div class="popup__typebox__header">
               <span>全部类型</span>
             </div>
-            <div class="popup__typebox__list flex">
+            <div class="popup__typebox__list">
               <span
                 v-for="(type, idx) in types"
                 :key="idx"
                 class="item"
-                @click="handleTouchThatType(event, type)"
+                :class="[
+                  'item',
+                  type.id === typeVal ? 'item--selected' : ''
+                ]"
+                @click="handleTouchThatType($event, type)"
                 >
                 {{ type.name }}
               </span>
@@ -107,12 +128,32 @@
 </template>
 
 <script>
+import store from '@/store'
 import ApproveItem from './approve/components/ApproveItem.vue'
 
+import { getApproveList } from '@/api/approve'
+import { formatApproveItemRow } from '@/utils/approve-item'
+import { mapState } from 'vuex'
+
 export default {
   components: {
     ApproveItem
   },
+  computed: {
+    ...mapState('enum', {
+      types: state => state.moduleList
+    }),
+    rendeTitleCom () {
+      let t = this.formType
+      let welcomeTxt = `${store.getters.name}提交的`
+      switch (t) {
+          case 5:
+              welcomeTxt += '出差申请'
+              break
+      }
+      return welcomeTxt
+    }
+  },
   data () {
     return {
       popupVisibility: false,
@@ -130,92 +171,129 @@ export default {
           val: '急'
         }
       ],
-      activeName: '',
+
+      tabVal: '1',
       tabs: [
         {
           title: '待处理',
-          name: 'wait'
+          name: '1'
         },
         {
           title: '已处理',
-          name: 'over'
+          name: '2'
         },
         {
           title: '我收到的',
-          name: 'recive'
+          name: '3'
         },
       ],
+
       searchVal: '',
-      types: [
-         { name: '领用申请', id: '' },
-         { name: '申请批呈', id: '' },
-         { name: '学校文件', id: '' },
-         { name: '批阅申请', id: '' },
-         { name: '合同批呈', id: '' },
-         { name: '维修申请', id: '' },
-         { name: '用车申请', id: '' },
-         { name: '请假申请', id: '' },
-         { name: '出差申请', id: '' },
-         { name: '领用申请', id: '' },
-         { name: '入库申请', id: '' },
-         { name: '申购申请', id: '' },
-      ],
+
       typeVal: '',
       timeStart: '',
-      timeEnd: ''
+      timeEnd: '',
+
+      pagination: {
+        page: 1,
+        page_num: 10,
+      },
+      showEmpty: false,
+      listLoading: false,
+      finished: false,
+      finishedText: '暂无更多数据',
+      tableData: [],
     }
   },
   methods: {
-    // Tab 切换前验证
-    handleTabBeforeChangeEvnet (name) {
-      console.log(name);
-      // TODO: 切换需要调用接口让数据加载出来。
-      return true
+    onLoadData () {
+      this.__record_list__()
     },
+    async __record_list__ () {
+      try {
+        const that = this
+        const params = {
+          status: this.tabVal,
+          module: this.typeVal || '', // 默认全部
+          start_time: this.timeStart,
+          end_time: this.timeEnd,
+        }
+        const result = await getApproveList(params)
+        if (result.code === 1) {
+          this.listLoading = false
+          let list = result.data || []
+          list = list.map(item => ({
+            ...item,
+            __title__: `${item.approve_info_user.name}提交的${that.$store.getters['enum/getModuleText'](item.type)}`,
+            __rows_item__: formatApproveItemRow(item, item.module)
+          }))
 
-    // 切换接口中转站
-    handleGetListMiddware () {
-      let type = this.activeName
-      
-      switch(type) {
-        case 'wait':
-          this.__wait__()
-        break;
-        case 'over':
-          this.__over__()
-          break;
-        case 'recive':
-          this.__recive__()
-          break;
+          if (list.length < this.pagination.page_num) this.finished = true
+          else this.pagination.page++
+
+          this.tableData = this.tableData.concat(list)
+
+          if (this.finished && !this.tableData.length) {
+            this.finishedText = ''
+            this.showEmpty = true
+          }
+        }
+      } catch (error) {
+        console.log('%c approve record_list error >>>', 'background: blue; color: #fff', error);
       }
     },
-    __wait__ () {},
-    __over__ () {},
-    __recive__ () {},
+    handleTabBeforeChangeEvnet (val) {
+      this.tabVal = val
+      this.showEmpty = false
+      this.tableData = []
+      this.listLoading = true
+      this.finished = false
+      this.finishedText = '暂无更多数据'
+      this.pagination.page = 1
+      this.timeStart = ''
+      this.timeEnd = ''
+      this.typeVal = ''
+      this.onLoadData()
+      return true
+    },
 
     // NOTE: choosed type
-    handleTouchThatType (type) {
-      const { name } = type
-      this.typeVal = name
+    handleTouchThatType (event, type) {
+      const { id } = type
+      this.typeVal = id
     },
 
     // NOTE: 点击跳转搜索页
     handleClickSearchBox () {
       this.$router.push({
-        name: 'Search'
+        name: 'Search',
+        query: {
+          flag: 'approve',
+          flagState: this.tabVal,
+          formType: 0
+        }
       })
     },
 
-    // NOTE: 点击筛选弹出选择内容
-    handleSwitchFilterBox () {
+    // NOTE: 提交过滤搜索条件
+    handleSubmitFilter () {
+      this.popupVisibility = false
+      this.onLoadData()
+    },
 
+    handleClosePopup () {
+      this.typeVal = ''
+      this.timeStart = ''
+      this.timeEnd = ''
     },
 
-    // NOTE: 提交过滤搜索条件
-    handleSubmitFilter () {
-      // NOTE:收集申请类型和申请时间
 
+    // TODO: 缺少module无法准确进入某一审批详情页面
+    handleGoInfo(item) {
+      console.log('%c handle Go_info  >>>', 'background: blue; color: #fff', item);
+      
     },
+
     goexamine () {
       this.$router.push({
         name: 'Examine',
@@ -318,10 +396,11 @@ export default {
       margin-bottom: 12px;
     }
     &__list {
-      flex-wrap: wrap;
-      justify-content: space-between;
+      display: grid;
+      grid-template-columns: repeat(3, 33.3%);
+
       padding-bottom: 14px;
-      span {
+      span.item {
         display: inline-block;
         width: 111px;
         height: 34px;
@@ -333,6 +412,10 @@ export default {
         font-weight: 400;
         color: #191A1E;
         margin-bottom: 8px;
+        &--selected {
+          background-color: #3290C4;
+          color: #fff;
+        } 
       }
     }
   }

+ 76 - 31
src/views/My.vue

@@ -5,14 +5,14 @@
         </div>
         <!-- 个人信息 -->
         <div class="info" @click="handleGoUserInfo">
-            <div class="p__avatar">
-                <img :src="info.avatar" :alt="info.name">
+            <div class="p__avatar" v-if="userinfo">
+                <img :src="userinfo.avatar" :alt="userinfo.name">
             </div>
             <div class="p__infobox">
-                <div class="p__name">{{ info.name }}</div>
+                <div class="p__name">{{ username }}</div>
                 <div class="p__row">
-                    <span class="p__row-schoolname">{{ info.schoolName }}</span>
-                    <span class="department">{{ info.department }}</span>
+                    <span class="p__row-schoolname">{{ schoolName }}</span>
+                    <span class="department">{{ departments }}</span>
                 </div>
                 <div class="p__tags">
                     <span class="tag" v-for="(tag, idx) in tags" :key="idx">
@@ -28,8 +28,9 @@
             <div class="scroll-box">
                 <div
                     class="pbn__item"
-                    v-for="(item, idx) in list"
+                    v-for="(item, idx) in renderList"
                     :key="idx"
+                    @click="handleGoApplyOfState(item)"
                     >
                     <div class="pbn__item__pic">
                         <img :src="item.pic" :alt="item.tit">
@@ -49,81 +50,116 @@
 </template>
 
 <script>
+import { getUserApproveCount } from '@/api/member'
+
+import { mapState, mapGetters } from 'vuex'
+
 export default {
     name: 'My',
+    computed: {
+        ...mapState('user', {
+            username: state => state.name,
+            userinfo: state => state.userinfo,
+            schoolName: state => state.schoolName
+        }),
+        ...mapGetters('user', {
+            tags: 'getTags',
+            departments: 'getDepartments'
+        })
+    },
     data () {
         return {
-            info: {
-                avatar: require('@/assets/index/index-file-req.png'),
-                name: '刘辉',
-                schoolName: '深圳市第二特殊教育学校',
-                department: '教师部',
-            },
-            tags: [
-                '职业教师',
-                '职业法师'
-            ],
+            renderList: [],
             list: [
                 {
                     pic: require('@/assets/index/index-business-shoppcar.png'),
                     tit: '采购',
-                    msgNum: 3 // 待处理消息
                 },
                 {
                     pic: require('@/assets/index/index-business-libs.png'),
                     tit: '入库',
-                    msgNum: 0
                 },
                 {
                     pic: require('@/assets/index/index-business-recive.png'),
                     tit: '领用',
-                    msgNum: 0
                 },
                 {
                     pic: require('@/assets/index/index-business-setting.png'),
                     tit: '维修',
-                    msgNum: 0
                 },
                 {
                     pic: require('@/assets/index/index-personnel-plane.png'),
                     tit: '出差',
-                    msgNum: 0
                 },
                 {
                     pic: require('@/assets/index/index-personnel-ask.png'),
                     tit: '请假',
-                    msgNum: 0
                 },
                 {
                     pic: require('@/assets/index/index-personnel-usecar.png'),
                     tit: '用车',
-                    msgNum: 0
                 },
                 {
                     pic: require('@/assets/index/index-file-req.png'),
                     tit: '申请呈批',
-                    msgNum: 0
                 },
                 {
                     pic: require('@/assets/index/index-file-contract.png'),
                     tit: '合同呈批',
-                    msgNum: 0
                 },
                 {
                     pic: require('@/assets/index/index-file-rec-approve.png'),
                     tit: '收文批阅',
-                    msgNum: 0
                 },
                 {
                     pic: require('@/assets/index/index-file-school-file.png'),
                     tit: '学校文件',
-                    msgNum: 0
                 },
             ]
         }
     },
+    async created () {
+        // NOTE: 进入当前页面更新用户信息
+        await this.$store.dispatch('user/getUserInfo')
+        this.__init__()
+    },
+
     methods: {
-        handleGoSetting() {},
+        __init__ () {
+            this.get_approve_count()
+        },
+
+        // NOTE: 获取待处理信息
+        async get_approve_count () {
+            try {
+                const result = await getUserApproveCount()
+                if (result.code === 1) {
+                    let list = [undefined].concat(this.list)
+                    let arrs = result.data
+                    this.renderList = arrs.map(item => ({
+                        module: item.module,
+                        pic: list[item.module].pic,
+                        msgNum: item.number,
+                        tit: item.module_text
+                    }))
+                }
+            } catch(e) {
+                console.log('get_approve_count error>', e);
+            }
+        },
+
+        // NOTE: 跳转申请状态列表
+        handleGoApplyOfState (row) {
+            this.$router.push({
+                name: 'ApplyState',
+                query: {
+                    type: row.module
+                }
+            })
+        },
+        handleGoSetting() {
+            this.handleGoUserInfo()
+        },
         handleGoUserInfo () {
             this.$router.push({
                 name: 'Userinfo'
@@ -160,7 +196,7 @@ export default {
     .pbn-box {
         flex: 1;
         height: 100%;
-        overflow: auto;
+        overflow: hidden;
         padding-bottom: 50px;
     }
 }
@@ -191,10 +227,12 @@ export default {
         color: rgba(10, 22, 41, 1);
     }
     &__row {
+        font-size: 24px;
+        padding-bottom: 4px;
         span {
             display: inline-block;
             font-size: 12px;
-            line-height: 20px;
+            line-height: 14px;
             font-weight: 500;
             color: rgba(114, 114, 115, 1);
         }
@@ -256,6 +294,13 @@ export default {
             }
         }
     }
-    
+}
+
+.scroll-box {
+    height: 100%;
+    overflow: auto;
+    &::-webkit-scrollbar {
+        display: none;
+    }
 }
 </style>

+ 1 - 1
src/views/apply-state/index.vue

@@ -242,7 +242,7 @@ export default {
         onLoadData () {
             this.__record_list__()
         },
-        // Tab 切换前验证
+
         handleTabBeforeChangeEvnet(val) {
             this.tabVal = val
             this.showEmpty = false

+ 127 - 1
src/views/applyfor/askForLeaveType.vue

@@ -1,12 +1,138 @@
 <template>
     <div class="ask-for-leave-container">
-        ask-for-leave
+        <div class="header">
+            <span>请先选择请假类型</span>
+        </div>
+        <div class="rows-container">
+
+            <div class="rows__item flex flex-row flex-row-aic"
+                v-for="(row, idx) in list"
+                :key="row.id"
+            >
+                <div class="content flex flex-row flex-row-aic">
+                    <div class="left">
+                        <span :class="['color', `color--${idx}`]"></span>
+                        <span class="label">{{ row.name }}</span>
+                    </div>
+                    <div class="midd-line"></div>
+                </div>
+                <div class="right">
+                    <span>按照半天请假</span>
+                    <!-- life 剩余多少假期等等 -->
+                </div>
+                <van-icon name="arrow" :size="20" />
+            </div>
+        </div>
     </div>
 </template>
 
+<style lang="less" scoped>
+.ask-for-leave-container {
+    .header {
+        font-size: 12px;
+        font-weight: 400;
+        color: #727273;
+        line-height: 24px;
+    }
+
+    .rows {
+        &-container {
+        }
+        
+        &__item {
+            justify-content: space-between;
+            padding: 8px 18px;
+            margin-bottom: 10px;
+            background: #FFFFFF;
+            &:last-child {
+                margin-bottom: initial;
+            }
+            .left {
+                font-size: 14px;
+                width: 100px;
+                padding-right: 10px;
+                .color {
+                    display: inline-block;
+                    width: 7px;
+                    height: 7px;
+                    margin-right: 6px;
+                    border-radius: 7px;
+                    &--0 {
+                        background: #FA9340;
+                    }
+                    
+                    &--1 {
+                        background: #008BFF;
+                    }
+                    &--2 {
+                        background: #F25B48;
+                    }
+                    &--3 {
+
+                        background: #28BB86;
+                    }
+                    &--4 {
+
+                        background: #FFE052;
+                    }
+                    &--5 {
+
+                        background: #0E89FF;
+                    }
+                    &--6 {
+
+                        background: #01BD84;
+                    }
+                    &--7 {
+
+                        background: #FEE04E;
+                    }
+                    &--8 {
+                        background: #0E89FF;
+
+                    }
+                    &--9 {
+                        background: #01BD84;
+
+                    }
+                    &--10 {
+                        background: #FEE04E;
+                    }
+                }
+                .label {
+                    font-weight: 400;
+                    color: #191A1E;
+                }
+                
+            }
+            .midd-line {
+                width: 1px;
+                min-height: 26px;
+                opacity: 0.3;
+                background-color: #979797;
+            }
+            .right {
+                font-size: 14px;
+                font-weight: 400;
+                color: #727273;
+                line-height: 20px;
+            }
+        }
+    }
+}
+</style>
+
 <script>
+import { mapState } from 'vuex';
+
+
 export default {
     name: 'AskForLeave',
+    computed: {
+        ...mapState('enum', {
+            list: state => state.leaveTypeList || []
+        })
+    },
     data () {
         return {
 

+ 12 - 26
src/views/approve/components/ApproveItem.vue

@@ -73,18 +73,19 @@ export default {
             let key = this.flagState
             return (type === 'approve' && key === 2) || (type === 'info' && key === 3)
         },
-        // TODO: 渲染内容通过(JSX/createElement)
         rowsRender () {
-            let type = this.approveType
-            let VNODE_DOM = null
-            switch (type) {
-                case 'xx':
-                    return  this.handleRender_xx()
-                case 'c123':
-                    break;
-            }
-            console.log('?', VNODE_DOM);
-            return VNODE_DOM
+            return h('ul', {
+                class: ['flex', 'flex-col']
+            },this.rows.map(row => h('li', {
+                class: ['row', 'flex', 'flex-row']
+            }, [
+                h('div', {
+                    class: ['row__title', 'flex-0shrink']
+                }, `${row.label}:`),
+                h('div', {
+                    class: ['row__content', 'ellipsis-2rows']
+                }, row.val)
+            ])))
         },
 
         // 根据 falg 和 flagState进行
@@ -153,21 +154,6 @@ export default {
         h = this.$createElement
     },
     methods: {
-        // NOTE: 审批基础内容展示框
-        handleRender_xx() {
-            return h('ul', {
-                class: ['flex', 'flex-col']
-            },this.rows.map(row => h('li', {
-                class: ['row', 'flex', 'flex-row']
-            }, [
-                h('div', {
-                    class: ['row__title', 'flex-0shrink']
-                }, `${row.label}:`),
-                h('div', {
-                    class: ['row__content', 'ellipsis-2rows']
-                }, row.val)
-            ])))
-        },
         // NOTE: 前往项目详情页
         handleGoDetail () {
             if (this.$listeners.click) this.$emit('click')

+ 27 - 20
src/views/approve/search.vue

@@ -1,6 +1,6 @@
 <template>
     <!-- 搜索页面 -->
-    <div class="search-container">
+    <div class="search-container flex flex-col">
         <div class="filter-container p-h-12 flex flex-row flex-row-aic">
             <van-field
                 v-model.trim="searchVal"
@@ -19,16 +19,17 @@
                 :finished="finished"
                 :finished-text="finishedText"
                 @load="onLoadData"
+                :immediate-check="false"
             >
                 <approve-item
                     v-for="(item, idx) in tableData"
                     :key="idx"
                     approve-type="xx"
-                    :title="rendeTitleCom"
+                    :title="item.__title__"
                     :time="item.apply_date"
                     :rows="item.__rows_item__"
                     :person="item.approve_one.user.name"
-                    flag="info"
+                    :flag="flag"
                     :flag-state="flagState"
                 />
             </van-list>
@@ -44,9 +45,9 @@
 import throttle from 'lodash/throttle'
 
 import ApproveItem from '@/views/approve/components/ApproveItem.vue'
-import store from '@/store'
 
 import { getRecordList } from '@/api/approveinfo' 
+import { getApproveList } from '@/api/approve'
 import { formatApproveItemRow } from '@/utils/approve-item'
 
 
@@ -57,9 +58,10 @@ export default {
         ApproveItem
     },
     computed: {
+        // TODO: 缺少相对应的文案
         // NOTE: 输入框的placeholder
         inputPlaceholderText () {
-            let placeholder = 'xxx'
+            let placeholder = '请输入申请内容、关键词'
             let ft = Number(this.formType)
             switch(ft) {
                 case 5:
@@ -67,18 +69,7 @@ export default {
                 break
             }
             return  placeholder
-        },
-        // NOTE: 单项标题
-        rendeTitleCom () {
-            let t = this.formType
-            let welcomeTxt = `${store.getters.name}提交的`
-            switch (t) {
-                case 5:
-                    welcomeTxt += '出差申请'
-                break
-            }
-            return welcomeTxt
-        },
+        }
     },
     data () {
         return {
@@ -99,22 +90,33 @@ export default {
             }
         }
     },
+
     created () {
         console.log('quers', this.$route.query);
         this.init()
     },
+
     methods: {
         init () {
             const { formType, flag, flagState } = this.$route.query
             this.formType = Number(formType)
             this.flagState = Number(flagState)
             this.flag = flag
-            this.apifunc = flag === 'info' ? getRecordList : () => {}
+            this.apifunc = flag === 'info' ? getRecordList : getApproveList
         },
 
         handleInputEvent: throttle(function (keyword) {
             console.log('keyword>>>', keyword);
-        }, 200),
+
+            this.tableData = []
+            this.listLoading = true
+            this.pagination.page = 1
+            this.finished = false
+            this.showEmpty = false
+            this.finishedText = '暂无更多数据了'
+
+            this.__getlist__()
+        }, 400),
 
         handleStartSearch () {
             this.tableData = []
@@ -127,6 +129,7 @@ export default {
         },
 
         onLoadData () {
+            // NOTE: 判断刚进入页面不加载
             this.__getlist__()
         },
 
@@ -150,11 +153,12 @@ export default {
                 const res = await this.apifunc(params)
                 if (res.code === 1) {
                     const THAT = this
-                    console.log(res.data);
+                    // console.log(res.data);
                     this.listLoading = false
                     let list = res.data || []
                     list = list.map(item => ({
                         ...item,
+                        __title__: `${item.approve_info_user.name}提交的${THAT.$store.getters['enum/getModuleText'](item.type)}`,
                         __rows_item__: formatApproveItemRow(item, THAT.formType)
                     }))
                     if (list.length < this.pagination.page_num) this.finished = true
@@ -204,6 +208,9 @@ export default {
             }
         }
     }
+    &-main {
+        flex: 1;
+    }
 }
 
 </style>

+ 19 - 64
src/views/Userinfo.vue → src/views/personal/Userinfo.vue

@@ -1,18 +1,15 @@
 <template>
     <div class="user-info-container userinfo">
-        <div class="backbtn p-h-12" @click="handleGoBack">
-            <van-icon name="arrow-left" size="24" />
-        </div>
         <!-- 个人信息 -->
         <div class="info p-h-12">
             <div class="p__avatar">
-                <img :src="info.avatar" :alt="info.name">
+                <img :src="userinfo.avatar" :alt="userinfo.name">
             </div>
             <div class="p__infobox">
-                <div class="p__name">{{ info.name }}</div>
+                <div class="p__name">{{ username }}</div>
                 <div class="p__row">
-                    <span class="p__row-schoolname">{{ info.schoolName }}</span>
-                    <span class="department">{{ info.department }}</span>
+                    <span class="p__row-schoolname">{{ schoolName }}</span>
+                    <span class="department">{{ departments }}</span>
                 </div>
                 <div class="p__tags">
                     <span class="tag" v-for="(tag, idx) in tags" :key="idx">
@@ -40,80 +37,38 @@
 </template>
 
 <script>
-import * as dd from 'dingtalk-jsapi'
+
+import { mapState, mapGetters } from 'vuex';
+
 export default {
     name: 'Userinfo',
-    data() {
-        return {
-            info: {
-                avatar: require('@/assets/index/index-file-req.png'),
-                name: '刘辉',
-                schoolName: '深圳市第二特殊教育学校',
-                department: '教师部',
-            },
-            tags: [
-                '职业教师',
-                '职业法师'
-            ],
-            groupInfo: [
-                {
-                    label: '企业/组员',
-                    val: '深圳市第二教育中学'
-                },
-                {
-                    label: '姓名',
-                    val: '刘辉'
-                },
-                {
-                    label: '职务',
-                    val: '职业教师、职业xxx'
-                }
-            ]
-        }
-    },
-    created() {
-        console.log(this.$router.history);
-        console.log(this.$router);
-    },
-    mounted () {
-        dd.ready(() => {
+    computed: {
+        ...mapState('user', {
+            username: state => state.name,
+            userinfo: state => state.userinfo,
+            schoolName: state => state.schoolName
+        }),
+        ...mapGetters('user', {
+            tags: 'getTags',
+            departments: 'getDepartments',
+            groupInfo: 'teamMemberInfomation',
+        }),
 
-        })
     },
     methods: {
-        back() {
-            console.log(123);
-            console.log(dd);
-            this.$router.replace('/my')
-            // this.$router.go(-1)
-            // this.$router.back()
-            // dd.biz.navigation.goBack()
-        },
         // NOTE: 跳转编辑页面
         handleGoEditPage () {
             this.$router.push({
                 name: 'Singleinfo'
             })
-        },
-        handleGoBack () {
-            this.$router.go(-1)
-        },
+        }
     }
 }
 </script>
 
 <style lang="less">
 @import url("@/styles/variables.less");
-
 .userinfo {
-    .backbtn {
-        text-align: left;
-        padding: 4px initial;
-        font-size: @text-secondery-size;
-        color: rgba(10, 22, 41, 1);
-        background-color: @white;
-    }
-
     .info {
         display: flex;
         flex-direction: row;

+ 20 - 2
src/views/personal/nickname.vue

@@ -13,19 +13,37 @@
 </template>
 
 <script>
+
+import updateUserInfoMixin from './updateUserInfo.mixin'
+
 export default {
+    name: 'Nickname',
+    mixins: [
+        updateUserInfoMixin
+    ],
+
+    created () {
+        this.nickname = this.$store.state.user.nickname
+    },
+
     data () {
         return {
             nickname: ''
         }
     },
 
+
     methods: {
         handleSaveOperate () {
             const nickname = this.nickname
             if (!nickname) return this.$toast.fail('昵称尚未填写')
-            // await Api
-        }
+
+            const params = {
+                type: 1,
+                nickname: this.nickname
+            }
+            this.put_userinfo(params, true)
+        },
     }
 }
 </script>

+ 26 - 8
src/views/personal/phone-number.vue

@@ -11,7 +11,7 @@
             <van-field
                 class="ninput"
                 v-if="operateState === 0"
-                v-model="phoneNumber"
+                v-model.trim="phoneNumber"
                 left-icon="edit"
                 size="large"
                 center
@@ -21,7 +21,7 @@
                 :error-message="errMsg"
                 @input="handleInputEvent"
             />
-            <span v-else class="phone">{{ phoneNumber }}</span>
+            <span v-else class="phone">{{ phoneNumber || '暂无手机号' }}</span>
         </div>
 
         <div class="btnbox" @click="handleBtnSwitch">
@@ -32,26 +32,44 @@
 </template>
 
 <script>
+import { checkMobile } from '@/utils/util';
+import updateUserInfoMixin from './updateUserInfo.mixin';
 export default {
+    mixins: [
+        updateUserInfoMixin
+    ],
     data () {
         return {
-            operateState: 0, // 0: 保存 1:修改
-            phoneNumber: '1234567',
-
+            operateState: 1, // 0: 保存 1:修改
+            phoneNumber: '',
             errMsg: ""
         }
     },
+    created () {
+        this.phoneNumber = this.$store.state.user.mobile || ''
+    },
     methods: {
         handleInputEvent(value) {
-            console.log('---', value);
-            // TODO:判断手机号格式是否正确
-            let bool = false
+            if (!value) return
+            let bool = checkMobile(value)
             if (!bool) {
                 this.errMsg = '手机号格式不正确请检查'
+                return
             }
+            this.errMsg = ''
         },
         handleBtnSwitch () {
+            if (this.operateState === 0) {
+                this.__save_phone__()
+            }
             this.operateState = this.operateState === 0 ? 1 : 0
+        },
+        __save_phone__ () {
+            const params = {
+                type: 3,
+                mobile: this.phoneNumber
+            }
+            this.put_userinfo(params, true)
         }
     }
 }

+ 65 - 7
src/views/personal/signature.vue

@@ -7,22 +7,33 @@
             <img v-else :src="img" alt="个签">
             <div class="state">
                 <van-icon :name="examineIconName" size="20" />
-                <span>{{ examineState }}</span>
+                <span>{{ signatureStateText }}</span>
             </div>
         </div>
         <div class="btnbox" @click="handleResetSignature">
             <span>重新上传</span>
-            <!-- <van-button type="default">重新上传</van-button> -->
         </div>
     </div>
 </template>
 
 <script>
+
+import { waitAuthentication, getChooseImages } from '@/utils/dingtalk'
+
+import updateUserInfoMixin from './updateUserInfo.mixin'
+
+import { mapState } from 'vuex'
+import upload from '@/utils/upload'
+
 export default {
+    mixins: [
+        updateUserInfoMixin
+    ],
     computed: {
-        examineState () {
-            return '已通过'
-        },
+        ...mapState('user', {
+            // 签名状态:0=待审核,1=审核通过,2=审核驳回  
+            signatureStateText: state => state.signatureStateText,
+        }),
         examineIconName () {
             return 'checked'
         },
@@ -32,12 +43,59 @@ export default {
             img: require('@/assets/index/index-business-libs.png'),
         }
     },
+    mounted() {
+        const that = this
+        waitAuthentication({
+            jsApiList: [],
+            errorCallback: that.handleAError.bind(that),
+            readyCallback: that.handleAReady.bind(that)
+        })
+
+    },
     methods: {
+        handleAError (error) {
+            console.log('error', error);
+        },
+        handleAReady (){
+            this.handleResetSignature()
+        },
         // NOTE:点击上传个签
-        handleUpdateSignature() {},
+        handleUpdateSignature() {
+            const params = {
+                type: 4,
+                signature: this.img
+            }
+            this.put_userinfo(params, true)
+        },
 
         // NOTE: 重新上传个签
-        handleResetSignature () {},
+        async handleResetSignature () {
+            let toastLoadingInstance = null
+            try {
+                const result = await getChooseImages({})    
+                console.log('%c chooseimg res >>>', 'background: blue; color: #fff', result);
+
+
+                toastLoadingInstance = this.$toast.loading({
+                    message: '图片上传中',
+                    duration: 0
+                })
+
+                
+                // TODO: Update image
+                const uploadResult = await upload(result.files)
+                console.log('%c upload result >>>', 'background: blue; color: #fff', uploadResult);
+                // TODO: Update userinfo
+                this.img = uploadResult
+                this.handleUpdateSignature()
+            } catch (error) {
+                console.log('getChooseImage error', error);
+            } finally {
+                if (toastLoadingInstance) {
+                    toastLoadingInstance.clear()
+                }
+            }
+        },
     }
 }
 </script>

+ 139 - 59
src/views/personal/single-info.vue

@@ -7,10 +7,10 @@
                 :key="idx"
                 @click="handleClickRow(row, idx)"
             >
-                <div class="label">{{ row.label }}</div>
+                <div class="lab">{{ row.label }}</div>
                 <div class="val">
-                    <template v-if="row.pic">
-                        <img class="avatar" :src="row.pic" :alt="row.label" />
+                    <template v-if="row.jumpto === 'avatar'">
+                        <img class="avatar" :src="row.pic" alt="avatar" />
                     </template>
                     <template v-else>
                         <span>{{ row.val }}</span>
@@ -21,17 +21,21 @@
                 </div>
             </div>
         </div>
-        <div class="row single-row singlerrr">
-            <div class="label">{{ signature.label }}</div>
-                <div class="val">
-                    <template>
-                        <!-- computed state -->
-                        <span>{{ signature.state }}</span>
-                    </template>
-                </div>
-                <div class="arrow">
-                    <van-icon name="arrow" color="rgba(194, 194, 194, 1)" size="26" />
-                </div>
+
+        <!-- 单独签名 -->
+        <div class="row single-row singlerrr"
+            @click="handleGoChange('signature')"
+        >
+            <div class="lab">{{ signature.label }}</div>
+            <div class="val">
+                <template>
+                    <!-- computed state -->
+                    <span>{{ signature.state }}</span>
+                </template>
+            </div>
+            <div class="arrow">
+                <van-icon name="arrow" color="rgba(194, 194, 194, 1)" size="26" />
+            </div>
         </div>
 
         <!-- 弹窗 更新头像 -->
@@ -51,74 +55,148 @@
 
 <script>
 
-import * as dd from 'dingtalk-jsapi'
+import { mapState, mapGetters } from 'vuex';
+
+import { waitAuthentication, getChooseImages } from '@/utils/dingtalk';
+
+import upload from '@/utils/upload';
+
+import updateUserInfoMixin from './updateUserInfo.mixin';
 
 export default {
+    name: 'SingleInfo',
+    mixins: [
+        updateUserInfoMixin
+    ],
+    computed: {
+        ...mapState('user', {
+            avatar: state => (state.userinfo.avatar || ''),
+            nickname: state => (state.userinfo.nickname || ''),
+            mobile: 'mobile',
+            signatureStateText: 'signatureStateText'
+        }),
+        ...mapGetters('user', {
+            tags: 'getTags',
+            department: 'getDepartments'
+        })
+    },
     data () {
         return {
-            infoList: [
+            infoList: [],
+            signature: {
+                label: '个人签名',
+                state: ''
+            },
+            show: false
+        }
+    },
+
+    created () {
+        this.__init__()
+    },
+
+    mounted () {
+        const that = this
+        waitAuthentication({
+            jsApiList: [],
+            errorCallback: that.handleAError.bind(that),
+            readyCallback: that.handleAReady.bind(that)
+        })
+    },
+
+    methods: {
+        handleAError (error) {
+            console.log('error', error);
+        },
+        handleAReady (){
+            console.log('%c 添加中转Key 告知用户是否可以进行上传头像 >>>', 'background: blue; color: #fff');
+        },
+        __init__() {
+            let that = this
+            that.infoList = [
                 {
                     label: '头像',
-                    pic: require('@/assets/index/index-business-libs.png'),
-                    jumpto: ''
+                    pic: that.avatar, 
+                    jumpto: 'avatar'
                 },
                 {
                     label: '昵称',
-                    val: '刘辉',
-                    jumpto: ''
+                    val: that.username,
+                    jumpto: 'nickname'
                 },
                 {
                     label: '手机号',
-                    val: '15192833176',
-                    jumpto: ''
+                    val: that.mobile || '暂无手机号',
+                    jumpto: 'mobile'
                 },
                 {
                     label: '部门',
-                    val: '教师部',
+                    val: that.department || '-',
                     isNOJump: true,
-                    jumpto: ''
                 },
                 {
                     label: '职业',
-                    val: '职业教师',
+                    val: that.tags.join(',') || '-',
                     isNOJump: true,
-                    jumpto: ''
                 }
-            ],
-            signature: {
-                label: '个人签名',
-                state: '已上传'
-            },
-            show: false
-        }
-    },
-    methods: {
-        handleClickRow(row, idx) {
-            console.log('???', row, idx);
+            ]
+            that.signature.state = that.signatureStateText
+        },
+
+        handleGoChange (state) {
+            let name = ''
+            switch (state) {
+                case 'nickname':
+                    name = 'Nickname'
+                    break
+                case 'mobile':
+                    name = 'Mobile'
+                    break
+                case 'signature':
+                    name = 'Signature'
+                    break
+            }
+
+            if (!name) return
+
+            this.$router.push({
+                name: name
+            })
+        },
 
-            // NOTE: idx === 0 调用图片接口。 更新头像
+        handleClickRow(row, idx) {
             if (idx === 0) {
                 this.show = true
+            } else if (!row.isNOJump) {
+                this.handleGoChange(row.jumpto)
             }
         },
 
-        handleChooseLocalImg() {
-            // TODO: 选择图片 需要鉴权
-            // biz.util.chooseImage
-            // 钉钉版本≥6.5.35
-            dd.biz.util.chooseImage({
-                count:1,
-                secret:false,
-                sourceType:['camera'],
-                position:'front',
-                onSuccess: (res) => {
-                console.log(JSON.stringify(res))
-                    },
-                onFail:(err) =>{
-                console.log(JSON.stringify(err))
-                    }
-            })
+        // NOTE: 更新用户头像
+        handleUpdateAvatar () {
+            const params = {
+                type: 1,
+                avatar: this.updateAvatarImg
+            }
+            this.put_userinfo(params)
+        },
+
+        async handleChooseLocalImg() {
+            try {
+                const result = await getChooseImages({})    
+                console.log('%c chooseimg res >>>', 'background: blue; color: #fff', result);
+                // TODO: Update image
+                const uploadResult = await upload(result.files)
+                console.log('%c upload result >>>', 'background: blue; color: #fff', uploadResult);
+                // TODO: Update userinfo
+                this.img = uploadResult
+                this.handleUpdateAvatar()
+            } catch (error) {
+                console.log('%c handleChooseLocalImg >>>', 'background: blue; color: #fff', error);
+            }
+
         },
+
         handleOpenCamera () {
             this.handleChooseLocalImg()
         },
@@ -129,7 +207,7 @@ export default {
 }
 </script>
 
-<style lang="less">
+<style lang="less" scoped>
 @import url("@/styles/variables.less");
 .single-info {
     .infolist,
@@ -138,23 +216,25 @@ export default {
         background-color: @white;
         padding: 0 17px 0 12px;
     }
+
     .row {
         display: flex;
         flex-direction: row;
         align-items: center;
-        // padding: 9px 17px 9px 12px;
         padding: 9px 0;
-        // margin: 0 17px 0 12px;
         border-bottom: 1px solid rgba(151, 151, 151, .3);
+
         &:last-child {
             border-bottom: initial;
         }
-        .lable {
-            font-size: @text-secondery-size;
+
+        .lab {
+            font-size: @font-size-common;
             font-weight: 400;
             color: @text-common-color;
             line-height: 20px;
         }
+
         .val {
             flex: 1;
             text-align: right;

+ 23 - 0
src/views/personal/updateUserInfo.mixin.js

@@ -0,0 +1,23 @@
+import { putUserInfo } from '@/api/member'
+
+// :1=头像地址,2=昵称,3=手机号码,4=签名  
+const map = ['', '头像', '昵称', '手机号码', '个性签名']
+
+export default {
+  methods: {
+    // NOTE: Update userinfo
+    async put_userinfo (params, isBack) {
+      try {
+          const result = await putUserInfo(params)
+          if (result.code === 1) {
+            console.log(result.data);
+            this.$toast(`更新${map[params.type]}成功`)
+            await this.$store.dispatch('user/getUserInfo')
+            isBack && this.$router.go(-1)
+          }
+      } catch (error) {
+          console.log('%c put_userinfo error >>>', 'background: blue; color: #fff', error, params);
+      }
+  }
+  }
+}