Browse Source

feat: reset password

Casper Dai 3 years ago
parent
commit
21b42b8676

+ 2 - 0
.env

@@ -50,6 +50,8 @@ VUE_APP_CAMERA_PROXY = '/prod-api/websocket'
 # user default password
 # MD5('123456a?' + MD5('123456a?'))
 VUE_APP_USER_PASSWORD = '1fc3fdb8d13bd4e2d1336f6dd377ee4f'
+# MD5('123456a?')
+VUE_APP_SALT = '42857cfddb33f3fddb27fff9773683f3'
 
 # gaode
 VUE_APP_GAODE_MAP_KEY = '9c499e7000d066c05de9af8556a890f7'

+ 1 - 0
package.json

@@ -19,6 +19,7 @@
     "element-ui": "^2.15.6",
     "hls.js": "^1.1.2",
     "keycloak-js": "^15.0.2",
+    "md5": "^2.3.0",
     "mediainfo.js": "^0.1.7",
     "mpegts.js": "^1.6.10",
     "mqtt": "^4.3.7",

+ 13 - 1
src/api/user.js

@@ -1,3 +1,4 @@
+import md5 from 'md5'
 import store from '@/store'
 import { Role } from '@/constant'
 import request, { keycloakRequest } from '@/utils/request'
@@ -42,7 +43,10 @@ export function addUser (data) {
   return add({
     url: `/users`,
     method: 'POST',
-    data
+    data: {
+      ...data,
+      credentials: [{ type: 'password', value: process.env.VUE_APP_USER_PASSWORD, temporary: true }]
+    }
   }, keycloakRequest)
 }
 
@@ -87,6 +91,14 @@ export function resetPassword ({ id }) {
   }, '重置', keycloakRequest)
 }
 
