From b77b4b05369fffc3615a9cac2949ceabcd66ac53 Mon Sep 17 00:00:00 2001 From: Ultrame <1019265060@qq.com> Date: Wed, 3 Dec 2025 15:44:41 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E8=8E=B7=E5=8F=96=E6=98=B5?= =?UTF-8?q?=E7=A7=B0=E4=BF=AE=E6=94=B9=E7=8A=B6=E6=80=81=E7=9A=84=E6=97=B6?= =?UTF-8?q?=E6=9C=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/CommonPopup/index.module.scss | 8 +- src/components/EditModal/index.tsx | 15 +- src/components/UserInfo/index.tsx | 6 +- src/login_pages/index/index.tsx | 3 +- src/main_pages/index.tsx | 149 ++++++++++--------- src/services/loginService.ts | 61 ++++---- src/user_pages/edit/index.tsx | 6 +- src/user_pages/withdrawal/index.scss | 3 +- src/utils/authInit.ts | 37 +++-- 9 files changed, 160 insertions(+), 128 deletions(-) diff --git a/src/components/CommonPopup/index.module.scss b/src/components/CommonPopup/index.module.scss index f9dfc02..7357ba9 100644 --- a/src/components/CommonPopup/index.module.scss +++ b/src/components/CommonPopup/index.module.scss @@ -4,7 +4,7 @@ position: fixed; z-index: 9999 !important; &:global(.nut-popup-bottom.nut-popup-round) { - border-radius: 20px 20px 0 0!important; + border-radius: 20px 20px 0 0 !important; } .common-popup__drag-handle-container { position: position; @@ -22,8 +22,8 @@ display: flex; justify-content: center; align-items: flex-start; - &::before{ - content: ''; + &::before { + content: ""; width: 32px; height: 4px; background-color: rgba(22, 24, 35, 0.2); @@ -105,7 +105,7 @@ padding: 8px 10px 0 10px; display: flex; gap: 8px; - background: #fff; + background: #fafafa; padding-bottom: max(10px, env(safe-area-inset-bottom)); } diff --git a/src/components/EditModal/index.tsx b/src/components/EditModal/index.tsx index 47807b4..4e17b0d 100644 --- a/src/components/EditModal/index.tsx +++ b/src/components/EditModal/index.tsx @@ -11,7 +11,7 @@ interface EditModalProps { placeholder: string; initialValue: string; maxLength: number; - invalidCharacters: string; + invalidCharacters: RegExp | null; onSave: (value: string) => void; onCancel: () => void; validationMessage?: string; @@ -24,7 +24,7 @@ const EditModal: React.FC = ({ placeholder, initialValue, maxLength, - invalidCharacters = "", + invalidCharacters = null, onSave, onCancel, validationMessage, @@ -63,18 +63,13 @@ const EditModal: React.FC = ({ } }, [visible, initialValue]); - const createExcludeRegex = (chars: string) => { - const escapedChars = chars.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); - const pattern = `[${escapedChars}]`; - return new RegExp(pattern); - }; const handle_input_change = (e: any) => { const new_value = e.detail.value; setValue(new_value); let ishasIllegal = false; - if (type === "nickname") { - ishasIllegal = createExcludeRegex(invalidCharacters).test(new_value); - setHasIllegal(ishasIllegal); + if (type === "nickname" && invalidCharacters) { + ishasIllegal = invalidCharacters.test(new_value); + setHasIllegal(!ishasIllegal); } const illegal = /\b(SELECT|INSERT|UPDATE|DELETE|DROP|UNION|ALTER|CREATE|EXEC|DECLARE)\b|('|--|\/\*|\*\/|;|#)|(=|'|"|`|\\|\|\|&&)|\bOR\s+['"]?[\w]+['"]?\s*=\s*['"]?[\w]+['"]?|\bUNION\s+SELECT\b|\bDROP\s+TABLE\b|\bINSERT\s+INTO\b|\bUPDATE\s+[\w]+\s+SET\b|\bDELETE\s+FROM\b/i.test( diff --git a/src/components/UserInfo/index.tsx b/src/components/UserInfo/index.tsx index 5f61347..760c73b 100644 --- a/src/components/UserInfo/index.tsx +++ b/src/components/UserInfo/index.tsx @@ -610,7 +610,11 @@ const UserInfoCardComponent: React.FC = ({ } initialValue={form_data[editing_field as keyof typeof form_data] || ""} maxLength={editing_field === "nickname" ? 20 : 100} - invalidCharacters={editing_field === "nickname" ? "@<>/" : ""} + invalidCharacters={ + editing_field === "nickname" + ? /^[\u4e00-\u9fa5a-zA-Z0-9_\-\.\(\)\s]*$/ + : null + } onSave={handle_edit_modal_save} onCancel={handle_edit_modal_cancel} validationMessage={ diff --git a/src/login_pages/index/index.tsx b/src/login_pages/index/index.tsx index 0e726ec..0cbe10d 100644 --- a/src/login_pages/index/index.tsx +++ b/src/login_pages/index/index.tsx @@ -20,7 +20,7 @@ const LoginPage: React.FC = () => { const { params: { redirect }, } = useRouter(); - const { fetchUserInfo } = useUserActions(); + const { fetchUserInfo, checkNicknameChangeStatus } = useUserActions(); // 执行微信登录的核心逻辑 const execute_wechat_login = async (e: any) => { @@ -41,6 +41,7 @@ const LoginPage: React.FC = () => { if (response.success) { save_login_state(response.token!, response.user_info!); fetchUserInfo(); + checkNicknameChangeStatus(); setTimeout(() => { if (redirect) { diff --git a/src/main_pages/index.tsx b/src/main_pages/index.tsx index 2b16d19..752276c 100644 --- a/src/main_pages/index.tsx +++ b/src/main_pages/index.tsx @@ -12,7 +12,7 @@ import ListPageContent from "./components/ListPageContent"; import MessagePageContent from "./components/MessagePageContent"; import MyselfPageContent from "./components/MyselfPageContent"; import "./index.scss"; -import FamilyContext from '@/context'; +import FamilyContext from "@/context"; type TabType = "list" | "message" | "personal"; @@ -22,14 +22,17 @@ const MainPage: React.FC = () => { const [isDistanceFilterVisible, setIsDistanceFilterVisible] = useState(false); const [isCityPickerVisible, setIsCityPickerVisible] = useState(false); const [isFilterPopupVisible, setIsFilterPopupVisible] = useState(false); - const [isShowInputCustomerNavBar, setIsShowInputCustomerNavBar] = useState(false); - const [listPageScrollToTopTrigger, setListPageScrollToTopTrigger] = useState(0); + const [isShowInputCustomerNavBar, setIsShowInputCustomerNavBar] = + useState(false); + const [listPageScrollToTopTrigger, setListPageScrollToTopTrigger] = + useState(0); const [showAuthError, setShowAuthError] = useState(false); - const [authErrorMessage, setAuthErrorMessage] = useState(''); + const [authErrorMessage, setAuthErrorMessage] = useState(""); - const { fetchUserInfo } = useUserActions(); + const { fetchUserInfo, checkNicknameChangeStatus } = useUserActions(); // 从 store 获取 GuideBar 相关状态和方法 - const { showGuideBar, guideBarZIndex, setShowGuideBar, setGuideBarZIndex } = useGlobalState(); + const { showGuideBar, guideBarZIndex, setShowGuideBar, setGuideBarZIndex } = + useGlobalState(); // 初始化:自动微信授权并获取用户信息 useEffect(() => { @@ -38,21 +41,21 @@ const MainPage: React.FC = () => { if (!hasValidToken) { try { - console.log('开始微信授权...'); + console.log("开始微信授权..."); const loginRes = await wechat_auth_login(); if (loginRes.success && loginRes.token) { save_login_state(loginRes.token, loginRes.user_info); - console.log('微信授权成功'); + console.log("微信授权成功"); } else { // 显示错误提示 - setAuthErrorMessage(loginRes.message || '微信授权失败'); + setAuthErrorMessage(loginRes.message || "微信授权失败"); setShowAuthError(true); return; } } catch (error) { - console.error('微信授权异常:', error); - setAuthErrorMessage('微信授权失败,请重试'); + console.error("微信授权异常:", error); + setAuthErrorMessage("微信授权失败,请重试"); setShowAuthError(true); return; } @@ -62,8 +65,9 @@ const MainPage: React.FC = () => { if (tokenManager.hasValidToken()) { try { await fetchUserInfo(); + await checkNicknameChangeStatus(); } catch (error) { - console.error('获取用户信息失败:', error); + console.error("获取用户信息失败:", error); } } }; @@ -75,29 +79,32 @@ const MainPage: React.FC = () => { useEffect(() => { if (showAuthError) { Taro.showModal({ - title: '授权失败', + title: "授权失败", content: `${authErrorMessage}\n\n请重启小程序后重试`, showCancel: false, - confirmText: '我知道了', + confirmText: "我知道了", success: () => { setShowAuthError(false); - } + }, }); } }, [showAuthError, authErrorMessage]); // 处理标签切换 - const handleTabChange = useCallback((code: string) => { - if (code === currentTab) { - return; - } - setCurrentTab(code as TabType); - // 切换标签时滚动到顶部 - (Taro as any).pageScrollTo({ - scrollTop: 0, - duration: 300, - }); - }, [currentTab]); + const handleTabChange = useCallback( + (code: string) => { + if (code === currentTab) { + return; + } + setCurrentTab(code as TabType); + // 切换标签时滚动到顶部 + (Taro as any).pageScrollTo({ + scrollTop: 0, + duration: 300, + }); + }, + [currentTab] + ); // 处理发布菜单显示/隐藏 const handlePublishMenuVisibleChange = useCallback((visible: boolean) => { @@ -120,28 +127,31 @@ const MainPage: React.FC = () => { }, []); // 处理列表页导航状态变化 - const handleListNavStateChange = useCallback((state: { - isShowInputCustomerNavBar?: boolean; - isDistanceFilterVisible?: boolean; - isCityPickerVisible?: boolean; - }) => { - if (state.isShowInputCustomerNavBar !== undefined) { - setIsShowInputCustomerNavBar(state.isShowInputCustomerNavBar); - } - if (state.isDistanceFilterVisible !== undefined) { - setIsDistanceFilterVisible(state.isDistanceFilterVisible); - } - if (state.isCityPickerVisible !== undefined) { - setIsCityPickerVisible(state.isCityPickerVisible); - } - }, []); + const handleListNavStateChange = useCallback( + (state: { + isShowInputCustomerNavBar?: boolean; + isDistanceFilterVisible?: boolean; + isCityPickerVisible?: boolean; + }) => { + if (state.isShowInputCustomerNavBar !== undefined) { + setIsShowInputCustomerNavBar(state.isShowInputCustomerNavBar); + } + if (state.isDistanceFilterVisible !== undefined) { + setIsDistanceFilterVisible(state.isDistanceFilterVisible); + } + if (state.isCityPickerVisible !== undefined) { + setIsCityPickerVisible(state.isCityPickerVisible); + } + }, + [] + ); // 滚动到顶部 const scrollToTop = useCallback(() => { // 如果当前是列表页,触发列表页内部滚动 if (currentTab === "list") { // 通过状态变化触发 ListPageContent 内部滚动 - setListPageScrollToTopTrigger(prev => prev + 1); + setListPageScrollToTopTrigger((prev) => prev + 1); } else { // 其他页面使用 pageScrollTo (Taro as any).pageScrollTo({ @@ -154,13 +164,22 @@ const MainPage: React.FC = () => { // 动态控制 GuideBar 的 z-index useEffect(() => { if (isPublishMenuVisible) { - setGuideBarZIndex('high'); - } else if (isDistanceFilterVisible || isCityPickerVisible || isFilterPopupVisible) { - setGuideBarZIndex('low'); + setGuideBarZIndex("high"); + } else if ( + isDistanceFilterVisible || + isCityPickerVisible || + isFilterPopupVisible + ) { + setGuideBarZIndex("low"); } else { - setGuideBarZIndex('high'); + setGuideBarZIndex("high"); } - }, [isPublishMenuVisible, isDistanceFilterVisible, isCityPickerVisible, isFilterPopupVisible]); + }, [ + isPublishMenuVisible, + isDistanceFilterVisible, + isCityPickerVisible, + isFilterPopupVisible, + ]); // 渲染自定义导航栏(参考原始页面的实现) const renderCustomNavbar = () => { @@ -190,21 +209,15 @@ const MainPage: React.FC = () => { ); } else if (currentTab === "personal") { // 我的页:使用 GeneralNavbar 显示标题 - return ( - - ); + return ; } return null; }; const handleGrandchildTrigger = (value) => { - console.log('[MainPage] handleGrandchildTrigger called with:', value); + console.log("[MainPage] handleGrandchildTrigger called with:", value); setShowGuideBar(!value); - } + }; return ( @@ -241,21 +254,21 @@ const MainPage: React.FC = () => { {/* 底部导航栏 */} - { - showGuideBar ? - : - null - } - + {showGuideBar ? ( + + ) : null} ); }; export default MainPage; - diff --git a/src/services/loginService.ts b/src/services/loginService.ts index db085de..de46e72 100644 --- a/src/services/loginService.ts +++ b/src/services/loginService.ts @@ -82,7 +82,7 @@ export interface UserInfoType { // 微信授权登录 // phone_code: 可选参数,如果提供则绑定手机号,否则只进行微信授权 export const wechat_auth_login = async ( - phone_code?: string, + phone_code?: string ): Promise => { try { // 先进行微信登录获取code @@ -144,7 +144,7 @@ export interface ChangePhoneParams { // 手机号验证码登录 export const phone_auth_login = async ( - params: PhoneLoginParams, + params: PhoneLoginParams ): Promise => { try { // 使用 httpService 调用验证验证码接口 @@ -158,6 +158,7 @@ export const phone_auth_login = async ( // 登录成功后,更新用户信息到 store try { await useUser.getState().fetchUserInfo(); + await useUser.getState().checkNicknameChangeStatus(); } catch (error) { console.error("更新用户信息到 store 失败:", error); } @@ -216,7 +217,7 @@ export const send_sms_code = async (phone: string): Promise => { // 验证短信验证码 export const verify_sms_code = async ( phone: string, - code: string, + code: string ): Promise => { try { const response = await httpService.post("user/sms/verify", { @@ -378,8 +379,6 @@ export const refresh_login_status = async (): Promise => { } }; - - // 更新用户手机号 export const updateUserPhone = async (payload: ChangePhoneParams) => { try { @@ -389,13 +388,17 @@ export const updateUserPhone = async (payload: ChangePhoneParams) => { console.error("更新用户手机号失败:", error); throw error; } -} +}; // 获取指定用户信息 export const getUserInfoById = async (id) => { try { - const response = await httpService.post("/user/detail_by_id", { id }, { - showLoading: false, - },); + const response = await httpService.post( + "/user/detail_by_id", + { id }, + { + showLoading: false, + } + ); return response; } catch (error) { console.error("获取用户信息失败:", error); @@ -436,13 +439,13 @@ let silentLoginPromise: Promise | null = null; export const silentLogin = async (): Promise => { // 如果已经有正在进行的静默登录,直接返回该 Promise if (silentLoginPromise) { - console.log('静默登录正在进行中,等待结果...'); + console.log("静默登录正在进行中,等待结果..."); return silentLoginPromise; } // 先检查是否已经登录 if (check_login_status()) { - console.log('已登录,跳过静默登录'); + console.log("已登录,跳过静默登录"); return { success: true, message: "已登录", @@ -454,13 +457,13 @@ export const silentLogin = async (): Promise => { // 创建静默登录 Promise silentLoginPromise = (async (): Promise => { try { - console.log('开始执行静默登录...'); + console.log("开始执行静默登录..."); // 调用微信登录获取code const login_result = await Taro.login(); - console.log('微信登录结果:', login_result); + console.log("微信登录结果:", login_result); if (!login_result.code) { - console.error('微信登录失败:未获取到code'); + console.error("微信登录失败:未获取到code"); return { success: false, message: "微信登录失败", @@ -468,15 +471,19 @@ export const silentLogin = async (): Promise => { } // 调用微信授权接口,不传 phone_code(静默登录) - console.log('调用后端接口进行静默登录...'); - const auth_response = await httpService.post("user/wx_auth", { - code: login_result.code, - // 不传 phone_code,实现静默登录 - }, { - showLoading: false, // 静默登录不显示loading - }); + console.log("调用后端接口进行静默登录..."); + const auth_response = await httpService.post( + "user/wx_auth", + { + code: login_result.code, + // 不传 phone_code,实现静默登录 + }, + { + showLoading: false, // 静默登录不显示loading + } + ); - console.log('后端接口响应:', auth_response); + console.log("后端接口响应:", auth_response); if (auth_response.code === 0) { const token = auth_response.data?.token || ""; @@ -484,11 +491,11 @@ export const silentLogin = async (): Promise => { // 保存登录状态 if (token && user_info) { - console.log('保存登录状态...'); + console.log("保存登录状态..."); save_login_state(token, user_info); - console.log('静默登录成功,已保存登录状态'); + console.log("静默登录成功,已保存登录状态"); } else { - console.warn('静默登录成功,但token或user_info为空'); + console.warn("静默登录成功,但token或user_info为空"); } return { @@ -498,7 +505,7 @@ export const silentLogin = async (): Promise => { user_info, }; } else { - console.error('静默登录失败:', auth_response.message); + console.error("静默登录失败:", auth_response.message); return { success: false, message: auth_response.message || "静默登录失败", @@ -517,4 +524,4 @@ export const silentLogin = async (): Promise => { })(); return silentLoginPromise; -}; \ No newline at end of file +}; diff --git a/src/user_pages/edit/index.tsx b/src/user_pages/edit/index.tsx index a109011..c3b6275 100644 --- a/src/user_pages/edit/index.tsx +++ b/src/user_pages/edit/index.tsx @@ -819,7 +819,11 @@ const EditProfilePage: React.FC = () => { } initialValue={form_data[editing_field as keyof typeof form_data] || ""} maxLength={editing_field === "nickname" ? 24 : 100} - invalidCharacters={editing_field === "nickname" ? "@<>/" : ""} + invalidCharacters={ + editing_field === "nickname" + ? /^[\u4e00-\u9fa5a-zA-Z0-9_\-\.\(\)\s]*$/ + : null + } onSave={handle_edit_modal_save} onCancel={handle_edit_modal_cancel} validationMessage={ diff --git a/src/user_pages/withdrawal/index.scss b/src/user_pages/withdrawal/index.scss index 2d2880f..1629f6a 100644 --- a/src/user_pages/withdrawal/index.scss +++ b/src/user_pages/withdrawal/index.scss @@ -156,12 +156,11 @@ .popup_text { font-family: DingTalk JinBuTi; - font-weight: 400; + font-weight: 600; font-style: Regular; font-size: 20px; line-height: 16px; margin: 20px 0; - font-style: italic; .integer { font-size: 36px; } diff --git a/src/utils/authInit.ts b/src/utils/authInit.ts index 7aa474c..963e63e 100644 --- a/src/utils/authInit.ts +++ b/src/utils/authInit.ts @@ -1,4 +1,8 @@ -import { check_login_status, get_user_token, silentLogin } from "@/services/loginService"; +import { + check_login_status, + get_user_token, + silentLogin, +} from "@/services/loginService"; import { useUser } from "@/store/userStore"; import tokenManager from "@/utils/tokenManager"; @@ -15,7 +19,7 @@ const initUserAuthCore = async (): Promise => { // 如果已经登录且有用户信息,跳过 const login_status = check_login_status(); - const { user, fetchUserInfo } = useUser.getState(); + const { user, fetchUserInfo, checkNicknameChangeStatus } = useUser.getState(); if (login_status && user && Object.keys(user).length > 0) { return Promise.resolve(); } @@ -27,8 +31,9 @@ const initUserAuthCore = async (): Promise => { // 已登录,获取用户信息 try { await fetchUserInfo(); + await checkNicknameChangeStatus(); } catch (error) { - console.error('获取用户信息失败:', error); + console.error("获取用户信息失败:", error); } } else { // 未登录,尝试静默登录 @@ -37,14 +42,15 @@ const initUserAuthCore = async (): Promise => { if (loginResult.success) { // 静默登录成功,获取用户信息 await fetchUserInfo(); + await checkNicknameChangeStatus(); } } catch (error) { - console.error('静默登录失败:', error); + console.error("静默登录失败:", error); // 静默登录失败不影响使用 } } } catch (error) { - console.error('初始化用户授权失败:', error); + console.error("初始化用户授权失败:", error); } finally { isInitializingAuth = false; authInitPromise = null; @@ -60,7 +66,7 @@ export const waitForAuthInit = async (): Promise => { if (tokenManager.hasValidToken() || get_user_token()) { return Promise.resolve(); } - + // 如果正在初始化,等待完成 if (authInitPromise) { await authInitPromise; @@ -69,7 +75,7 @@ export const waitForAuthInit = async (): Promise => { return; } } - + // 检查是否已经登录且有用户信息 const login_status = check_login_status(); const { user } = useUser.getState(); @@ -79,22 +85,26 @@ export const waitForAuthInit = async (): Promise => { return Promise.resolve(); } } - + // 触发初始化 const promise = initUserAuthCore(); await promise; - + // 等待 token 真正存在(最多等待 2 秒) let retryCount = 0; const maxRetries = 20; // 20 * 100ms = 2秒 - while (retryCount < maxRetries && !tokenManager.hasValidToken() && !get_user_token()) { - await new Promise(resolve => setTimeout(resolve, 100)); + while ( + retryCount < maxRetries && + !tokenManager.hasValidToken() && + !get_user_token() + ) { + await new Promise((resolve) => setTimeout(resolve, 100)); retryCount++; } - + // 如果还是没有 token,记录警告但继续执行 if (!tokenManager.hasValidToken() && !get_user_token()) { - console.warn('等待静默登录完成,但未获取到 token'); + console.warn("等待静默登录完成,但未获取到 token"); } }; @@ -102,4 +112,3 @@ export const waitForAuthInit = async (): Promise => { export const initUserAuth = () => { return initUserAuthCore(); }; -