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.

331 lines
8.4 KiB

2 weeks ago
  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 (!/^[a-zA-Z0-9!@#$%^&*()-_+={}[\]|\\:;"'<>,.?/~\`]+$/.test(value)) {
  126. callback(new Error('密码只能包含数字、字母或符号'));
  127. } else if (value === passwd.oldPassword) {
  128. callback(new Error('新密码不能与旧密码一致'))
  129. } else if (value.length < 8 || value.length > 16) {
  130. callback(new Error('长度应在 8 到 16 个字符'))
  131. } else {
  132. const types = [/\d/, /[a-z]/, /[A-Z]/, /[^a-zA-Z0-9]/]
  133. const matchCount = types.filter((r) => r.test(value)).length
  134. if (matchCount < 2) {
  135. callback(new Error('密码至少包含两种类型(数字、字母或符号)'))
  136. } else {
  137. callback()
  138. }
  139. }
  140. },
  141. trigger: 'blur'
  142. }
  143. ],
  144. againPassword: [
  145. {required: true, message: '请再次输入新密码', trigger: 'blur'},
  146. {
  147. validator: (rule, value, callback) => {
  148. if (value !== passwd.newPassword) {
  149. callback(new Error('两次输入密码不一致'))
  150. } else {
  151. callback()
  152. }
  153. },
  154. trigger: 'blur'
  155. }
  156. ]
  157. })
  158. // 修改密码接口调用方法
  159. const changePassword = async function () {
  160. try {
  161. const params = {
  162. account: passwd.account,
  163. oldPassword: passwd.oldPassword,
  164. newPassword: passwd.newPassword,
  165. againPassword: passwd.againPassword
  166. }
  167. const result = await API({url: '/admin/password', data: params})
  168. console.log('@@@@@@@@@@@修改密码结果:', result)
  169. if (result.code === 0){
  170. ElMessage.error('原密码错误')
  171. }
  172. if (result.code === 200) {
  173. // 使用命名路由跳转
  174. //await router.push({ name: 'PasswordSuccess' });
  175. // 或者使用路径跳转(确保大小写完全匹配)
  176. await router.push('/PasswordSuccess');
  177. ElMessage.success('修改密码成功');
  178. resetFields();
  179. }else if(result.code === 400){
  180. // 显示失败弹窗
  181. console.log('修改密码失败')
  182. ElMessage.error('修改密码失败')
  183. //todo 待完善
  184. }
  185. } catch (error) {
  186. console.log('re:', result)
  187. console.error('修改密码失败', error)
  188. ElMessage.error('操作失败')
  189. // 抛出错误让外层捕获,保持finally正常执行
  190. throw error
  191. }
  192. }
  193. // 修改密码接口调用方法(模拟始终成功)
  194. // const changePassword = async function () {
  195. // try {
  196. // // 模拟API调用
  197. // const result = {
  198. // status: 200,
  199. // data: {success: true, message: '密码修改成功'}
  200. // };
  201. // console.log('修改密码结果:', result);
  202. // // 统一处理成功逻辑
  203. // if (result.status === 200 ) {
  204. // // 显示成功提示
  205. // ElMessage.success(result.data.message || '修改密码成功');
  206. // // 重置表单字段
  207. // resetFields();
  208. // router.replace ('/PasswordSuccess');
  209. // return result;
  210. // } else {
  211. // // 处理API返回但状态非成功的情况
  212. // const errorMsg = result.data.message || '密码修改失败';
  213. // ElMessage.error(errorMsg);
  214. // throw new Error(errorMsg);
  215. // }
  216. // } catch (error) {
  217. // // 处理网络错误或API异常
  218. // console.error('修改密码失败:', error);
  219. // ElMessage.error(error.message || '操作失败,请重试');
  220. // throw error;
  221. // }
  222. // };
  223. // 表单提交与重置
  224. const resetFields = () => {
  225. passwdFormRef.value.resetFields()
  226. errorMsg.value = ''
  227. }
  228. // 修改提交方法
  229. const onSubmit = () => {
  230. // 若正在加载中则直接返回,防止重复提交
  231. if (loading.value) return
  232. passwdFormRef.value.validate(async (valid) => {
  233. if (valid) {
  234. loading.value = true // 开始加载
  235. try {
  236. await changePassword() // 调用修改密码接口
  237. emit('confirm')
  238. } finally {
  239. loading.value = false // 无论成功失败都关闭加载态
  240. }
  241. } else {
  242. console.log('表单校验失败')
  243. }
  244. })
  245. }
  246. onMounted(() => {
  247. getAccount()
  248. })
  249. </script>
  250. <style scoped>
  251. .form-container {
  252. justify-content: center;
  253. align-items: center;
  254. }
  255. .password-form {
  256. border-radius: 8px;
  257. }
  258. .form-title {
  259. text-align: left;
  260. margin-bottom: 24px;
  261. font-size: 20px;
  262. font-weight: bold;
  263. }
  264. .button-group {
  265. display: flex;
  266. justify-content: center;
  267. margin-top: 24px;
  268. }
  269. .rule-tips {
  270. margin-left: 100px;
  271. margin-bottom: 12px;
  272. }
  273. .tip {
  274. font-size: 14px;
  275. margin: 4px 0;
  276. display: flex;
  277. align-items: center;
  278. }
  279. .pass {
  280. color: #67c23a;
  281. }
  282. .neutral {
  283. color: #999;
  284. }
  285. .fail {
  286. color: #f56c6c;
  287. }
  288. .rule-tips .el-icon {
  289. margin-right: 6px;
  290. }
  291. </style>