You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

329 lines
8.2 KiB

  1. <template>
  2. <div class="form-container">
  3. <el-form
  4. ref="passwdFormRef"
  5. :model="passwd"
  6. :rules="rules"
  7. label-width="100px"
  8. class="password-form"
  9. >
  10. <h3 class="form-title">修改密码</h3>
  11. <!-- 原密码 -->
  12. <el-form-item prop="oldPassword" label="原密码">
  13. <el-input
  14. v-model.trim="passwd.oldPassword"
  15. type="password"
  16. placeholder="请输入原密码"
  17. show-password
  18. />
  19. </el-form-item>
  20. <!-- 新密码 -->
  21. <el-form-item prop="newPassword" label="新密码">
  22. <el-input
  23. v-model.trim="passwd.newPassword"
  24. type="password"
  25. placeholder="请输入新密码"
  26. show-password
  27. />
  28. </el-form-item>
  29. <!-- 重复密码 -->
  30. <el-form-item prop="againPassword" label="重复密码">
  31. <el-input
  32. v-model.trim="passwd.againPassword"
  33. type="password"
  34. placeholder="请再次输入新密码"
  35. show-password
  36. />
  37. </el-form-item>
  38. <!-- 密码规则提示 -->
  39. <div class="rule-tips">
  40. <div :class="isLengthValid ? 'tip pass' : 'tip neutral'">
  41. <el-icon>
  42. <component :is="isLengthValid ? SuccessFilled : SuccessFilled"/>
  43. </el-icon>
  44. 密码由8-16位数字字母或符号组成
  45. </div>
  46. <div :class="isComplexValid ? 'tip pass' : 'tip neutral'">
  47. <el-icon>
  48. <component :is="isComplexValid ? SuccessFilled : SuccessFilled"/>
  49. </el-icon>
  50. 至少含2种以上字符
  51. </div>
  52. <div v-if="errorMsg" class="tip fail">
  53. <el-icon>
  54. <CircleCloseFilled/>
  55. </el-icon>
  56. {{ errorMsg }}
  57. </div>
  58. </div>
  59. <!-- 按钮 -->
  60. <div class="button-group">
  61. <el-button
  62. type="primary"
  63. @click="onSubmit"
  64. style="width: 300px"
  65. :loading="loading"
  66. :disabled="!isLengthValid || !isComplexValid"
  67. >
  68. {{ loading ? '修改中...' : '确定' }}
  69. </el-button>
  70. </div>
  71. </el-form>
  72. </div>
  73. </template>
  74. <script setup>
  75. import {ref, reactive, computed, watch, onMounted} from 'vue'
  76. import {CircleCloseFilled, SuccessFilled} from '@element-plus/icons-vue'
  77. import {ElMessage} from "element-plus";
  78. import API from '@/util/http'
  79. import PasswordSuccess from './PasswordSuccess.vue';
  80. import router from "@/router/index.js";
  81. // 父组件调用
  82. const emit = defineEmits(['confirm'])
  83. const passwdFormRef = ref(null)
  84. const passwd = reactive({
  85. account: '',
  86. oldPassword: '',
  87. newPassword: '',
  88. againPassword: ''
  89. })
  90. const errorMsg = ref('')
  91. //获取用户信息的account
  92. const getAccount= async function() {
  93. try {
  94. const adminData = ref({})
  95. const result = await API({ url: '/admin/userinfo', data: {} })
  96. adminData.value = result
  97. console.log('管理员用户信息', adminData.value)
  98. passwd.account = adminData.value.account
  99. } catch (error) {
  100. console.log('请求失败', error)
  101. }
  102. }
  103. // 实时密码规则校验
  104. const isLengthValid = computed(() => passwd.newPassword.length >= 8 && passwd.newPassword.length <= 16)
  105. const isComplexValid = computed(() => {
  106. const rules = [/\d/, /[a-z]/, /[A-Z]/, /[^a-zA-Z0-9]/]
  107. return rules.filter((r) => r.test(passwd.newPassword)).length >= 2
  108. })
  109. watch(() => passwd.newPassword, (val) => {
  110. if (val && val === passwd.oldPassword) {
  111. errorMsg.value = '新密码不能与旧密码一致'
  112. } else {
  113. errorMsg.value = ''
  114. }
  115. })
  116. // 提交加载状态
  117. const loading = ref(false)
  118. // 表单校验规则
  119. const rules = reactive({
  120. oldPassword: [{required: true, message: '请输入原密码', trigger: 'blur'}],
  121. newPassword: [
  122. {required: true, message: '新密码不能为空', trigger: 'blur'},
  123. {
  124. validator: (rule, value, callback) => {
  125. if (value === passwd.oldPassword) {
  126. callback(new Error('新密码不能与旧密码一致'))
  127. } else if (value.length < 8 || value.length > 16) {
  128. callback(new Error('长度应在 8 到 16 个字符'))
  129. } else {
  130. const types = [/\d/, /[a-z]/, /[A-Z]/, /[^a-zA-Z0-9]/]
  131. const matchCount = types.filter((r) => r.test(value)).length
  132. if (matchCount < 2) {
  133. callback(new Error('密码至少包含两种类型(数字、字母或符号)'))
  134. } else {
  135. callback()
  136. }
  137. }
  138. },
  139. trigger: 'blur'
  140. }
  141. ],
  142. againPassword: [
  143. {required: true, message: '请再次输入新密码', trigger: 'blur'},
  144. {
  145. validator: (rule, value, callback) => {
  146. if (value !== passwd.newPassword) {
  147. callback(new Error('两次输入密码不一致'))
  148. } else {
  149. callback()
  150. }
  151. },
  152. trigger: 'blur'
  153. }
  154. ]
  155. })
  156. // 修改密码接口调用方法
  157. const changePassword = async function () {
  158. try {
  159. const params = {
  160. account: passwd.account,
  161. oldPassword: passwd.oldPassword,
  162. newPassword: passwd.newPassword,
  163. againPassword: passwd.againPassword
  164. }
  165. const result = await API({url: '/admin/password', data: params})
  166. console.log('@@@@@@@@@@@修改密码结果:', result)
  167. if (result.code === 0){
  168. ElMessage.error('原密码错误')
  169. }
  170. if (result.code === 200) {
  171. // 使用命名路由跳转
  172. //await router.push({ name: 'PasswordSuccess' });
  173. // 或者使用路径跳转(确保大小写完全匹配)
  174. await router.push('/PasswordSuccess');
  175. ElMessage.success('修改密码成功');
  176. resetFields();
  177. }else if(result.code === 400){
  178. // 显示失败弹窗
  179. console.log('修改密码失败')
  180. ElMessage.error('修改密码失败')
  181. //todo 待完善
  182. }
  183. } catch (error) {
  184. console.log('re:', result)
  185. console.error('修改密码失败', error)
  186. ElMessage.error('操作失败')
  187. // 抛出错误让外层捕获,保持finally正常执行
  188. throw error
  189. }
  190. }
  191. // 修改密码接口调用方法(模拟始终成功)
  192. // const changePassword = async function () {
  193. // try {
  194. // // 模拟API调用
  195. // const result = {
  196. // status: 200,
  197. // data: {success: true, message: '密码修改成功'}
  198. // };
  199. // console.log('修改密码结果:', result);
  200. // // 统一处理成功逻辑
  201. // if (result.status === 200 ) {
  202. // // 显示成功提示
  203. // ElMessage.success(result.data.message || '修改密码成功');
  204. // // 重置表单字段
  205. // resetFields();
  206. // router.replace ('/PasswordSuccess');
  207. // return result;
  208. // } else {
  209. // // 处理API返回但状态非成功的情况
  210. // const errorMsg = result.data.message || '密码修改失败';
  211. // ElMessage.error(errorMsg);
  212. // throw new Error(errorMsg);
  213. // }
  214. // } catch (error) {
  215. // // 处理网络错误或API异常
  216. // console.error('修改密码失败:', error);
  217. // ElMessage.error(error.message || '操作失败,请重试');
  218. // throw error;
  219. // }
  220. // };
  221. // 表单提交与重置
  222. const resetFields = () => {
  223. passwdFormRef.value.resetFields()
  224. errorMsg.value = ''
  225. }
  226. // 修改提交方法
  227. const onSubmit = () => {
  228. // 若正在加载中则直接返回,防止重复提交
  229. if (loading.value) return
  230. passwdFormRef.value.validate(async (valid) => {
  231. if (valid) {
  232. loading.value = true // 开始加载
  233. try {
  234. await changePassword() // 调用修改密码接口
  235. emit('confirm')
  236. } finally {
  237. loading.value = false // 无论成功失败都关闭加载态
  238. }
  239. } else {
  240. console.log('表单校验失败')
  241. }
  242. })
  243. }
  244. onMounted(() => {
  245. getAccount()
  246. })
  247. </script>
  248. <style scoped>
  249. .form-container {
  250. justify-content: center;
  251. align-items: center;
  252. }
  253. .password-form {
  254. border-radius: 8px;
  255. }
  256. .form-title {
  257. text-align: left;
  258. margin-bottom: 24px;
  259. font-size: 20px;
  260. font-weight: bold;
  261. }
  262. .button-group {
  263. display: flex;
  264. justify-content: center;
  265. margin-top: 24px;
  266. }
  267. .rule-tips {
  268. margin-left: 100px;
  269. margin-bottom: 12px;
  270. }
  271. .tip {
  272. font-size: 14px;
  273. margin: 4px 0;
  274. display: flex;
  275. align-items: center;
  276. }
  277. .pass {
  278. color: #67c23a;
  279. }
  280. .neutral {
  281. color: #999;
  282. }
  283. .fail {
  284. color: #f56c6c;
  285. }
  286. .rule-tips .el-icon {
  287. margin-right: 6px;
  288. }
  289. </style>