增加积分任务及操作员修改密码功能
diff --git a/backend/build.gradle b/backend/build.gradle
index ff5ab0b..631933b 100644
--- a/backend/build.gradle
+++ b/backend/build.gradle
@@ -74,7 +74,7 @@
     compile group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.9.1'
     compile group: 'log4j', name: 'log4j', version: '1.2.17'
 
-    compile group: 'com.supwisdom', name: 'payapi-sdk', version: '1.0.26-3-gde708e9'
+    compile group: 'com.supwisdom', name: 'payapi-sdk', version: '1.0.26-5-gf114ee1'
     
     implementation 'org.hamcrest:hamcrest:2.1'
 }
diff --git a/backend/src/main/java/com/supwisdom/dlpay/portal/util/PortalConstant.java b/backend/src/main/java/com/supwisdom/dlpay/portal/util/PortalConstant.java
index 8f0f157..21fc3a6 100644
--- a/backend/src/main/java/com/supwisdom/dlpay/portal/util/PortalConstant.java
+++ b/backend/src/main/java/com/supwisdom/dlpay/portal/util/PortalConstant.java
@@ -22,9 +22,9 @@
   public static final String ARTICLE_STATUS_REVIEW = "review";
   public static final String ARTICLE_STATUS_REJECT = "reject";
 
-  public static final String POINTTASK_STATUS_UNDONE = "undone";
-  public static final String POINTTASK_STATUS_DONE = "done";
-  public static final String POINTTASK_STATUS_RECEIVED = "received";
+  public static final String POINTTASK_STATUS_UNDONE = "undone";//未完成
+  public static final String POINTTASK_STATUS_DONE = "done";//已完成未领取
+  public static final String POINTTASK_STATUS_RECEIVED = "received";//已完成已领取
 
   public static final String FEEDBACK_STATUS_DELETE = "delete";
   public static final String FEEDBACK_STATUS_TOREPLY = "toreply";
diff --git a/backend/src/main/kotlin/com/supwisdom/dlpay/api/service/impl/user_service_impl.kt b/backend/src/main/kotlin/com/supwisdom/dlpay/api/service/impl/user_service_impl.kt
index a731ac1..ae8b3fd 100644
--- a/backend/src/main/kotlin/com/supwisdom/dlpay/api/service/impl/user_service_impl.kt
+++ b/backend/src/main/kotlin/com/supwisdom/dlpay/api/service/impl/user_service_impl.kt
@@ -96,10 +96,19 @@
     }
 
     override fun completePointTask(user: TBMobileUser, taskcode:String):JsonResult {
-        return if (taskcode == PointTaskCode.SIGN_IN.code) {
-            signInPointTask(user)
-        }else{
-            JsonResult.error(400,"未知的任务CODE")
+        return when (taskcode) {
+            PointTaskCode.SIGN_IN.code -> {
+                signInPointTask(user)
+            }
+            PointTaskCode.AUTH.code -> {
+                authPointTask(user)
+            }
+            PointTaskCode.CONSUME.code -> {
+                consumePointTask(user)
+            }
+            else -> {
+                JsonResult.error(400,"未知的任务CODE")
+            }
         }
     }
 
@@ -130,6 +139,55 @@
         return JsonResult.ok()
     }
 