+export function resetPasswordByUser (newPassword) {
+  return messageSend({
+    url: '/keycloak/changePassword',
+    method: 'PUT',
+    params: { newPassword: md5(`${newPassword}${process.env.VUE_APP_SALT}`) }
+  }, '重置')
+}
+
 export function getTenantsByQuery (query, onlyOnce) {
   const { pageSize: max, pageNum, name, ...params } = query
   return keycloakRequest({

+ 8 - 0
src/scss/bem/_utility.scss

@@ -84,6 +84,10 @@
 }
 
 .u-align-self {
+  &--stretch {
+    align-self: stretch;
+  }
+
   &--center {
     align-self: center;
   }
@@ -98,3 +102,7 @@
 .u-width--auto {
   width: auto !important;
 }
+
+.u-password {
+  -webkit-text-security: disc;
+}

+ 1 - 7
src/views/external/camera/index.vue

@@ -48,7 +48,7 @@
           <span class="c-grid-form__label required">密码</span>
           <el-input
             v-model.trim="camera.password"
-            class="o-password"
+            class="u-password"
             placeholder="最多50个字符"
             maxlength="50"
           />
@@ -212,9 +212,3 @@ export default {
   }
 }
 </script>
-
-<style lang="scss" scoped>
-.o-password {
-  -webkit-text-security: disc;
-}
-</style>

+ 73 - 0
src/views/platform/profile/components/ResetPassword.vue

@@ -0,0 +1,73 @@
+<template>
+  <div class="c-grid-form medium">
+    <span class="c-grid-form__label required">新密码</span>
+    <el-input
+      v-model.trim="newPassword"
+      class="u-password"
+      placeholder="6-20位密码"
+      maxlength="50"
+    />
+    <span class="c-grid-form__label required">确认新密码</span>
+    <el-input
+      v-model.trim="confirmNewPassword"
+      class="u-password"
+      maxlength="50"
+    />
+    <div class="c-grid-form__row has-top-padding u-text-center">
+      <button
+        class="o-button"
+        @click="onSave"
+      >
+        保存
+      </button>
+    </div>
+  </div>
+</template>
+
+<script>
+import { resetPasswordByUser } from '@/api/user'
+
+export default {
+  name: 'ResetPasswrod',
+  data () {
+    return {
+      newPassword: '',
+      confirmNewPassword: ''
+    }
+  },
+  computed: {
+    disabled () {
+      return !this.newPassword || !this.confirmNewPassword
+    }
+  },
+  methods: {
+    onSave () {
+      if (!this.newPassword) {
+        this.$message({
+          type: 'warning',
+          message: '请填写新密码'
+        })
+        return
+      }
+      if (this.newPassword.length < 6) {
+        this.$message({
+          type: 'warning',
+          message: '请填写至少6位的密码'
+        })
+        return
+      }
+      if (this.newPassword !== this.confirmNewPassword) {
+        this.$message({
+          type: 'warning',
+          message: '确认新密码与新密码不一致'
+        })
+        return
+      }
+      resetPasswordByUser(this.newPassword).then(() => {
+        this.newPassword = ''
+        this.confirmNewPassword = ''
+      })
+    }
+  }
+}
+</script>

+ 6 - 4
src/views/platform/profile/components/UserInfoItem.vue

@@ -3,20 +3,19 @@
     <div class="l-flex--row c-sibling-item--v c-line">
       <div class="c-line__left u-color--black u-bold">{{ config.label }}</div>
       <el-input
-        v-if="initial || expanded"
         v-model="cValue"
         class="l-flex__fill c-sibling-item"
         :maxlength="config.maxlength"
-        :placeholder="'请输入'+config.tipText"
+        :placeholder="placeholder"
         :readonly="!expanded"
       />
       <div class="c-sibling-item c-line__right">
         <button
-          v-if="expanded === false"
+          v-if="!expanded"
           class="o-button"
           @click="onExpanded"
         >
-          {{ initial ? '更换' : '绑定' }}{{ config.label }}
+          {{ initial ? '更换' : '绑定' }}
         </button>
         <div
           v-else
@@ -133,6 +132,9 @@ export default {
     },
     codeText () {
       return this.verification ? `${this.countDown} s` : '发送验证码'
+    },
+    placeholder () {
+      return this.value || (this.expanded ? `请输入${this.config.tipText}` : '暂未绑定')
     }
   },
   watch: {

+ 75 - 50
src/views/platform/profile/index.vue

@@ -33,6 +33,22 @@
       v-loading="loading"
       class="l-flex__fill l-flex--col center"
     >
+      <el-tabs
+        v-model="active"
+        class="c-tabs has-padding u-align-self--stretch"
+        stretch
+      >
+        <el-tab-pane
+          class="c-sibling-item"
+          label="账号设置"
+          name="settings"
+        />
+        <el-tab-pane
+          class="c-sibling-item far"
+          label="密码重置"
+          name="password"
+        />
+      </el-tabs>
       <warning
         v-if="error"
         @click="getUserInfo"
@@ -41,60 +57,65 @@
         v-else
         class="has-padding l-flex--col"
       >
-        <div class="l-flex--row has-bottom-padding">
-          <user-info-item
-            v-if="user"
-            key="phone"
-            v-model="phone"
-            type="phone"
-            :initial="user.attributes.phone[0]"
-            @update="changeAttribute('phone', phone, '更新手机号')"
-          />
-        </div>
-        <div class="l-flex--row has-padding--v">
-          <user-info-item
-            v-if="user"
-            key="email"
-            v-model="email"
-            type="email"
-            :initial="user.email"
-            @update="updateUser({email}, '更新邮箱')"
-          />
-        </div>
-        <div class="l-flex--row center has-top-padding">
-          <div
-            class="o-app u-pointer"
-            @click="onClickWechat"
-          >
-            <i class="o-icon wechat has-bg" />
+        <template v-if="active === 'settings'">
+          <div class="l-flex--row has-bottom-padding">
+            <user-info-item
+              v-if="user"
+              key="phone"
+              v-model="phone"
+              type="phone"
+              :initial="user.attributes.phone[0]"
+              @update="changeAttribute('phone', phone, '更新手机号')"
+            />
+          </div>
+          <div class="l-flex--row has-padding--v">
+            <user-info-item
+              v-if="user"
+              key="email"
+              v-model="email"
+              type="email"
+              :initial="user.email"
+              @update="updateUser({email}, '更新邮箱')"
+            />
+          </div>
+          <div class="l-flex--row center has-top-padding">
             <div
-              v-if="wechat"
-              class="l-flex--row"
+              class="o-app u-pointer"
+              @click="onClickWechat"
             >
-              <div class="c-sibling-item u-color--info light">已绑定</div>
-              <div class="c-sibling-item u-pointer">解绑</div>
+              <i class="o-icon wechat has-bg" />
+              <div
+                v-if="wechat"
+                class="l-flex--row"
+              >
+                <div class="c-sibling-item u-color--info light">已绑定</div>
+                <div class="c-sibling-item u-pointer">解绑</div>
+              </div>
+              <div v-else>
+                绑定微信
+              </div>
             </div>
-            <div v-else>
-              绑定微信
-            </div>
-          </div>
-          <div
-            class="o-app u-pointer"
-            @click="onClickApplet"
-          >
-            <i class="o-icon applet has-bg" />
             <div
-              v-if="applet"
-              class="l-flex--row"
+              class="o-app u-pointer"
+              @click="onClickApplet"
             >
-              <div class="c-sibling-item u-color--info light">已绑定</div>
-              <div class="c-sibling-item u-pointer">解绑</div>
-            </div>
-            <div v-else>
-              绑定小程序
+              <i class="o-icon applet has-bg" />
+              <div
+                v-if="applet"
+                class="l-flex--row"
+              >
+                <div class="c-sibling-item u-color--info light">已绑定</div>
+                <div class="c-sibling-item u-pointer">解绑</div>
+              </div>
+              <div v-else>
+                绑定小程序
+              </div>
             </div>
           </div>
-        </div>
+        </template>
+        <template v-if="active === 'password'">
+          <reset-password />
+        </template>
       </div>
     </div>
     <el-dialog
@@ -129,27 +150,31 @@
 
 <script>
 import { mapGetters } from 'vuex'
-import UserInfoItem from './components/UserInfoItem'
 import {
   userinfo,
   updateUser,
   getTicket,
   getQrcode
 } from '@/api/user'
+import { GATEWAY } from '@/constant'
 import {
   validPhone,
   validEmail
 } from '@/utils/validate'
-import { GATEWAY } from '@/constant'
+import UserInfoItem from './components/UserInfoItem'
+import ResetPassword from './components/ResetPassword'
+
 export default {
   name: 'Profile',
   components: {
-    UserInfoItem
+    UserInfoItem,
+    ResetPassword
   },
   data () {
     return {
       loading: true,
       error: false,
+      active: 'settings',
       user: {
         email: '',
         attributes: {

+ 1 - 2
src/views/realm/user/Account.vue

@@ -207,8 +207,7 @@ export default {
       addUser({
         username,
         groups: this.getGroups(group),
-        enabled: true,
-        credentials: [{ type: 'password', value: process.env.VUE_APP_USER_PASSWORD, temporary: true }]
+        enabled: true
       }).then(() => {
         done()
         this.onCancel()

+ 20 - 1
yarn.lock

@@ -2927,6 +2927,11 @@ chardet@^0.7.0:
   resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e"
   integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==
 
+charenc@0.0.2:
+  version "0.0.2"
+  resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667"
+  integrity sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==
+
 check-types@^8.0.3:
   version "8.0.3"
   resolved "https://registry.yarnpkg.com/check-types/-/check-types-8.0.3.tgz#3356cca19c889544f2d7a95ed49ce508a0ecf552"
@@ -3663,6 +3668,11 @@ cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3:
     shebang-command "^2.0.0"
     which "^2.0.1"
 
+crypt@0.0.2:
+  version "0.0.2"
+  resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b"
+  integrity sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==
+
 crypto-browserify@^3.11.0:
   version "3.12.0"
   resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec"
@@ -6100,7 +6110,7 @@ is-boolean-object@^1.1.0:
     call-bind "^1.0.2"
     has-tostringtag "^1.0.0"
 
-is-buffer@^1.1.5:
+is-buffer@^1.1.5, is-buffer@~1.1.6:
   version "1.1.6"
   resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be"
   integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==
@@ -6968,6 +6978,15 @@ md5.js@^1.3.4:
     inherits "^2.0.1"
     safe-buffer "^5.1.2"
 
+md5@^2.3.0:
+  version "2.3.0"
+  resolved "https://registry.yarnpkg.com/md5/-/md5-2.3.0.tgz#c3da9a6aae3a30b46b7b0c349b87b110dc3bda4f"
+  integrity sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==
+  dependencies:
+    charenc "0.0.2"
+    crypt "0.0.2"
+    is-buffer "~1.1.6"
+
 mdn-data@2.0.14:
   version "2.0.14"
   resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.14.tgz#7113fc4281917d63ce29b43446f701e68c25ba50"