Jelajahi Sumber

修改-增加修改密码校验

lishuangjiang@potevio.com 1 tahun lalu
induk
melakukan
2429b973d6

+ 2 - 2
sso-ui/sso-ui-admin-vue3/src/router/index.ts

@@ -1,11 +1,11 @@
 import type { App } from 'vue'
 import type { RouteRecordRaw } from 'vue-router'
-import { createRouter, createWebHistory } from 'vue-router'
+import { createRouter, createWebHistory,createWebHashHistory  } from 'vue-router'
 import remainingRouter from './modules/remaining'
 
 // 创建路由实例
 const router = createRouter({
-  history: createWebHistory(), // createWebHashHistory URL带#,createWebHistory URL不带#
+  history: createWebHashHistory(import.meta.env.VITE_PUBLIC_PATH), // createWebHashHistory URL带#,createWebHistory URL不带#
   strict: true,
   routes: remainingRouter as RouteRecordRaw[],
   scrollBehavior: () => ({ left: 0, top: 0 })

+ 6 - 0
sso-ui/sso-ui-admin-vue3/src/store/modules/user.ts

@@ -90,6 +90,12 @@ export const useUserStore = defineStore('admin-user', {
       deleteUserCache() // 删除用户缓存
       this.resetState()
     },
+    async resetPassword() { // await loginOut()
+      // removeToken()
+      // deleteUserCache() // 删除用户缓存
+      // this.resetState()
+
+    },
     resetState() {
       this.permissions = []
       this.roles = []

+ 12 - 0
sso-ui/sso-ui-admin-vue3/src/utils/auth.ts

@@ -6,6 +6,7 @@ const { wsCache } = useCache()
 
 const AccessTokenKey = 'ACCESS_TOKEN'
 const RefreshTokenKey = 'REFRESH_TOKEN'
+const LoginDate = 'LOGIN_DATE'
 
 // 获取token
 export const getAccessToken = () => {
@@ -18,16 +19,27 @@ export const getRefreshToken = () => {
   return wsCache.get(RefreshTokenKey)
 }
 
+// 获取登录时间
+export const getLoginDate = () => {
+  return wsCache.get(LoginDate)
+}
+
+export const setLoginDate = (loginDate) => {
+  return  wsCache.set(LoginDate, loginDate)
+}
+
 // 设置token
 export const setToken = (token: TokenType) => {
   wsCache.set(RefreshTokenKey, token.refreshToken)
   wsCache.set(AccessTokenKey, token.accessToken)
+  wsCache.set(LoginDate, token.loginDate)
 }
 
 // 删除token
 export const removeToken = () => {
   wsCache.delete(AccessTokenKey)
   wsCache.delete(RefreshTokenKey)
+  wsCache.delete(LoginDate)
 }
 
 /** 格式化token(jwt格式) */

+ 170 - 58
sso-ui/sso-ui-admin-vue3/src/views/Home/Index2.vue

@@ -3,73 +3,153 @@
     <div class="content">
       <div class="title"></div>
       <div class="logout_bg">
-        <el-image style="width:40px;height:40px;margin-right: 40px" :src="manager" fit="cover"  @click="toProfile"  v-if="roles.includes('super_admin') || roles.includes('sys_admin')"/>
-        <el-image style="width:40px;height:40px;margin-right: 20px" :src="logouticon" fit="cover" @click="loginOut"/>
+        <el-tooltip content="重置密码" placement="bottom" effect="light" @click.stop.prevent>
+          <el-image style="width:40px;height:40px;margin-right: 40px" :src="password" fit="cover" @click="resetPassword" v-if="!roles.includes('lj_org_admin') && !roles.includes('super_admin') && !roles.includes('sys_admin')"/>
+        </el-tooltip>
+        <el-tooltip content="管理界面" placement="bottom" effect="light" @click.stop.prevent>
+          <el-image style="width:40px;height:40px;margin-right: 40px" :src="manager" fit="cover"  @click="toProfile"  v-if="roles.includes('lj_org_admin') || roles.includes('super_admin') || roles.includes('sys_admin')"/>
+        </el-tooltip>
+        <el-tooltip content="退出系统" placement="bottom" effect="light" @click.stop.prevent>
+          <el-image style="width:40px;height:40px;margin-right: 20px" :src="logouticon" fit="cover" @click="loginOut"/>
+        </el-tooltip>
       </div>
       <div class="main">
         <div class="col">
           <div class="row2" style="background-color: #30CA79;">
-            <img class="image2" :src="img1" alt />
-            <div class="text2"><el-link style="font-size: 30px;color: #fff; font-weight: 500" @click="handlerLink(44)">社区居家养老管理系统</el-link></div>
+            <img class="image2" :src="img1" alt/>
+            <div class="text2">
+              <el-link style="font-size: 30px;color: #fff; font-weight: 500" @click="handlerLink(44)">社区居家养老管理系统
+              </el-link>
+            </div>
           </div>
           <div class="row2" style="background-color: #32B19E;">
-            <img class="image2" :src="img2" alt />
-            <div class="text2"><el-link style="font-size: 30px;color: #fff; font-weight: 500" @click="handlerLink(43)">智慧养老IOT物联网平台</el-link></div>
-<!--            <div class="text2"><a class="aaa" href="http://web.poteviohealth.com/boss/" target="_blank">智慧养老IOT物联网平台</a></div>-->
+            <img class="image2" :src="img2" alt/>
+            <div class="text2">
+              <el-link style="font-size: 30px;color: #fff; font-weight: 500" @click="handlerLink(43)">智慧养老IOT物联网平台
+              </el-link>
+            </div>
+            <!--            <div class="text2"><a class="aaa" href="http://web.poteviohealth.com/boss/" target="_blank">智慧养老IOT物联网平台</a></div>-->
           </div>
         </div>
         <div class="col col3" style="background-color: #00734D;">
-          <div class="text0"><el-link style="font-size: 30px;color: #fff; font-weight: 500" @click="handlerLink(45)">康养大数据平台</el-link></div>
+          <div class="text0">
+            <el-link style="font-size: 30px;color: #fff; font-weight: 500" @click="handlerLink(45)">康养大数据平台</el-link>
+          </div>
         </div>
         <div class="col">
           <div class="row2" style="background-color: #4C4B6B;">
-            <img class="image2" :src="img5" alt />
-            <div class="text2"><el-link style="font-size: 30px;color: #fff; font-weight: 500" @click="handlerLink(50)">机构养老运营管理系统</el-link></div>
+            <img class="image2" :src="img5" alt/>
+            <div class="text2">
+              <el-link style="font-size: 30px;color: #fff; font-weight: 500" @click="handlerLink(50)">机构养老运营管理系统
+              </el-link>
+            </div>
           </div>
           <div class="row2" style="background-color: #4A8EE2;">
-            <img class="image2" :src="img6" alt />
-            <div class="text2"><el-link style="font-size: 30px;color: #fff; font-weight: 500" @click="handlerLink(49)">旅居养老运营管理系统</el-link></div>
+            <img class="image2" :src="img6" alt/>
+            <div class="text2">
+              <el-link style="font-size: 30px;color: #fff; font-weight: 500" @click="handlerLink(49)">旅居养老运营管理系统
+              </el-link>
+            </div>
           </div>
         </div>
         <div class="col">
           <div class="row2" style="background-color: #F3B144;">
             <img class="image2" :src="img7" alt/>
-            <div class="text2"><el-link style="font-size: 30px;color: #fff; font-weight: 500" @click="handlerLink(46)">运营数据分析平台</el-link></div>
+            <div class="text2">
+              <el-link style="font-size: 30px;color: #fff; font-weight: 500" @click="handlerLink(46)">运营数据分析平台</el-link>
+            </div>
           </div>
           <div class="row2" style="background-color: #AACC05;">
-            <img class="image2" :src="img8" alt />
+            <img class="image2" :src="img8" alt/>
             <div class="text2">建设中</div>
           </div>
         </div>
       </div>
     </div>
+    <el-dialog v-model="dialogFormVisible" title="重置密码" width="400" draggable @close="reset(formRef)">
+      <el-form ref="formRef" :model="passwordModel" :rules="rules" :label-width="100">
+        <el-form-item :label="t('profile.password.oldPassword')" prop="oldPassword">
+          <InputPassword v-model="passwordModel.oldPassword"/>
+        </el-form-item>
+        <el-form-item :label="t('profile.password.newPassword')" prop="newPassword">
+          <InputPassword v-model="passwordModel.newPassword" strength/>
+        </el-form-item>
+        <el-form-item :label="t('profile.password.confirmPassword')" prop="confirmPassword">
+          <InputPassword v-model="passwordModel.confirmPassword" strength/>
+        </el-form-item>
+        <el-form-item>
+          <XButton :title="t('common.save')" type="primary" @click="submit(formRef)"/>
+          <XButton :title="t('common.reset')" type="danger" @click="reset(formRef)"/>
+        </el-form-item>
+      </el-form>
+    </el-dialog>
   </div>
+
 </template>
-<script setup>
-  import { ElMessageBox } from 'element-plus'
-  import { useUserStore } from '@/store/modules/user'
-  import { useTagsViewStore } from '@/store/modules/tagsView'
-  import { CACHE_KEY, useCache } from '@/hooks/web/useCache'
+<script lang="ts" setup>
+  import {FormInstance, FormRules} from 'element-plus'
+  import {ElMessageBox} from 'element-plus'
+  import {useUserStore} from '@/store/modules/user'
+  import {useTagsViewStore} from '@/store/modules/tagsView'
+  import {CACHE_KEY, useCache} from '@/hooks/web/useCache'
   import * as UserApi from '@/api/system/user'
-   import img1 from '@/assets/imgs/main/icon1.png';
-   import img2 from '@/assets/imgs/main/icon2.png';
-   import img3 from '@/assets/imgs/main/icon3.png';
-   import img5 from '@/assets/imgs/main/icon5.png';
-   import img6 from '@/assets/imgs/main/icon6.png';
-   import img7 from '@/assets/imgs/main/icon7.png';
-   import img8 from '@/assets/imgs/main/icon8.png';
-   import logouticon from '@/assets/imgs/main/logout.png'
-   import manager from '@/assets/imgs/main/manager.png'
-
+  import img1 from '@/assets/imgs/main/icon1.png';
+  import img2 from '@/assets/imgs/main/icon2.png';
+  import img3 from '@/assets/imgs/main/icon3.png';
+  import img5 from '@/assets/imgs/main/icon5.png';
+  import img6 from '@/assets/imgs/main/icon6.png';
+  import img7 from '@/assets/imgs/main/icon7.png';
+  import img8 from '@/assets/imgs/main/icon8.png';
+  import logouticon from '@/assets/imgs/main/logout.png'
+  import manager from '@/assets/imgs/main/manager.png'
+  import password from '@/assets/imgs/main/password.png'
   import * as authUtil from '@/utils/auth'
+  import {InputPassword} from '@/components/InputPassword'
+  import {updateUserPassword} from '@/api/system/user/profile'
+
+  const message = useMessage()
 
   const userStore = useUserStore()
   const tagsViewStore = useTagsViewStore()
-  const { t } = useI18n()
+  const {t} = useI18n()
   //
-  const { push, replace } = useRouter()
+  const {push, replace} = useRouter()
+
+  const {wsCache} = useCache()
+
+  const formLabelWidth = '140px'
+  const dialogFormVisible = ref(false)
+  const formRef = ref<FormInstance>()
+  const passwordModel = reactive({
+    oldPassword: '',
+    newPassword: '',
+    confirmPassword: ''
+  })
+
+  // 表单校验
+  const equalToPassword = (_rule, value, callback) => {
+    if (passwordModel.newPassword !== value) {
+      callback(new Error(t('profile.password.diffPwd')))
+    } else {
+      callback()
+    }
+  }
+  const rules = reactive<FormRules>({
+    oldPassword: [
+      {required: true, message: t('profile.password.oldPwdMsg'), trigger: 'blur'},
+      {min: 6, max: 20, message: t('profile.password.pwdRules'), trigger: 'blur'}
+    ],
+    newPassword: [
+      {required: true, message: t('profile.password.newPwdMsg'), trigger: 'blur'},
+      {min: 6, max: 20, message: t('profile.password.pwdRules'), trigger: 'blur'}
+    ],
+    confirmPassword: [
+      {required: true, message: t('profile.password.cfPwdMsg'), trigger: 'blur'},
+      {required: true, validator: equalToPassword, trigger: 'blur'}
+    ]
+  })
+
 
-  const { wsCache } = useCache()
   const loginOut = async () => {
     try {
       await ElMessageBox.confirm(t('common.loginOutMessage'), t('common.reminder'), {
@@ -79,26 +159,34 @@
       })
       await userStore.loginOut()
       tagsViewStore.delAllViews()
+
       replace('/login?redirect=/Navicate')
-    } catch {}
+    } catch {
+    }
   }
   const toProfile = async () => {
     push('/system/user')
   }
 
-  const handlerLink = async (id) =>{
-     try {
-       const linkUrl = await UserApi.getLinkInfo(id, authUtil.getAccessToken())
-       console.log(linkUrl)
-       if (linkUrl != "") {
-         window.open(linkUrl);
-       }
-     }catch (error) {
-       console.log(error);
-     }
+  const resetPassword = async () => {
+    dialogFormVisible.value = true
+  }
+
+  const handlerLink = async (id) => {
+    if (id==50){
+      window.open("http://106.37.165.111:9080/pc/PCLGO001Action_001.action");
+    }else{
+      try {
+        const linkUrl = await UserApi.getLinkInfo(id, authUtil.getAccessToken())
+        if (linkUrl != "") {
+          window.open(linkUrl);
+        }
+      } catch (error) {
+        console.log(error);
+      }
+    }
   }
   const roles = wsCache.get(CACHE_KEY.USER).roles
-  console.log("######################",roles)
   // const checkRoles = (value)=>{
   //   const permissionRoles = value
   //   const roles = wsCache.get(CACHE_KEY.USER).roles
@@ -107,6 +195,22 @@
   //   })
   //   return hasRole;
   // }
+  const submit = (formEl: FormInstance | undefined) => {
+    if (!formEl) return
+    formEl.validate(async (valid) => {
+      if (valid) {
+        await updateUserPassword(passwordModel.oldPassword, passwordModel.newPassword)
+        message.success(t('common.updateSuccess'))
+        formEl.resetFields()
+        dialogFormVisible.value=false
+      }
+    })
+  }
+
+  const reset = (formEl: FormInstance | undefined) => {
+    if (!formEl) return
+    formEl.resetFields()
+  }
 
 </script>
 <style lang="scss" scoped>
@@ -124,11 +228,12 @@
     display: flex;
     justify-content: center;
 
-    .content{
+    .content {
       width: 1660px;
       height: 777px;
     }
-    .title{
+
+    .title {
       width: 655px;
       height: 62px;
       // position: absolute;
@@ -139,7 +244,7 @@
       background-repeat: no-repeat;
     }
 
-    .main{
+    .main {
       width: 1680px;
       height: 600px;
       margin-top: 115px;
@@ -152,47 +257,54 @@
       font-size: 32px;
     }
 
-    .col{
+    .col {
       width: 400px;
       height: 600px;
       margin-right: 10px;
       text-decoration: none;
     }
 
-    .col3{
+    .col3 {
       background-image: url("/src/assets/imgs/main/icon0.png");
     }
 
 
-    .row3{
+    .row3 {
       width: 400px;
       height: 190px;
       margin-bottom: 15px;
       display: flex;
     }
-    .image3{
+
+    .image3 {
       margin: 61px 24px 61px 40px;
     }
-    .text3{
+
+    .text3 {
       margin-top: 51px;
     }
-    .row2{
+
+    .row2 {
       width: 400px;
       height: 290px;
       margin-bottom: 20px;
     }
-    .text0{
+
+    .text0 {
       text-align: center;
       margin-top: 110px;
     }
-    .image2{
+
+    .image2 {
       margin: 69px 160px 20px 160px;
     }
-    .text2{
+
+    .text2 {
       text-align: center;
       width: 100%;
     }
-    .aaa{
+
+    .aaa {
       color: #fff;
     }
 
@@ -200,7 +312,7 @@
       position: absolute;
       top: 80px;
       right: 60px;
-      width: 200px;
+      width: 260px;
       height: 50px;
       cursor: pointer;
     }

+ 144 - 23
sso-ui/sso-ui-admin-vue3/src/views/Home/index3.vue

@@ -3,8 +3,15 @@
     <div class="screen-container">
       <div class="title"></div>
       <div class="logout_bg">
-        <el-image style="width:40px;height:40px;margin-right: 40px" :src="manager" fit="cover"  @click="toProfile"  v-if="roles.includes('super_admin') || roles.includes('sys_admin')"/>
-        <el-image style="width:40px;height:40px;margin-right: 20px" :src="logouticon" fit="cover" @click="loginOut"/>
+        <el-tooltip content="重置密码" placement="bottom" effect="light" @click.stop.prevent>
+          <el-image style="width:40px;height:40px;margin-right: 40px" :src="password" fit="cover" @click="resetPassword" v-if="!roles.includes('lj_org_admin') && !roles.includes('super_admin') && !roles.includes('sys_admin')"/>
+        </el-tooltip>
+        <el-tooltip content="管理界面" placement="bottom" effect="light" @click.stop.prevent>
+          <el-image style="width:40px;height:40px;margin-right: 40px" :src="manager" fit="cover"  @click="toProfile"  v-if="roles.includes('lj_org_admin') || roles.includes('super_admin') || roles.includes('sys_admin')"/>
+        </el-tooltip>
+        <el-tooltip content="退出系统" placement="bottom" effect="light" @click.stop.prevent>
+          <el-image style="width:40px;height:40px;margin-right: 20px" :src="logouticon" fit="cover" @click="loginOut"/>
+        </el-tooltip>
       </div>
       <div class="main">
         <div class="col">
@@ -44,18 +51,34 @@
       </div>
     </div>
   </ScaleContainer>
-
+  <el-dialog v-model="dialogFormVisible" title="重置密码" width="400" draggable :before-close="handleClose" @close="reset(formRef)">
+    <el-form ref="formRef" :model="passwordModel" :rules="rules" :label-width="100">
+      <el-form-item :label="t('profile.password.oldPassword')" prop="oldPassword">
+        <InputPassword v-model="passwordModel.oldPassword"/>
+      </el-form-item>
+      <el-form-item :label="t('profile.password.newPassword')" prop="newPassword">
+        <InputPassword v-model="passwordModel.newPassword" strength/>
+      </el-form-item>
+      <el-form-item :label="t('profile.password.confirmPassword')" prop="confirmPassword">
+        <InputPassword v-model="passwordModel.confirmPassword" strength/>
+      </el-form-item>
+      <el-form-item>
+        <XButton :title="t('common.save')" type="primary" @click="submit(formRef)"/>
+        <XButton :title="t('common.reset')" type="danger" @click="reset(formRef)"/>
+      </el-form-item>
+    </el-form>
+  </el-dialog>
 </template>
 
 <script setup lang="ts">
 import ScaleContainer from "@/components/common/ScaleContainer/index.vue";
-import manager from "@/assets/imgs/main/manager.png";
-import logouticon from "@/assets/imgs/main/logout.png";
+import {FormInstance, FormRules} from 'element-plus'
+import {ElMessageBox} from 'element-plus'
+import {useUserStore} from '@/store/modules/user'
+import {useTagsViewStore} from '@/store/modules/tagsView'
+import {CACHE_KEY, useCache, deleteUserCache} from '@/hooks/web/useCache'
+import { resetRouter } from '@/router'
 
-import { ElMessageBox } from 'element-plus'
-import { useUserStore } from '@/store/modules/user'
-import { useTagsViewStore } from '@/store/modules/tagsView'
-import { CACHE_KEY, useCache } from '@/hooks/web/useCache'
 import * as UserApi from '@/api/system/user'
 import img1 from '@/assets/imgs/main/icon1.png';
 import img2 from '@/assets/imgs/main/icon2.png';
@@ -64,17 +87,68 @@ import img5 from '@/assets/imgs/main/icon5.png';
 import img6 from '@/assets/imgs/main/icon6.png';
 import img7 from '@/assets/imgs/main/icon7.png';
 import img8 from '@/assets/imgs/main/icon8.png';
+import logouticon from '@/assets/imgs/main/logout.png'
+import manager from '@/assets/imgs/main/manager.png'
+import password from '@/assets/imgs/main/password.png'
+import * as authUtil from '@/utils/auth'
+import {InputPassword} from '@/components/InputPassword'
+import {updateUserPassword} from '@/api/system/user/profile'
 
 
-import * as authUtil from '@/utils/auth'
+const message = useMessage()
 
 const userStore = useUserStore()
 const tagsViewStore = useTagsViewStore()
-const { t } = useI18n()
+const {t} = useI18n()
 //
-const { push, replace } = useRouter()
+const {push, replace} = useRouter()
+
+const {wsCache} = useCache()
+
+const formLabelWidth = '140px'
+const dialogFormVisible = ref(false)
+const formRef = ref<FormInstance>()
+const passwordModel = reactive({
+  oldPassword: '',
+  newPassword: '',
+  confirmPassword: ''
+})
+
+// 表单校验
+const equalToPassword = (_rule, value, callback) => {
+  if (passwordModel.newPassword !== value) {
+    callback(new Error(t('profile.password.diffPwd')))
+  } else {
+    callback()
+  }
+}
+const newPasswordValidate = (rule, value, callback) => {
+  if (passwordModel.oldPassword === value) {
+    callback(new Error('新密码不能与旧密码一致'))
+  } else if (/^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[._~!@#$^&*])[A-Za-z0-9._~!@#$^&*]{8,20}$/g.test(value)) {
+    callback()
+  } else {
+    callback(new Error('请输入包含英文字母大小写、数字和特殊符号的 8-20 位组合'))
+  }
+}
+
+const rules = reactive<FormRules>({
+  oldPassword: [
+    {required: true, message: t('profile.password.oldPwdMsg'), trigger: 'blur'},
+    {min: 8, max: 20, message: t('profile.password.pwdRules'), trigger: 'blur'}
+  ],
+  newPassword: [
+    {required: true, message: t('profile.password.newPwdMsg'), trigger: 'blur'},
+    {min: 8, max: 20, message: t('profile.password.pwdRules'), trigger: 'blur'},
+    {required: true, validator: newPasswordValidate, trigger: 'blur'}
+  ],
+  confirmPassword: [
+    {required: true, message: t('profile.password.cfPwdMsg'), trigger: 'blur'},
+    {required: true, validator: equalToPassword, trigger: 'blur'}
+  ]
+})
+
 
-const { wsCache } = useCache()
 const loginOut = async () => {
   try {
     await ElMessageBox.confirm(t('common.loginOutMessage'), t('common.reminder'), {
@@ -84,29 +158,76 @@ const loginOut = async () => {
     })
     await userStore.loginOut()
     tagsViewStore.delAllViews()
+    deleteUserCache() // 清空用户缓存
     replace('/login?redirect=/Navicate')
-  } catch {}
+  } catch {
+  }
 }
 const toProfile = async () => {
   push('/system/user')
 }
 
-const handlerLink = async (id) =>{
-  try {
-    const linkUrl = await UserApi.getLinkInfo(id, authUtil.getAccessToken())
-    console.log(linkUrl)
-    if (linkUrl != "") {
-      window.open(linkUrl);
+const resetPassword = async () => {
+  dialogFormVisible.value = true
+}
+
+const handlerLink = async (id) => {
+  if (id==50){
+    window.open("http://106.37.165.111:9080/pc/PCLGO001Action_001.action");
+  }else{
+    try {
+      const linkUrl = await UserApi.getLinkInfo(id, authUtil.getAccessToken())
+      if (linkUrl != "") {
+        window.open(linkUrl);
+      }
+    } catch (error) {
+      console.log(error);
     }
-  }catch (error) {
-    console.log(error);
   }
 }
 const roles = wsCache.get(CACHE_KEY.USER).roles
-console.log("######################",roles)
+// const checkRoles = (value)=>{
+//   const permissionRoles = value
+//   const roles = wsCache.get(CACHE_KEY.USER).roles
+//   const hasRole = roles.some((role) => {
+//     return super_admin === role || permissionRoles.includes(role)
+//   })
+//   return hasRole;
+// }
+const submit = (formEl: FormInstance | undefined) => {
+  if (!formEl) return
+  formEl.validate(async (valid) => {
+    if (valid) {
+      await updateUserPassword(passwordModel.oldPassword, passwordModel.newPassword)
+      message.success(t('common.updateSuccess'))
+      formEl.resetFields()
+      dialogFormVisible.value=false
+    }
+  })
+}
 
+const reset = (formEl: FormInstance | undefined) => {
+  if (!formEl) return
+  formEl.resetFields()
+}
+/** 初始化 */
+onMounted(() => {
+  const loginDadte = authUtil.getLoginDate()
+  if(!loginDadte){
+    resetPassword()
+  }
+})
+const handleClose = (done: () => void) => {
+  const loginDadte = authUtil.getLoginDate()
+  if(!loginDadte){
+    message.error('首次登录请修改密码!')
+    return false
+  }
+}
 </script>
 
+
+
 <style scoped lang="scss">
 .screen-container {
   // background: var(--app-screen-bg-color);

+ 1 - 0
sso-ui/sso-ui-admin-vue3/src/views/Login/components/LoginForm.vue

@@ -236,6 +236,7 @@ const handleLogin = async (params) => {
     }
     loginData.loginForm.captchaVerification = params.captchaVerification
     const res = await LoginApi.login(loginData.loginForm)
+    debugger
     if (!res) {
       return
     }

+ 12 - 3
sso-ui/sso-ui-admin-vue3/src/views/Profile/components/ResetPwd.vue

@@ -40,15 +40,24 @@ const equalToPassword = (_rule, value, callback) => {
     callback()
   }
 }
-
+const newPasswordValidate = (rule, value, callback) => {
+  if (password.oldPassword === value) {
+    callback(new Error('新密码不能与旧密码一致'))
+  } else if (/^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[._~!@#$^&*])[A-Za-z0-9._~!@#$^&*]{8,20}$/g.test(value)) {
+    callback()
+  } else {
+    callback(new Error('请输入包含英文字母大小写、数字和特殊符号的 8-20 位组合'))
+  }
+}
 const rules = reactive<FormRules>({
   oldPassword: [
     { required: true, message: t('profile.password.oldPwdMsg'), trigger: 'blur' },
-    { min: 6, max: 20, message: t('profile.password.pwdRules'), trigger: 'blur' }
+    { min: 8, max: 20, message: t('profile.password.pwdRules'), trigger: 'blur' }
   ],
   newPassword: [
     { required: true, message: t('profile.password.newPwdMsg'), trigger: 'blur' },
-    { min: 6, max: 20, message: t('profile.password.pwdRules'), trigger: 'blur' }
+    { min: 8, max: 20, message: t('profile.password.pwdRules'), trigger: 'blur' },
+    {required: true, validator: newPasswordValidate, trigger: 'blur'}
   ],
   confirmPassword: [
     { required: true, message: t('profile.password.cfPwdMsg'), trigger: 'blur' },

+ 1 - 1
sso-ui/sso-ui-admin-vue3/src/views/system/user/UserClientForm.vue

@@ -151,7 +151,7 @@
     clientParams.clientId = clientId.value
     clientParams.userId = userId.value
     clientParams.syncStatus = 0
-    clientParams.status = 1
+    clientParams.status = 0
     await UserClientApi.createUserClient(clientParams)
     getList()
     message.success(t('common.createSuccess'))

+ 37 - 5
sso-ui/sso-ui-admin-vue3/src/views/system/user/UserForm.vue

@@ -21,7 +21,7 @@
         <el-input v-model="formData.mobile" maxlength="11" placeholder="请输入手机号码" />
       </el-form-item>
       <el-form-item label="性别">
-        <el-select v-model="formData.sex" placeholder="请选择" disabled>
+        <el-select v-model="formData.sex" placeholder="请选择">
           <el-option
             v-for="dict in getIntDictOptions(DICT_TYPE.SYSTEM_USER_SEX)"
             :key="dict.value"
@@ -65,10 +65,11 @@
       <el-form-item v-if="formData.id === undefined" label="密码" prop="password">
         <el-input
           v-model="formData.password"
-          placeholder="请输入用户密码"
+          placeholder="请输入包含英文字母大小写、数字和特殊符号的 8-20 位组合"
           show-password
           type="password"
-          autocomplete="new-password"
+          maxlength="20"
+          @input="handlePassInput"
         />
       </el-form-item>
       <el-form-item label="创建用户" prop="roleType">
@@ -141,12 +142,27 @@ const formData = ref({
   roleIds: [],
   roleType: '1'
 })
+const passValidate = (rule, value, callback) => {
+  if (/^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[._~!@#$^&*])[A-Za-z0-9._~!@#$^&*]{8,20}$/g.test(value)) {
+    callback()
+  } else {
+    callback(new Error('请输入包含英文字母大小写、数字和特殊符号的 8-20 位组合'))
+  }
+}
 const formRules = reactive<FormRules>({
   name: [{ required: true, message: '姓名不能为空', trigger: 'blur' }],
   username: [{ required: true, message: '用户名称不能为空', trigger: 'blur' }],
   nickname: [{ required: true, message: '用户昵称不能为空', trigger: 'blur' }],
   roleType: [{ required: true, message: '创建用户权限不能为空', trigger: 'blur' }],
-  password: [{ required: true, message: '密码不能为空', trigger: 'blur' }],
+  password: [{ required: true, message: '密码不能为空', trigger: 'blur' },
+    {
+      min: 8,
+      max: 20,
+      message: '长度应在 8 到 20 个字符',
+      trigger: 'blur'
+    },
+    { validator: passValidate, trigger: 'blur' }
+  ],
   deptId: [{ required: true, message: '机构不能为空', trigger: 'blur' }],
   idNumber: [{pattern:/(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/, message: '你的身份证格式不正确' }],
   email: [
@@ -269,7 +285,16 @@ const submitForm = async () => {
     formLoading.value = false
   }
 }
-
+// let  mobileWatch = watch(
+//   ()=> formData.value.mobile,
+//   (newValue,oldValue)=>{
+//     console.log('mobile变化了',newValue,oldValue)
+//     formData.value.password = "Mm#"+newValue
+//   }
+// )
+// const handlePassInput = () =>{
+//   mobileWatch()
+// }
 /** 重置表单 */
 const resetForm = () => {
   formData.value = {
@@ -294,5 +319,12 @@ const resetForm = () => {
     roleType: '1'
   }
   formRef.value?.resetFields()
+  //  mobileWatch = watch(
+  //   ()=> formData.value.mobile,
+  //   (newValue,oldValue)=>{
+  //     console.log('mobile变化了',newValue,oldValue)
+  //     formData.value.password = "Mm#"+newValue
+  //   }
+  // )
 }
 </script>