+    fun consumePointTask(user: TBMobileUser):JsonResult{
+        val consumeResponse = userProxy.consumeTask(user.userid)
+        if (consumeResponse.retcode != 0) {
+            return JsonResult.error(400,"未完成首次消费不能领取积分")
+        }
+        val statusReponse = userProxy.getTaskStatus(user.userid, PointTaskCode.CONSUME.taskid)
+        if (statusReponse["retcode"] != 0) {
+            logger.error { "查询消费任务异常:${statusReponse["retmsg"]}" }
+            throw PortalBusinessException(statusReponse["retmsg"] as String)
+        }
+        val data = statusReponse["data"] as String
+        if (data.isNotEmpty()) {
+            return JsonResult.error(400,"已领取积分,不能重复领取")
+        }
+        val taskResponse = userProxy.userTask(UserTaskParam().apply {
+            this.userid = user.userid
+            this.taskid = PointTaskCode.CONSUME.taskid.toString()
+        })
+        if (taskResponse.retcode != 0) {
+            logger.error { "完成消费积分任务异常:${taskResponse.retmsg}" }
+            throw PortalBusinessException(taskResponse.retmsg)
+        }
+        return JsonResult.ok()
+    }
+
+    fun authPointTask(user: TBMobileUser):JsonResult{
+        if (user.userid.isNullOrEmpty()) {
+            return JsonResult.error(400,"未完成身份认证不能领取积分")
+        }
+        val statusReponse = userProxy.getTaskStatus(user.userid, PointTaskCode.AUTH.taskid)
+        if (statusReponse["retcode"] != 0) {
+            logger.error { "查询身份认证任务异常:${statusReponse["retmsg"]}" }
+            throw PortalBusinessException(statusReponse["retmsg"] as String)
+        }
+        val data = statusReponse["data"] as String
+        if (data.isNotEmpty()) {
+            return JsonResult.error(400,"已领取积分,不能重复领取")
+        }
+        val taskResponse = userProxy.userTask(UserTaskParam().apply {
+            this.userid = user.userid
+            this.taskid = PointTaskCode.AUTH.taskid.toString()
+        })
+        if (taskResponse.retcode != 0) {
+            logger.error { "完成身份认证积分任务异常:${taskResponse.retmsg}" }
+            throw PortalBusinessException(taskResponse.retmsg)
+        }
+        return JsonResult.ok()
+    }
+
     fun querySignInTaskStatus(user: TBMobileUser,list:ArrayList<PointTaskBean>,bean:PointTaskBean){
         bean.taskcode = PointTaskCode.SIGN_IN.code
         val lastTime = user.lastsignintime
@@ -147,15 +205,32 @@
         list.add(bean)
     }
 
