Просмотр исходного кода

fix(frontend): 登录/资料页防重复提交 + 修复重复调用审计

- login.vue: 手机登录按钮加 :loading/:disabled,getPhoneNumber 加 early-return 守卫
- user-edit.vue: 3个提交按钮加 :disabled,saveUserInfo/submitTeacherInfo/joinFamilyAction 加 early-return 守卫
- audit script: 守卫白名单补充 _pageShown,修复 report-list.vue 误报 ERROR
Sisyphus 2 месяцев назад
Родитель
Сommit
19a0e33102

+ 3 - 0
cfc-frontend/pages/login/login.vue

@@ -39,6 +39,8 @@
           class="phone-btn"
           open-type="getPhoneNumber"
           @getphonenumber="getPhoneNumber"
+          :loading="loading"
+          :disabled="loading"
         >
           <text class="phone-btn-icon">📱</text>
           <text>手机号快捷登录</text>
@@ -122,6 +124,7 @@ export default {
         uni.showToast({ title: '请先同意用户协议', icon: 'none' })
         return
       }
+      if (this.loading) return
       if (!e.detail || !e.detail.code) {
         uni.showToast({ title: '授权失败,请重试', icon: 'none' })
         return

+ 6 - 3
cfc-frontend/pages/user-edit/user-edit.vue

@@ -243,7 +243,7 @@
 		<!-- 新用户流程:保存资料 / 跳过 -->
 		<view class="family-section" v-if="isEdit && form.role !== 'teacher' && isNewUserFlow">
 			<view class="family-actions">
-				<button class="btn btn-primary" @click="saveUserInfo" :loading="loading">
+				<button class="btn btn-primary" @click="saveUserInfo" :loading="loading" :disabled="loading">
 					保存资料
 				</button>
 				<button class="btn btn-skip" @click="skip">
@@ -255,7 +255,7 @@
 		<!-- 修改个人资料保存按钮(非新用户流程时显示) -->
 		<view class="family-section" v-if="isEdit && !isNewUserFlow && form.role !== 'teacher'">
 			<view class="family-actions">
-				<button class="btn btn-primary" @click="saveUserInfo" :loading="loading">
+				<button class="btn btn-primary" @click="saveUserInfo" :loading="loading" :disabled="loading">
 					保存修改
 				</button>
 			</view>
@@ -264,7 +264,7 @@
 		<!-- 成长规划师提交按钮 -->
 		<view class="family-section" v-if="isEdit && form.role === 'teacher' && isNewUserFlow">
 			<view class="family-actions">
-				<button class="btn btn-primary" @click="submitTeacherInfo" :loading="loading">
+				<button class="btn btn-primary" @click="submitTeacherInfo" :loading="loading" :disabled="loading">
 					提交申请
 				</button>
 			</view>
@@ -715,6 +715,7 @@ export default {
 		},
 		// 保存修改
 		async saveUserInfo() {
+			if (this.loading) return
 			// 新用户流程:昵称可跳过(跳过时用默认昵称);老用户流程:昵称必填
 			if (!this.isNewUserFlow && !this.form.nickname) {
 				uni.showToast({ title: '请输入昵称', icon: 'none' })
@@ -784,6 +785,7 @@ export default {
 		}
 	},
 		async submitTeacherInfo() {
+			if (this.loading) return
 			// 验证必填字段
 			if (!this.form.nickname) {
 				uni.showToast({ title: '请输入昵称', icon: 'none' })
@@ -827,6 +829,7 @@ export default {
 			}
 		},
 		async joinFamilyAction() {
+			if (this.loading) return
 			if (!this.form.inviteCode) {
 				uni.showToast({ title: '请输入邀请码', icon: 'none' })
 				return

+ 2 - 2
cfc-frontend/scripts/audit-duplicate-api-calls.js

@@ -122,12 +122,12 @@ for (var f = 0; f < files.length; f++) {
       var guarded = false
       shared.forEach(function (m) {
         var mbody = hookBody(script, m)
-        if (mbody && /(?:_initDone|_inited|_loaded|_hasInit|loggedInInitDone|_firstLoad|_onLoadFired|_initialLoadDone|_mounted)\b/.test(mbody)) guarded = true
+        if (mbody && /(?:_initDone|_inited|_loaded|_hasInit|loggedInInitDone|_firstLoad|_onLoadFired|_initialLoadDone|_mounted|_pageShown)\b/.test(mbody)) guarded = true
       })
       // 也检查生命周期钩子本身是否含守卫标志(_onLoadFired 通常写在 onLoad/onShow 里)
       ;[a, b].forEach(function (h) {
         var hbody = hookBody(script, h)
-        if (hbody && /(?:_initDone|_inited|_loaded|_hasInit|loggedInInitDone|_firstLoad|_onLoadFired|_initialLoadDone|_mounted)\b/.test(hbody)) guarded = true
+        if (hbody && /(?:_initDone|_inited|_loaded|_hasInit|loggedInInitDone|_firstLoad|_onLoadFired|_initialLoadDone|_mounted|_pageShown)\b/.test(hbody)) guarded = true
       })
       violations.push({
         type: 'LIFECYCLE_DOUBLE',