changePassword.vue 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430
  1. <template>
  2. <view class="content">
  3. <up-form :model="form" labelWidth="70" ref="uFormRef">
  4. <view class="title" style="margin-top: 0;">
  5. 修改密码
  6. </view>
  7. <!-- 原密码 -->
  8. <up-form-item label="原密码" :required="rules.password[0].required" prop="password">
  9. <up-input v-model="form.password" placeholder="请输入原密码" type="password" clearable />
  10. </up-form-item>
  11. <!-- 新密码 -->
  12. <up-form-item label="新密码" :required="rules.newpassword[0].required" prop="newpassword">
  13. <up-input v-model="form.newpassword" placeholder="请输入新密码" type="password" clearable
  14. @change="onNewPwdInput" />
  15. </up-form-item>
  16. <!-- 新增:动态密码规则 -->
  17. <view v-if="form.newpassword" class="pwd-rules-box">
  18. <view class="pwd-rule" v-for="item in rulesDisplay" :key="item.key">
  19. <text :style="{ color: item.pass ? '#07c160' : '#fa5151' }">
  20. {{ item.pass ? '✔' : '✖' }} {{ item.text }}
  21. </text>
  22. </view>
  23. </view>
  24. <!-- 确认密码 -->
  25. <up-form-item label="确认密码" :required="rules.confirmPassword[0].required" prop="confirmPassword">
  26. <up-input v-model="form.confirmPassword" placeholder="请确认新密码" type="password" clearable
  27. @change="onConfirmInput" />
  28. </up-form-item>
  29. <!-- 新增:两次密码不一致提示 -->
  30. <view v-if="confirmPassword === false" class="errmsg">
  31. 两次密码不一致
  32. </view>
  33. </up-form>
  34. <view style="padding-bottom: 50px;width: 350rpx;margin: 40rpx auto 0;" @click="save">
  35. <My-button text="确认修改" :loading="loading" />
  36. </view>
  37. </view>
  38. </template>
  39. <script setup>
  40. import { ref, reactive, getCurrentInstance } from 'vue';
  41. import { onLoad } from '@dcloudio/uni-app';
  42. const { $Http } = getCurrentInstance().proxy;
  43. /* ---------------------- 表单结构 ---------------------- */
  44. const uFormRef = ref(null);
  45. const form = reactive({
  46. password: '',
  47. newpassword: '',
  48. confirmPassword: ''
  49. });
  50. const confirmPassword = ref(""); // true、false、""
  51. /* ---------------------- 从缓存中读取 site 配置 ---------------------- */
  52. const site = uni.getStorageSync("siteP") || {};
  53. /* 获取密码规则 */
  54. function getPasswordRules() {
  55. return {
  56. minLength: site.password_length || 6,
  57. needLowercase: site.password_needlowercase == 1,
  58. needUppercase: site.password_needcapital == 1,
  59. needNumber: site.password_neednum == 1,
  60. needSpecial: site.password_needspecialchar == 1
  61. };
  62. }
  63. /* ---------------------- 动态规则展示 ---------------------- */
  64. const rulesDisplay = ref([]);
  65. // 更新规则显示列表
  66. function updateRuleList(pwd) {
  67. const rules = getPasswordRules();
  68. const list = [];
  69. list.push({
  70. key: "min",
  71. text: `长度至少 ${rules.minLength} 位`,
  72. pass: pwd.length >= rules.minLength
  73. });
  74. if (rules.needLowercase)
  75. list.push({ key: "lower", text: "包含小写字母 a-z", pass: /[a-z]/.test(pwd) });
  76. if (rules.needUppercase)
  77. list.push({ key: "upper", text: "包含大写字母 A-Z", pass: /[A-Z]/.test(pwd) });
  78. if (rules.needNumber)
  79. list.push({ key: "num", text: "包含数字 0-9", pass: /\d/.test(pwd) });
  80. if (rules.needSpecial)
  81. list.push({
  82. key: "special",
  83. text: "包含特殊符号(如 !@#$% 等)",
  84. pass: /[^A-Za-z0-9]/.test(pwd)
  85. });
  86. rulesDisplay.value = list;
  87. }
  88. /* ---------------------- 输入事件 ---------------------- */
  89. function onNewPwdInput(value) {
  90. form.newpassword = value; // 保证同步
  91. updateRuleList(value);
  92. checkConfirmPassword();
  93. }
  94. function onConfirmInput(value) {
  95. form.confirmPassword = value; // 保证同步
  96. checkConfirmPassword();
  97. }
  98. // 判断两次密码是否一致
  99. function checkConfirmPassword() {
  100. if (!form.newpassword || !form.confirmPassword) {
  101. confirmPassword.value = "";
  102. return;
  103. }
  104. confirmPassword.value = form.newpassword === form.confirmPassword;
  105. }
  106. /* ---------------------- 表单验证规则 ---------------------- */
  107. const rules = reactive({
  108. password: [{ required: true, message: '请输入原密码', trigger: 'blur' }],
  109. newpassword: [
  110. { required: true, message: '请输入新密码', trigger: 'blur' },
  111. {
  112. validator: (rule, value) => {
  113. const rules = getPasswordRules();
  114. if (value.length < rules.minLength)
  115. return new Error(`密码长度至少 ${rules.minLength} 位`);
  116. if (rules.needLowercase && !/[a-z]/.test(value))
  117. return new Error("需包含小写字母");
  118. if (rules.needUppercase && !/[A-Z]/.test(value))
  119. return new Error("需包含大写字母");
  120. if (rules.needNumber && !/\d/.test(value))
  121. return new Error("需包含数字");
  122. if (rules.needSpecial && !/[^A-Za-z0-9]/.test(value))
  123. return new Error("需包含特殊符号");
  124. return true;
  125. },
  126. trigger: 'blur'
  127. }
  128. ],
  129. confirmPassword: [
  130. { required: true, message: '请确认新密码', trigger: 'input' },
  131. {
  132. validator: (rule, value) => {
  133. if (value !== form.newpassword) {
  134. return new Error("两次输入的密码不一致");
  135. }
  136. return true;
  137. },
  138. trigger: 'input'
  139. }
  140. ]
  141. });
  142. let token = null;
  143. onLoad(async (options) => {
  144. // 定义一个函数用于初始化规则
  145. const init = () => {
  146. // 根据最新 site 初始化规则提示(如果 newpassword 有值会实时计算)
  147. updateRuleList(form.newpassword);
  148. // 延迟是为了等待 uForm 渲染完成
  149. setTimeout(() => {
  150. uFormRef.value?.setRules(rules);
  151. }, 50);
  152. };
  153. // ------------------ 情况 1:通过 token 异步获取 site ------------------
  154. if (options.token) {
  155. $Http.base({
  156. "classname": "webmanage.site.site",
  157. "method": "querySite_Parameter",
  158. "content": {},
  159. "accesstoken": options.token
  160. }).then(res => {
  161. if (res.msg === "成功") {
  162. uni.setStorageSync("siteP", res.data);
  163. // 更新本地 site
  164. Object.assign(site, res.data);
  165. } else {
  166. uni.showToast({ title: res.msg, icon: "none" });
  167. }
  168. token = options.token;
  169. init(); // 异步返回后初始化规则
  170. }).catch(() => {
  171. init(); // 保底执行
  172. });
  173. return; // 结束 onLoad,避免执行后面的本地初始化
  174. }
  175. // ------------------ 情况 2:直接读取本地缓存 site ------------------
  176. const localSite = uni.getStorageSync("siteP");
  177. if (localSite) Object.assign(site, localSite);
  178. init(); // 本地立即初始化
  179. });
  180. /* ---------------------- 保存 ---------------------- */
  181. let loading = ref(false);
  182. async function save() {
  183. if (loading.value) return;
  184. // 先跑 uView 表单校验
  185. uFormRef.value.validate().then(valid => {
  186. if (!valid) return;
  187. if (confirmPassword.value !== true) {
  188. return uni.showToast({ title: "两次密码不一致", icon: "none" });
  189. }
  190. loading.value = true;
  191. // 发起实际请求
  192. $Http.base({
  193. "classname": "common.usercenter.usercenter",
  194. "method": "changePassWord",
  195. "content": {
  196. "newpassword": hexMD5(form.newpassword),
  197. "password": hexMD5(form.password),
  198. },
  199. "accesstoken": token || uni.getStorageSync("userMsg").token
  200. }).then(res => {
  201. loading.value = false;
  202. if (res.code == 1) {
  203. uni.showToast({ title: '密码修改成功,请重新登录', icon: 'none', mask: true });
  204. $Http.claerPassword && $Http.claerPassword(); // 清除密码缓存
  205. setTimeout(() => uni.reLaunch({ url: '/pages/login/login' }), 500);
  206. uni.removeStorageSync('accountnoPwd');
  207. uni.setStorageSync('accountnoPwd', '');
  208. } else {
  209. uni.showToast({ title: res.msg || '密码修改失败', icon: 'none' });
  210. }
  211. });
  212. }).catch(err => {
  213. console.error("表单验证失败", err);
  214. });
  215. }
  216. /* 以下为MD5加密 */
  217. function hexMD5(str) {
  218. return binl2hex(coreMD5(str2binl(str)))
  219. }
  220. function safe_add(x, y) {
  221. var lsw = (x & 0xFFFF) + (y & 0xFFFF)
  222. var msw = (x >> 16) + (y >> 16) + (lsw >> 16)
  223. return (msw << 16) | (lsw & 0xFFFF)
  224. }
  225. function rol(num, cnt) {
  226. return (num << cnt) | (num >>> (32 - cnt))
  227. }
  228. function cmn(q, a, b, x, s, t) {
  229. return safe_add(rol(safe_add(safe_add(a, q), safe_add(x, t)), s), b)
  230. }
  231. function ff(a, b, c, d, x, s, t) {
  232. return cmn((b & c) | ((~b) & d), a, b, x, s, t)
  233. }
  234. function gg(a, b, c, d, x, s, t) {
  235. return cmn((b & d) | (c & (~d)), a, b, x, s, t)
  236. }
  237. function hh(a, b, c, d, x, s, t) {
  238. return cmn(b ^ c ^ d, a, b, x, s, t)
  239. }
  240. function ii(a, b, c, d, x, s, t) {
  241. return cmn(c ^ (b | (~d)), a, b, x, s, t)
  242. }
  243. function coreMD5(x) {
  244. var a = 1732584193
  245. var b = -271733879
  246. var c = -1732584194
  247. var d = 271733878
  248. for (var i = 0; i < x.length; i += 16) {
  249. var olda = a
  250. var oldb = b
  251. var oldc = c
  252. var oldd = d
  253. a = ff(a, b, c, d, x[i + 0], 7, -680876936)
  254. d = ff(d, a, b, c, x[i + 1], 12, -389564586)
  255. c = ff(c, d, a, b, x[i + 2], 17, 606105819)
  256. b = ff(b, c, d, a, x[i + 3], 22, -1044525330)
  257. a = ff(a, b, c, d, x[i + 4], 7, -176418897)
  258. d = ff(d, a, b, c, x[i + 5], 12, 1200080426)
  259. c = ff(c, d, a, b, x[i + 6], 17, -1473231341)
  260. b = ff(b, c, d, a, x[i + 7], 22, -45705983)
  261. a = ff(a, b, c, d, x[i + 8], 7, 1770035416)
  262. d = ff(d, a, b, c, x[i + 9], 12, -1958414417)
  263. c = ff(c, d, a, b, x[i + 10], 17, -42063)
  264. b = ff(b, c, d, a, x[i + 11], 22, -1990404162)
  265. a = ff(a, b, c, d, x[i + 12], 7, 1804603682)
  266. d = ff(d, a, b, c, x[i + 13], 12, -40341101)
  267. c = ff(c, d, a, b, x[i + 14], 17, -1502002290)
  268. b = ff(b, c, d, a, x[i + 15], 22, 1236535329)
  269. a = gg(a, b, c, d, x[i + 1], 5, -165796510)
  270. d = gg(d, a, b, c, x[i + 6], 9, -1069501632)
  271. c = gg(c, d, a, b, x[i + 11], 14, 643717713)
  272. b = gg(b, c, d, a, x[i + 0], 20, -373897302)
  273. a = gg(a, b, c, d, x[i + 5], 5, -701558691)
  274. d = gg(d, a, b, c, x[i + 10], 9, 38016083)
  275. c = gg(c, d, a, b, x[i + 15], 14, -660478335)
  276. b = gg(b, c, d, a, x[i + 4], 20, -405537848)
  277. a = gg(a, b, c, d, x[i + 9], 5, 568446438)
  278. d = gg(d, a, b, c, x[i + 14], 9, -1019803690)
  279. c = gg(c, d, a, b, x[i + 3], 14, -187363961)
  280. b = gg(b, c, d, a, x[i + 8], 20, 1163531501)
  281. a = gg(a, b, c, d, x[i + 13], 5, -1444681467)
  282. d = gg(d, a, b, c, x[i + 2], 9, -51403784)
  283. c = gg(c, d, a, b, x[i + 7], 14, 1735328473)
  284. b = gg(b, c, d, a, x[i + 12], 20, -1926607734)
  285. a = hh(a, b, c, d, x[i + 5], 4, -378558)
  286. d = hh(d, a, b, c, x[i + 8], 11, -2022574463)
  287. c = hh(c, d, a, b, x[i + 11], 16, 1839030562)
  288. b = hh(b, c, d, a, x[i + 14], 23, -35309556)
  289. a = hh(a, b, c, d, x[i + 1], 4, -1530992060)
  290. d = hh(d, a, b, c, x[i + 4], 11, 1272893353)
  291. c = hh(c, d, a, b, x[i + 7], 16, -155497632)
  292. b = hh(b, c, d, a, x[i + 10], 23, -1094730640)
  293. a = hh(a, b, c, d, x[i + 13], 4, 681279174)
  294. d = hh(d, a, b, c, x[i + 0], 11, -358537222)
  295. c = hh(c, d, a, b, x[i + 3], 16, -722521979)
  296. b = hh(b, c, d, a, x[i + 6], 23, 76029189)
  297. a = hh(a, b, c, d, x[i + 9], 4, -640364487)
  298. d = hh(d, a, b, c, x[i + 12], 11, -421815835)
  299. c = hh(c, d, a, b, x[i + 15], 16, 530742520)
  300. b = hh(b, c, d, a, x[i + 2], 23, -995338651)
  301. a = ii(a, b, c, d, x[i + 0], 6, -198630844)
  302. d = ii(d, a, b, c, x[i + 7], 10, 1126891415)
  303. c = ii(c, d, a, b, x[i + 14], 15, -1416354905)
  304. b = ii(b, c, d, a, x[i + 5], 21, -57434055)
  305. a = ii(a, b, c, d, x[i + 12], 6, 1700485571)
  306. d = ii(d, a, b, c, x[i + 3], 10, -1894986606)
  307. c = ii(c, d, a, b, x[i + 10], 15, -1051523)
  308. b = ii(b, c, d, a, x[i + 1], 21, -2054922799)
  309. a = ii(a, b, c, d, x[i + 8], 6, 1873313359)
  310. d = ii(d, a, b, c, x[i + 15], 10, -30611744)
  311. c = ii(c, d, a, b, x[i + 6], 15, -1560198380)
  312. b = ii(b, c, d, a, x[i + 13], 21, 1309151649)
  313. a = ii(a, b, c, d, x[i + 4], 6, -145523070)
  314. d = ii(d, a, b, c, x[i + 11], 10, -1120210379)
  315. c = ii(c, d, a, b, x[i + 2], 15, 718787259)
  316. b = ii(b, c, d, a, x[i + 9], 21, -343485551)
  317. a = safe_add(a, olda)
  318. b = safe_add(b, oldb)
  319. c = safe_add(c, oldc)
  320. d = safe_add(d, oldd)
  321. }
  322. return [a, b, c, d]
  323. }
  324. function binl2hex(binarray) {
  325. var hex_tab = "0123456789abcdef"
  326. var str = ""
  327. for (var i = 0; i < binarray.length * 4; i++) {
  328. str += hex_tab.charAt((binarray[i >> 2] >> ((i % 4) * 8 + 4)) & 0xF) +
  329. hex_tab.charAt((binarray[i >> 2] >> ((i % 4) * 8)) & 0xF)
  330. }
  331. return str
  332. }
  333. function str2binl(str) {
  334. var nblk = ((str.length + 8) >> 6) + 1 // number of 16-word blocks
  335. var blks = new Array(nblk * 16)
  336. for (var i = 0; i < nblk * 16; i++) blks[i] = 0
  337. for (var i = 0; i < str.length; i++)
  338. blks[i >> 2] |= (str.charCodeAt(i) & 0xFF) << ((i % 4) * 8)
  339. blks[i >> 2] |= 0x80 << ((i % 4) * 8)
  340. blks[nblk * 16 - 2] = str.length * 8
  341. return blks
  342. }
  343. </script>
  344. <style lang="scss" scoped>
  345. .content {
  346. width: 100vw;
  347. padding: 20px;
  348. min-height: 100vh;
  349. background: #fff;
  350. box-sizing: border-box;
  351. .title {
  352. font-size: 34rpx;
  353. color: #4773EE;
  354. margin: 20rpx 0;
  355. font-weight: bold;
  356. }
  357. }
  358. /* 新增:动态密码规则样式 */
  359. .pwd-rules-box {
  360. margin: 10rpx 0 20rpx;
  361. font-size: 26rpx;
  362. line-height: 40rpx;
  363. box-sizing: border-box;
  364. }
  365. .pwd-rule {
  366. margin: 6rpx 0;
  367. }
  368. .errmsg {
  369. color: #fa5151;
  370. margin: 10rpx 0 20rpx;
  371. }
  372. </style>