فهرست منبع

feature(用户):
1、增加登录5次失败账号锁定功能
2、强制用户登录修改初始密码。
3、系统增加水印,水印包含用户昵称和电话号码。

ZZH-wl 2 سال پیش
والد
کامیت
65049d54ad

+ 2 - 1
package.json

@@ -38,7 +38,8 @@
     "vue-i18n": "^8.26.7",
     "vue-router": "^3.5.3",
     "vuedraggable": "^2.24.3",
-    "vuex": "^3.6.2"
+    "vuex": "^3.6.2",
+    "watermark-dom": "^2.3.0"
   },
   "devDependencies": {
     "@vue/cli-plugin-babel": "^4.5.15",

+ 6 - 0
src/api/system/user.js

@@ -24,4 +24,10 @@ export default {
   doDelete(query) {
     return micro_request.postRequest(basePath, 'User', 'DeleteByIds', query)
   },
+  resetPwd(query) {
+    return micro_request.postRequest(basePath, 'User', 'ResetPassword', query)
+  },
+  changePwd(query) {
+    return micro_request.postRequest(basePath, 'User', 'ChangePassword', query)
+  },
 }

+ 21 - 2
src/store/modules/user.js

@@ -7,20 +7,25 @@ import { getToken, removeToken, setToken } from '@/utils/token'
 import { resetRouter } from '@/router'
 import { isArray, isString } from '@/utils/validate'
 import { title, tokenName } from '@/config'
+import watermark from 'watermark-dom'
 
 const state = () => ({
   id: '',
   token: getToken(),
   username: '游客',
   nickName: '',
+  phone: '',
   avatar: 'https://i.gtimg.cn/club/item/face/img/2/15922_100.gif',
+  isFirstLogin: false,
 })
 const getters = {
   id: (state) => state.id,
   token: (state) => state.token,
   username: (state) => state.username,
   nickName: (state) => state.nickName,
+  phone: (state) => state.phone,
   avatar: (state) => state.avatar,
+  isFirstLogin: (state) => state.isFirstLogin,
 }
 const mutations = {
   /**
@@ -32,6 +37,10 @@ const mutations = {
     state.token = token
     setToken(token)
   },
+  setIsFirstLogin(state, isFirstLogin) {
+    state.isFirstLogin = isFirstLogin
+    localStorage.setItem('isFirstLogin', isFirstLogin)
+  },
   /**
    * @description 设置用户名
    * @param {*} state
@@ -43,6 +52,9 @@ const mutations = {
   setNickName(state, nickName) {
     state.nickName = nickName
   },
+  setPhone(state, phone) {
+    state.phone = phone
+  },
   setUserId(state, id) {
     state.id = id
   },
@@ -72,9 +84,10 @@ const actions = {
    */
   async login({ commit }, userInfo) {
     const {
-      data: { [tokenName]: token },
+      data: { [tokenName]: token, isFirstLogin },
     } = await userApi.login(userInfo)
     if (token) {
+      commit('setIsFirstLogin', isFirstLogin)
       commit('setToken', token)
       const hour = new Date().getHours()
       const thisTime =
@@ -98,7 +111,7 @@ const actions = {
     // console.log(username, avatar, roles, permissions)
 
     const res = await userApi.getUserInfo()
-    const { id, userName, nickName, avatar } = res.data.entity
+    const { id, userName, nickName, phone, avatar } = res.data.entity
     const { roleIds, permissions } = res.data
     /**
      * 检验返回数据是否正常,无对应参数,将使用默认用户名,头像,Roles和Permissions
@@ -111,6 +124,7 @@ const actions = {
       (userName && !isString(userName)) ||
       (avatar && !isString(avatar)) ||
       (nickName && !isString(nickName)) ||
+      (phone && !isString(phone)) ||
       (roleIds && !isArray(roleIds)) ||
       (permissions && !isArray(permissions))
     ) {
@@ -123,12 +137,15 @@ const actions = {
       // 如不使用username用户名,可删除以下代码
       if (userName) commit('setUsername', userName)
       if (nickName) commit('setNickName', nickName)
+      if (phone) commit('setPhone', phone)
       // 如不使用avatar头像,可删除以下代码
       if (avatar) commit('setAvatar', avatar)
       // 如不使用roles权限控制,可删除以下代码
       if (roleIds) dispatch('acl/setRole', roleIds, { root: true })
       // 如不使用permissions权限控制,可删除以下代码
       if (permissions) dispatch('acl/setPermission', permissions, { root: true })
+
+      watermark.init({ watermark_txt: nickName + phone, watermark_width: 150, watermark_alpha: 0.08 })
     }
   },
   /**
@@ -154,6 +171,8 @@ const actions = {
     await dispatch('tabs/delAllVisitedRoutes', null, { root: true })
     await resetRouter()
     removeToken()
+    watermark.init({ watermark_txt: '            ' })
+    watermark.remove()
   },
   /**
    * @description 设置token

+ 8 - 0
src/vab/components/VabAvatar/index.vue

@@ -9,6 +9,9 @@
     </span>
     <template #dropdown>
       <el-dropdown-menu>
+        <!--用户重置密码-->
+        <user-rest-pwd ref="userRestPwd" @fetch-data="logout" />
+
         <el-dropdown-item command="logout">
           <vab-icon icon="logout-circle-r-line" />
           {{ translateTitle('退出登录') }}
@@ -22,9 +25,11 @@
   import { translateTitle } from '@/utils/i18n'
   import { mapActions, mapGetters } from 'vuex'
   import { toLoginRoute } from '@/utils/routes'
+  import UserRestPwd from '@/views/system/user/components/UserRestPwd'
 
   export default {
     name: 'VabAvatar',
+    components: { UserRestPwd },
     data() {
       return {
         active: false,
@@ -47,6 +52,9 @@
           case 'logout':
             this.logout()
             break
+          case 'userRestPwd':
+            this.$refs['userRestPwd'].showEdit()
+            break
         }
       },
       handleVisibleChange(val) {

+ 3 - 3
src/views/index/index.vue

@@ -334,7 +334,7 @@
         this.initChart()
       },
       initChart() {
-        console.log(this.layout, 'layout')
+        // console.log(this.layout, 'layout')
         this.charts = []
         for (let i = 0; i < this.layout.length; i++) {
           this.getChartSize(this.layout[i].i)
@@ -347,9 +347,9 @@
         this.drawChart(item.i, index)
       },
       getChartSize(id) {
-        console.log(id, 'id')
+        // console.log(id, 'id')
         const bar = document.getElementById(id)
-        console.log(bar, 'bar')
+        // console.log(bar, 'bar')
         bar.style.height = bar.parentNode.clientHeight - 65 + 'px'
         bar.style.width = bar.parentNode.clientWidth + 'px'
       },

+ 58 - 0
src/views/system/user/components/RestPwd.vue

@@ -0,0 +1,58 @@
+<template>
+  <!-- 添加或修改参数配置对话框 -->
+  <el-dialog :title="title" :visible.sync="dialogFormVisible" width="600px" @close="close">
+    <el-form ref="form" label-width="80px" :model="form" :rules="rules">
+      <el-form-item prop="password">
+        <el-input v-model="form.password" placeholder="请输入新密码" show-password />
+      </el-form-item>
+    </el-form>
+    <template #footer>
+      <el-button type="primary" @click="save">确 定</el-button>
+      <el-button @click="close">取 消</el-button>
+    </template>
+  </el-dialog>
+</template>
+
+<script>
+  import userApi from '@/api/system/user'
+
+  export default {
+    name: 'RestPwd',
+    data() {
+      return {
+        form: {
+          id: undefined,
+          userName: '',
+          password: '',
+        },
+        rules: {
+          id: [{ required: true, message: '用户不能为空', trigger: 'blur' }],
+          password: [{ required: true, message: '用户密码不能为空', trigger: 'blur' }],
+        },
+        title: '重置密码',
+        dialogFormVisible: false,
+      }
+    },
+    methods: {
+      showEdit(row) {
+        this.form = row
+        this.dialogFormVisible = true
+      },
+      close() {
+        this.$refs['form'].resetFields()
+        this.form = this.$options.data().form
+        this.dialogFormVisible = false
+      },
+      save() {
+        this.$refs['form'].validate(async (valid) => {
+          if (valid) {
+            const { msg } = await userApi.resetPwd(this.form)
+            this.$baseMessage(msg, 'success', 'vab-hey-message-success')
+            this.$emit('fetch-data')
+            this.close()
+          }
+        })
+      },
+    },
+  }
+</script>

+ 102 - 0
src/views/system/user/components/UserRestPwd.vue

@@ -0,0 +1,102 @@
+<template>
+  <div>
+    <el-dropdown-item command="userRestPwd">
+      <vab-icon icon="user-line" />
+      {{ '修改密码' }}
+    </el-dropdown-item>
+    <!-- 添加或修改参数配置对话框 -->
+    <el-dialog
+      append-to-body
+      :close-on-click-modal="false"
+      :close-on-press-escape="false"
+      :show-close="false"
+      :title="title"
+      :visible.sync="dialogFormVisible"
+      width="600px"
+      @close="close">
+      <el-form ref="form" label-width="80px" :model="form" :rules="rules">
+        <el-form-item label="旧密码" prop="oldPassword">
+          <el-input v-model="form.oldPassword" placeholder="请输入旧密码" show-password />
+        </el-form-item>
+        <el-form-item label="新密码" prop="newPassword">
+          <el-input v-model="form.newPassword" placeholder="请输入新密码" show-password />
+        </el-form-item>
+        <el-form-item label="确认密码" prop="password">
+          <el-input v-model="form.password" placeholder="请确认新密码" show-password />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <el-button type="primary" @click="save">确 定</el-button>
+        <el-button v-if="!isFirstLogin" @click="close">取 消</el-button>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+  import userApi from '@/api/system/user'
+
+  export default {
+    name: 'RestPwd',
+    data() {
+      const validatePwd = (rule, value, callback) => {
+        if (value !== this.form.newPassword) callback(new Error())
+        else callback()
+      }
+      return {
+        form: {
+          id: undefined,
+          oldPassword: '',
+          newPassword: '',
+          password: '',
+        },
+        rules: {
+          oldPassword: [{ required: true, message: '旧密码不能为空', trigger: 'blur' }],
+          newPassword: [{ required: true, message: '新密码不能为空', trigger: 'blur' }],
+          password: [
+            { required: true, message: '确认密码不能为空', trigger: 'blur' },
+            {
+              validator: validatePwd,
+              message: '两次输入的密码不一致',
+              trigger: 'blur',
+            },
+          ],
+        },
+        title: '修改密码',
+        dialogFormVisible: false,
+        isFirstLogin: false,
+      }
+    },
+
+    mounted() {
+      this.isFirstLogin = JSON.parse(localStorage.getItem('isFirstLogin'))
+      if (this.isFirstLogin) {
+        this.showEdit()
+      }
+    },
+    methods: {
+      showEdit() {
+        this.dialogFormVisible = true
+      },
+      close() {
+        if (this.isFirstLogin) {
+          this.$baseMessage('首次登录系统,请先修改账号密码。', 'error', 'vab-hey-message-success')
+          return
+        }
+        this.$refs['form'].resetFields()
+        this.form = this.$options.data().form
+        this.dialogFormVisible = false
+      },
+      save() {
+        this.$refs['form'].validate(async (valid) => {
+          if (valid) {
+            const { msg } = await userApi.changePwd(this.form)
+            this.$baseMessage(msg, 'success', 'vab-hey-message-success')
+            this.$emit('fetch-data')
+            this.close()
+          }
+        })
+      },
+    },
+  }
+</script>

+ 9 - 3
src/views/system/user/index.vue

@@ -61,7 +61,7 @@
               {{ $index + 1 }}
             </template>
           </el-table-column>-->
-          <el-table-column align="center" label="id" prop="id" show-overflow-tooltip />
+          <!--          <el-table-column align="center" label="id" prop="id" show-overflow-tooltip />-->
           <el-table-column align="center" label="用户名" prop="userName" show-overflow-tooltip />
           <el-table-column align="center" label="昵称" prop="nickName" show-overflow-tooltip />
           <el-table-column align="center" label="手机号" prop="phone" show-overflow-tooltip />
@@ -80,8 +80,9 @@
               <span>{{ parseTime(scope.row.createdTime) }}</span>
             </template>
           </el-table-column>
-          <el-table-column align="center" label="操作" show-overflow-tooltip width="85">
+          <el-table-column align="center" label="操作" show-overflow-tooltip width="120">
             <template #default="{ row }">
+              <el-button type="text" @click="handleResetPwd(row)">重置</el-button>
               <el-button v-permissions="['system:user:edit']" type="text" @click="handleEdit(row)">编辑</el-button>
               <el-button v-permissions="['system:user:delete']" type="text" @click="handleDelete(row)">删除</el-button>
             </template>
@@ -101,6 +102,7 @@
       </el-col>
     </el-row>
     <edit ref="edit" @fetch-data="fetchData" />
+    <rest-pwd ref="restPwd" @fetch-data="fetchData" />
   </div>
 </template>
 
@@ -108,10 +110,11 @@
   import deptApi from '@/api/system/dept'
   import userApi from '@/api/system/user'
   import Edit from './components/UserEdit'
+  import RestPwd from './components/RestPwd'
 
   export default {
     name: 'User',
-    components: { Edit },
+    components: { Edit, RestPwd },
     data() {
       return {
         list: [],
@@ -164,6 +167,9 @@
       setSelectRows(val) {
         this.selectRows = val
       },
+      handleResetPwd(row) {
+        this.$refs['restPwd'].showEdit(row)
+      },
       handleEdit(row) {
         if (row.id) {
           this.$refs['edit'].showEdit(row)