-    fun queryAuthTaskStatus(user: TBMobileUser,list:ArrayList<PointTaskBean>,bean:PointTaskBean){
+    fun queryAuthTaskStatus(user: TBMobileUser,list:ArrayList<PointTaskBean>,bean:PointTaskBean,lasttime:Any?){
         bean.taskcode = PointTaskCode.AUTH.code
-        bean.status = PortalConstant.POINTTASK_STATUS_DONE
+        bean.status = PortalConstant.POINTTASK_STATUS_UNDONE
+        if (lasttime != null) {
+            bean.status = PortalConstant.POINTTASK_STATUS_RECEIVED
+        } else {
+            if (!user.userid.isNullOrEmpty()) {
+                bean.status = PortalConstant.POINTTASK_STATUS_DONE
+            }
+        }
         list.add(bean)
     }
 
-    fun queryConsumeTaskStatus(user: TBMobileUser,list:ArrayList<PointTaskBean>,bean:PointTaskBean){
+    fun queryConsumeTaskStatus(user: TBMobileUser,list:ArrayList<PointTaskBean>,bean:PointTaskBean,lasttime:Any?){
         bean.taskcode = PointTaskCode.CONSUME.code
-        bean.status = PortalConstant.POINTTASK_STATUS_DONE
+        bean.status = PortalConstant.POINTTASK_STATUS_UNDONE
+        if (lasttime != null) {
+            bean.status = PortalConstant.POINTTASK_STATUS_RECEIVED
+        } else {
+            if (!user.userid.isNullOrEmpty()) {
+                val consumeResponse = userProxy.consumeTask(user.userid)
+                if (consumeResponse.retcode == 0) {
+                    bean.status = PortalConstant.POINTTASK_STATUS_DONE
+                }
+            }
+        }
         list.add(bean)
     }
 
@@ -165,14 +240,18 @@
         list.add(bean)
     }
 
+    @Suppress("UNCHECKED_CAST")
     override fun queryPointTaskStatus(user: TBMobileUser): List<PointTaskBean>? {
         val result = ArrayList<PointTaskBean>()
-        val taskResponse = userProxy.tPointsTaskNoPage
+        var userid = ""
+        if (!StringUtil.isEmpty(user.userid)) {
+            userid = user.userid!!
+        }
+        val taskResponse = userProxy.getTPointsTaskNoPage(userid)
         if (taskResponse["retcode"] != 0) {
             logger.error { "查询积分任务异常" }
             throw PortalBusinessException("查询积分任务异常")
         }
-        @Suppress("UNCHECKED_CAST")
         val data = taskResponse["data"] as ArrayList<LinkedHashMap<String,Any>>
         data.forEach {
             val taskid =  it["taskid"] as Int
@@ -180,15 +259,16 @@
                 this.points = it["taskpoints"] as Int
                 this.taskname = it["taskname"] as String
             }
+            val lasttime = it["lasttime"]
             when (taskid) {
                 PointTaskCode.SIGN_IN.taskid -> {
                     querySignInTaskStatus(user,result,bean)
                 }
                 PointTaskCode.AUTH.taskid -> {
-                    queryAuthTaskStatus(user,result,bean)
+                    queryAuthTaskStatus(user,result,bean,lasttime)
                 }
                 PointTaskCode.CONSUME.taskid -> {
-                    queryConsumeTaskStatus(user,result,bean)
+                    queryConsumeTaskStatus(user,result,bean,lasttime)
                 }
                 PointTaskCode.EXCHANGE.taskid -> {
                     queryExchangeTaskStatus(user,result,bean)
diff --git a/backend/src/main/kotlin/com/supwisdom/dlpay/portal/PortalApi.kt b/backend/src/main/kotlin/com/supwisdom/dlpay/portal/PortalApi.kt
index 5e10bf5..23d7284 100644
--- a/backend/src/main/kotlin/com/supwisdom/dlpay/portal/PortalApi.kt
+++ b/backend/src/main/kotlin/com/supwisdom/dlpay/portal/PortalApi.kt
@@ -4,6 +4,7 @@
 import com.supwisdom.dlpay.api.service.UploadPicService
 import com.supwisdom.dlpay.framework.core.JwtConfig
 import com.supwisdom.dlpay.framework.core.JwtTokenUtil
+import com.supwisdom.dlpay.framework.domain.TOperator
 import com.supwisdom.dlpay.framework.redisrepo.ApiJwtRepository
 import com.supwisdom.dlpay.framework.service.OperatorDetailService
 import com.supwisdom.dlpay.framework.service.SystemUtilService
@@ -19,6 +20,7 @@
 import org.springframework.http.HttpStatus
 import org.springframework.http.ResponseEntity
 import org.springframework.security.core.context.SecurityContextHolder
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder
 import org.springframework.web.bind.annotation.*
 import org.springframework.web.multipart.MultipartHttpServletRequest
 import java.util.regex.Pattern
@@ -67,6 +69,33 @@
         return ResponseEntity.ok().body(JsonResult.ok())
     }
 
+    @RequestMapping("user/updatepwd")
+    fun updatePwd(@RequestHeader("Authorization") auth: String?,
+                  curpwd: String, newpwd: String, renewpwd: String): ResponseEntity<Any> {
+        if (auth == null) {
+            return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build()
+        }
+        val p = SecurityContextHolder.getContext().authentication
+        val oper = operatorDetailService.findByOperid(p.name)
+        val encoder = BCryptPasswordEncoder()
+        if (!encoder.matches(curpwd, oper.password)) {
+            return ResponseEntity.ok().body(JsonResult.error("当前密码错误"))
+        }
+        if (newpwd != renewpwd) {
+            return ResponseEntity.ok().body(JsonResult.error("两次密码不一致"))
+        }
+        if (newpwd.length < 6 || newpwd.length > 20) {
+            return ResponseEntity.ok().body(JsonResult.error("密码必须为6-20个字符"))
+        }
+        oper.operpwd = encoder.encode(newpwd)
+        operatorDetailService.saveOper(oper)
+        val jwt = auth.substring(jwtConfig.tokenHeader.length)
+        val claims = JwtTokenUtil(jwtConfig).verifyToken(jwt)
+        SecurityContextHolder.clearContext()
+        apiJwtRepository.deleteById(claims[ReservedClaimNames.JWT_ID].toString())
+        return ResponseEntity.ok().body(JsonResult.ok())
+    }
+
     @RequestMapping("/user/info")
     fun getUserInfo(): JsonResult? {
         return try {
@@ -89,6 +118,39 @@
 
     }
 
+    @RequestMapping("/user/updateinfo")
+    fun updateUserInfo(@RequestBody bean:TOperator): JsonResult?{
+        return try {
+            val p = SecurityContextHolder.getContext().authentication
+            val oper = operatorDetailService.findByOperid(p.name)
+            oper.opername = bean.opername
+            oper.mobile = bean.mobile
+            oper.email = bean.email
+            operatorDetailService.saveOper(oper)
+            return JsonResult.ok()
+        } catch (e: Exception) {
+            logger.error { e.message }
+            JsonResult.error("修改用户个人资料异常")
+        }
+    }
+
+    @RequestMapping("/user/data")
+    fun getUserData(): JsonResult? {
+        return try {
+            val p = SecurityContextHolder.getContext().authentication
+            val oper = operatorDetailService.findByOperid(p.name)
+            val data = HashMap<String, String?>()
+            data["opername"] = oper.opername
+            data["opercode"] = oper.opercode
+            data["mobile"] = oper.mobile
+            data["email"] = oper.email
+            JsonResult.ok().put("data", data)
+        } catch (e: Exception) {
+            logger.error { e.message }
+            JsonResult.error("查询用户个人信息异常")
+        }
+
+    }
     @RequestMapping("/user/resource")
     fun getUserResource(): JsonResult? {
         return try {
@@ -194,7 +256,7 @@
         if (map["retcode"] == "1") {
             return JsonResult.error("图片上传失败!")
         }
-        return JsonResult.ok().put("data",map)
+        return JsonResult.ok().put("data", map)
     }
 
     @RequestMapping("/column/all")
@@ -427,7 +489,7 @@
 
     @RequestMapping(value = ["/advisory/switchdisplay/{advisoryid}"], method = [RequestMethod.POST])
     fun switchAdvisoryDisplay(@PathVariable(value = "advisoryid") advisoryid: String,
-                      @RequestParam(value = "value") value: String): JsonResult? {
+                              @RequestParam(value = "value") value: String): JsonResult? {
         return try {
             val result = advisoryService.switchDisplay(advisoryid, value)
             JsonResult.ok().put("result", result)
@@ -484,7 +546,7 @@
      */
     @RequestMapping(value = ["/outlets/switchopen/{outletsno}"], method = [RequestMethod.POST])
     fun switchOutletsOpen(@PathVariable(value = "outletsno") outletsno: String,
-                              @RequestParam(value = "value") value: String): JsonResult? {
+                          @RequestParam(value = "value") value: String): JsonResult? {
         return try {
             val result = outletsService.switchOpen(outletsno, value)
             JsonResult.ok().put("result", result)
diff --git a/frontend/src/api/user.js b/frontend/src/api/user.js
index aba990c..62c9a25 100644
--- a/frontend/src/api/user.js
+++ b/frontend/src/api/user.js
@@ -26,6 +26,13 @@
   })
 }
 
+export function getData() {
+  return request({
+    url: '/user/data',
+    method: 'get'
+  })
+}
+
 export function logout() {
   return request({
     url: '/user/logout',
@@ -33,6 +40,32 @@
   })
 }
 
+export function savePwd(data) {
+  return request({
+    url: '/user/updatepwd',
+    method: 'post',
+    headers: {
+      'Content-Type': 'application/x-www-form-urlencoded'
+    },
+    data,
+    transformRequest: [function(data) {
+      let ret = ''
+      for (const it in data) {
+        ret += encodeURIComponent(it) + '=' + encodeURIComponent(data[it]) + '&'
+      }
+      return ret
+    }]
+  })
+}
+
+export function saveInfo(data) {
+  return request({
+    url: '/user/updateinfo',
+    method: 'post',
+    data
+  })
+}
+
 export function getAuthMenu(token) {
   return request({
     url: '/user/resource',
diff --git a/frontend/src/layout/components/Navbar.vue b/frontend/src/layout/components/Navbar.vue
index 0b0ebeb..d98e940 100644
--- a/frontend/src/layout/components/Navbar.vue
+++ b/frontend/src/layout/components/Navbar.vue
@@ -3,7 +3,88 @@
     <hamburger id="hamburger-container" :is-active="sidebar.opened" class="hamburger-container" @toggleClick="toggleSideBar" />
 
     <breadcrumb id="breadcrumb-container" class="breadcrumb-container" />
-
+    <el-dialog
+      title="修改密码"
+      :visible.sync="updatePwdVisable"
+      width="30%"
+    >
+      <div>
+        <el-form ref="pwdForm" :model="pwdForm" :rules="pwdRules" label-width="100px">
+          <el-form-item label="当前密码" prop="curpwd" class="form-input-item">
+            <el-input
+              v-model="pwdForm.curpwd"
+              type="password"
+              style="width:80%"
+            />
+          </el-form-item>
+          <el-form-item label="设置新密码" prop="newpwd" class="form-input-item">
+            <el-input
+              v-model="pwdForm.newpwd"
+              type="password"
+              style="width:80%"
+            />
+          </el-form-item>
+          <el-form-item label="重复新密码" prop="renewpwd" class="form-input-item">
+            <el-input
+              v-model="pwdForm.renewpwd"
+              type="password"
+              style="width:80%"
+            />
+          </el-form-item>
+        </el-form>
+      </div>
+      <div style="text-align:center">
+        <el-button
+          type="primary"
+          @click="savePwd('pwdForm')"
+        >保存
+        </el-button>
+        <el-button @click="updatePwdVisable = false">取消</el-button>
+      </div>
+    </el-dialog>
+    <el-dialog
+      title="设置个人信息"
+      :visible.sync="updateInfoVisable"
+      width="30%"
+    >
+      <div>
+        <el-form ref="infoForm" :model="infoForm" :rules="infoRules" label-width="100px">
+          <el-form-item label="登录名" prop="opercode" class="form-input-item">
+            <el-input
+              v-model="infoForm.opercode"
+              :disabled="true"
+              style="width:80%"
+            />
+          </el-form-item>
+          <el-form-item label="名称" prop="opername" class="form-input-item">
+            <el-input
+              v-model="infoForm.opername"
+              style="width:80%"
+            />
+          </el-form-item>
+          <el-form-item label="电话" prop="mobile" class="form-input-item">
+            <el-input
+              v-model="infoForm.mobile"
+              style="width:80%"
+            />
+          </el-form-item>
+          <el-form-item label="邮箱" prop="email" class="form-input-item">
+            <el-input
+              v-model="infoForm.email"
+              style="width:80%"
+            />
+          </el-form-item>
+        </el-form>
+      </div>
+      <div style="text-align:center">
+        <el-button
+          type="primary"
+          @click="saveInfo('infoForm')"
+        >保存
+        </el-button>
+        <el-button @click="updateInfoVisable = false">取消</el-button>
+      </div>
+    </el-dialog>
     <div class="right-menu">
       <template v-if="device!=='mobile'">
         <search id="header-search" class="right-menu-item" />
@@ -14,12 +95,8 @@
           <span v-html="getOperName()" />
         </div>
         <el-dropdown-menu slot="dropdown">
-          <router-link to="/">
-            <el-dropdown-item>个人信息</el-dropdown-item>
-          </router-link>
-          <router-link to="/">
-            <el-dropdown-item>修改密码</el-dropdown-item>
-          </router-link>
+          <el-dropdown-item @click.native="updateInfo('infoForm')">个人信息</el-dropdown-item>
+          <el-dropdown-item @click.native="updatePwd('pwdForm')">修改密码</el-dropdown-item>
           <el-dropdown-item divided @click.native="logout">
             <span style="display:block;">退出</span>
           </el-dropdown-item>
@@ -31,6 +108,7 @@
 
 <script>
 import { mapGetters } from 'vuex'
+import { savePwd, getData, saveInfo } from '@/api/user.js'
 import user from '@/store/modules/user'
 import Breadcrumb from '@/components/Breadcrumb'
 import Hamburger from '@/components/Hamburger'
@@ -42,6 +120,70 @@
     Hamburger,
     Search
   },
+  data() {
+    var validateRepwd = (rule, value, callback) => {
+      if (this.pwdForm.renewpwd !== this.pwdForm.newpwd) {
+        callback(new Error('两次输入密码不一致'))
+      }
+      callback()
+    }
+    var validateMobile = (rule, value, callback) => {
+      if (value !== null && value !== '') {
+        var reg = /^1[3456789]\d{9}$/
+        if (!reg.test(value)) {
+          callback(new Error('请输入有效的手机号码'))
+        }
+      }
+      callback()
+    }
+    var validateEmail = (rule, value, callback) => {
+      if (value !== null && value !== '') {
+        var reg = /^[A-Za-z0-9\u4e00-\u9fa5]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/
+        if (!reg.test(value)) {
+          callback(new Error('请输入有效的邮箱'))
+        }
+      }
+      callback()
+    }
+    return {
+      updatePwdVisable: false,
+      updateInfoVisable: false,
+      pwdForm: {
+        curpwd: '',
+        newpwd: '',
+        renewpwd: ''
+      },
+      infoForm: {
+        opercode: '',
+        opername: '',
+        mobile: '',
+        email: ''
+      },
+      pwdRules: {
+        curpwd: [
+          { required: true, message: '请输入当前密码', trigger: 'blur' }
+        ],
+        newpwd: [
+          { required: true, message: '请输入新密码', trigger: 'blur' }
+        ],
+        renewpwd: [
+          { required: true, message: '请确认新密码', trigger: 'blur' },
+          { validator: validateRepwd, trigger: 'blur' }
+        ]
+      },
+      infoRules: {
+        opername: [
+          { required: true, message: '请输入名称', trigger: 'blur' }
+        ],
+        mobile: [
+          { validator: validateMobile, trigger: 'blur' }
+        ],
+        email: [
+          { validator: validateEmail, trigger: 'blur' }
+        ]
+      }
+    }
+  },
   computed: {
     ...mapGetters([
       'sidebar',
@@ -60,6 +202,85 @@
         console.log(response)
       })
     },
+    updateInfo(formName) {
+      getData().then((response) => {
+        var data = response.data
+        this.infoForm.opercode = data.opercode
+        this.infoForm.opername = data.opername
+        this.infoForm.mobile = data.mobile
+        this.infoForm.email = data.email
+      }).catch((error) => {
+        this.$message({
+          message: error.msg || '请求异常',
+          type: 'error'
+        })
+      })
+      this.updateInfoVisable = true
+      this.$nextTick(() => {
+        this.$refs[formName].clearValidate()
+      })
+    },
+    updatePwd(formName) {
+      this.pwdForm = {
+        curpwd: '',
+        newpwd: '',
+        renewpwd: ''
+      }
+      this.$nextTick(() => {
+        this.$refs[formName].clearValidate()
+      })
+      this.updatePwdVisable = true
+    },
+    savePwd(formName) {
+      this.$refs[formName].validate((valid) => {
+        if (valid) {
+          var _this = this
+          savePwd(this.pwdForm).then(response => {
+            this.$message({
+              message: '修改密码成功,请重新登录!',
+              type: 'success',
+              duration: 1500,
+              onClose: function() {
+                _this.$store.dispatch('user/logout', true).then(() => {
+                  _this.$router.push(`/login?redirect=${_this.$route.fullPath}`)
+                }).catch((response) => {
+                  console.log(response)
+                })
+              }
+            })
+          }).catch(error => {
+            this.$message({
+              message: error.msg || '请求异常',
+              type: 'error'
+            })
+          })
+        } else {
+          return false
+        }
+      })
+    },
+    saveInfo(formName) {
+      this.$refs[formName].validate((valid) => {
+        if (valid) {
+          saveInfo(this.infoForm).then(response => {
+            this.$notify({
+              title: '成功',
+              message: '保存成功!',
+              type: 'success',
+              duration: 2000
+            })
+            this.updateInfoVisable = false
+          }).catch(error => {
+            this.$message({
+              message: error.msg || '请求异常',
+              type: 'error'
+            })
+          })
+        } else {
+          return false
+        }
+      })
+    },
     getOperName() {
       return user.state.name
     }
diff --git a/frontend/src/store/modules/user.js b/frontend/src/store/modules/user.js
index 736df15..7ff9b2b 100644
--- a/frontend/src/store/modules/user.js
+++ b/frontend/src/store/modules/user.js
@@ -77,9 +77,27 @@
   },
 
   // user logout
-  logout({ commit, state, dispatch }) {
+  logout({ commit, state, dispatch }, alreayLogout) {
     return new Promise((resolve, reject) => {
-      logout(state.token).then(() => {
+      if (!alreayLogout) {
+        logout(state.token).then(() => {
+          commit('SET_TOKEN', '')
+          commit('SET_ROLES', [])
+          commit('SET_URL', '')
+          commit('SET_MAPKEY', '')
+          commit('SET_MAPURL', '')
+          removeToken()
+          resetRouter()
+
+          // reset visited views and cached views
+          // to fixed https://github.com/PanJiaChen/vue-element-admin/issues/2485
+          dispatch('tagsView/delAllViews', null, { root: true })
+
+          resolve()
+        }).catch(error => {
+          reject(error)
+        })
+      } else {
         commit('SET_TOKEN', '')
         commit('SET_ROLES', [])
         commit('SET_URL', '')
@@ -93,9 +111,7 @@
         dispatch('tagsView/delAllViews', null, { root: true })
 
         resolve()
-      }).catch(error => {
-        reject(error)
-      })
+      }
     })
   },
 
diff --git a/frontend/src/utils/request.js b/frontend/src/utils/request.js
index 7288f82..a0b141a 100644
--- a/frontend/src/utils/request.js
+++ b/frontend/src/utils/request.js
@@ -57,7 +57,7 @@
       Message({
         message: '当前登录信息已过期,请重新登录',
         type: 'error',
-        duration: 5 * 1000
+        duration: 3 * 1000
       })
       router.push(`/login`)
     } else {