下拉隐藏简介等信息,优化超出文本长度交互

This commit is contained in:
2025-11-24 22:42:48 +08:00
parent 78ab8c9a42
commit 31194f67be
7 changed files with 222 additions and 156 deletions

View File

@@ -140,6 +140,9 @@
font-size: 14px; font-size: 14px;
line-height: 1.71em; line-height: 1.71em;
color: rgba(60, 60, 67, 0.3); color: rgba(60, 60, 67, 0.3);
&.un-valid {
color: #FF3B30;
}
} }
} }
} }
@@ -153,6 +156,9 @@
font-size: 12px; font-size: 12px;
line-height: 1.5em; line-height: 1.5em;
color: rgba(60, 60, 67, 0.6); color: rgba(60, 60, 67, 0.6);
&.illegal {
color: #FF3B30;
}
} }
} }
} }

View File

@@ -29,6 +29,7 @@ const EditModal: React.FC<EditModalProps> = ({
}) => { }) => {
const [value, setValue] = useState(initialValue); const [value, setValue] = useState(initialValue);
const [isValid, setIsValid] = useState(true); const [isValid, setIsValid] = useState(true);
const [isIllegal, setIsIllegal] = useState(false);
// 使用全局键盘状态 // 使用全局键盘状态
const { keyboardHeight, isKeyboardVisible, addListener, initializeKeyboardListener } = useKeyboardHeight() const { keyboardHeight, isKeyboardVisible, addListener, initializeKeyboardListener } = useKeyboardHeight()
@@ -58,6 +59,8 @@ const EditModal: React.FC<EditModalProps> = ({
const new_value = e.detail.value; const new_value = e.detail.value;
setValue(new_value); setValue(new_value);
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(new_value)
setIsIllegal(illegal)
// 验证输入 // 验证输入
const valid = new_value.length >= 2 && new_value.length <= maxLength; const valid = new_value.length >= 2 && new_value.length <= maxLength;
setIsValid(valid); setIsValid(valid);
@@ -72,6 +75,14 @@ const EditModal: React.FC<EditModalProps> = ({
}); });
return; return;
} }
if (isIllegal) {
Taro.showToast({
title: "输入的字符非法",
icon: 'none',
duration: 2000
});
return;
}
onSave(value); onSave(value);
}; };
@@ -104,48 +115,61 @@ const EditModal: React.FC<EditModalProps> = ({
<View className="input_container"> <View className="input_container">
{type === 'nickname' ? ( {type === 'nickname' ? (
<>
<Input <Input
className="text_input nickname_input" className="text_input nickname_input"
value={value} value={value}
type="nickname" type="nickname"
placeholder={placeholder} placeholder={placeholder}
maxlength={maxLength} // maxlength={maxLength}
onInput={handle_input_change} onInput={handle_input_change}
adjustPosition={false} adjustPosition={false}
confirmType="done" confirmType="done"
autoFocus={true} autoFocus={true}
/> />
<View className="char_count">
<Text className={`count_text ${value.length > maxLength && "un-valid"}`}>{value.length}/{maxLength}</Text>
</View>
</>
) : ( ) : (
<> <>
<Textarea <Textarea
className="text_input" className="text_input"
value={value} value={value}
placeholder={placeholder} placeholder={placeholder}
maxlength={maxLength} // maxlength={maxLength}
onInput={handle_input_change} onInput={handle_input_change}
autoFocus={true} autoFocus={true}
adjustPosition={false} adjustPosition={false}
/> />
<View className="char_count"> <View className="char_count">
<Text className="count_text">{value.length}/{maxLength}</Text> <Text className={`count_text ${value.length > maxLength && "un-valid"}`}>{value.length}/{maxLength}</Text>
</View> </View>
</> </>
)} )}
</View> </View>
{/* 验证提示 */} {/* 验证提示 */}
{!isValid && ( {
isIllegal ?
<View className="validation_message">
<Text className="validation_text illegal">
</Text>
</View> :
!isValid && (
<View className="validation_message"> <View className="validation_message">
<Text className="validation_text"> <Text className="validation_text">
{validationMessage || `请填写 2-${maxLength} 个字符`} {validationMessage || `请填写 2-${maxLength} 个字符`}
</Text> </Text>
</View> </View>
)} )
}
</View> </View>
{/* 底部按钮 */} {/* 底部按钮 */}
<View className="modal_footer"> <View className="modal_footer">
<View className={`save_button ${!isValid ? "disabled" : ""}`} onClick={handle_save}> <View className={`save_button ${!isValid || isIllegal ? "disabled" : ""}`} onClick={handle_save}>
<Text className="save_text"></Text> <Text className="save_text"></Text>
</View> </View>
</View> </View>

View File

@@ -320,6 +320,7 @@
line-height: 1.571em; line-height: 1.571em;
color: rgba(0, 0, 0, 0.65); color: rgba(0, 0, 0, 0.65);
white-space: pre-line; white-space: pre-line;
word-break: break-all;
} }
} }
} }

View File

@@ -47,6 +47,8 @@ interface UserInfoCardProps {
user_info: Partial<UserInfoType>; user_info: Partial<UserInfoType>;
is_current_user: boolean; is_current_user: boolean;
is_following?: boolean; is_following?: boolean;
collapseProfile?: boolean;
setMarginBottom?: boolean;
on_follow?: () => void; on_follow?: () => void;
on_message?: () => void; on_message?: () => void;
on_share?: () => void; on_share?: () => void;
@@ -66,12 +68,15 @@ const UserInfoCardComponent: React.FC<UserInfoCardProps> = ({
user_info, user_info,
is_current_user, is_current_user,
is_following = false, is_following = false,
collapseProfile,
setMarginBottom = true,
on_follow, on_follow,
on_message, on_message,
on_share, on_share,
set_user_info, set_user_info,
onTab, onTab,
}) => { }) => {
const { setShowGuideBar } = useGlobalState(); const { setShowGuideBar } = useGlobalState();
const { updateUserInfo } = useUserActions(); const { updateUserInfo } = useUserActions();
@@ -170,11 +175,13 @@ const UserInfoCardComponent: React.FC<UserInfoCardProps> = ({
return; return;
} }
if (field === "nickname") { if (field === "nickname") {
if (!is_current_user) return;
// 手动输入 // 手动输入
setShowGuideBar(false); setShowGuideBar(false);
setEditingField(field); setEditingField(field);
setEditModalVisible(true); setEditModalVisible(true);
} else { } else {
if (!is_current_user) return;
setShowGuideBar(false); setShowGuideBar(false);
setEditingField(field); setEditingField(field);
setEditModalVisible(true); setEditModalVisible(true);
@@ -376,7 +383,7 @@ const UserInfoCardComponent: React.FC<UserInfoCardProps> = ({
{/* 统计数据 */} {/* 统计数据 */}
<View className="stats_section"> <View className="stats_section">
<View className="stats_container"> <View className="stats_container" style={{ marginBottom: `${collapseProfile && setMarginBottom ? "16px" : "unset"}` }}>
<View <View
className="stat_item clickable" className="stat_item clickable"
onClick={() => handle_stats_click("following")} onClick={() => handle_stats_click("following")}
@@ -454,6 +461,8 @@ const UserInfoCardComponent: React.FC<UserInfoCardProps> = ({
</View> </View>
{/* 标签和简介 */} {/* 标签和简介 */}
{
!collapseProfile ?
<View className="tags_bio_section"> <View className="tags_bio_section">
<View className="tags_container"> <View className="tags_container">
{user_info.gender && user_info.gender !== "2" ? ( {user_info.gender && user_info.gender !== "2" ? (
@@ -549,7 +558,8 @@ const UserInfoCardComponent: React.FC<UserInfoCardProps> = ({
className="personal_profile" className="personal_profile"
onClick={() => handle_open_edit_modal("personal_profile")} onClick={() => handle_open_edit_modal("personal_profile")}
> >
{user_info.personal_profile ? ( {!collapseProfile ?
user_info.personal_profile ? (
<Text className="bio_text">{user_info.personal_profile}</Text> <Text className="bio_text">{user_info.personal_profile}</Text>
) : is_current_user ? ( ) : is_current_user ? (
<View className="personal_profile_edit"> <View className="personal_profile_edit">
@@ -559,9 +569,13 @@ const UserInfoCardComponent: React.FC<UserInfoCardProps> = ({
/> />
<Text className="bio_text"></Text> <Text className="bio_text"></Text>
</View> </View>
) : null} ) :
</View> null :
null}
</View> </View>
</View> :
null
}
{/* 编辑个人简介弹窗 */} {/* 编辑个人简介弹窗 */}
<EditModal <EditModal
@@ -687,7 +701,8 @@ const arePropsEqual = (
prevUserInfoStr === nextUserInfoStr && prevUserInfoStr === nextUserInfoStr &&
prevProps.editable === nextProps.editable && prevProps.editable === nextProps.editable &&
prevProps.is_current_user === nextProps.is_current_user && prevProps.is_current_user === nextProps.is_current_user &&
prevProps.is_following === nextProps.is_following prevProps.is_following === nextProps.is_following &&
prevProps.collapseProfile === nextProps.collapseProfile
); );
}; };
@@ -827,8 +842,7 @@ export const GameTabs: React.FC<GameTabsProps> = ({
<Text className="tab_text">{hosted_text}</Text> <Text className="tab_text">{hosted_text}</Text>
</View> </View>
<View <View
className={`tab_item ${ className={`tab_item ${active_tab === "participated" ? "active" : ""
active_tab === "participated" ? "active" : ""
}`} }`}
onClick={() => on_tab_change("participated")} onClick={() => on_tab_change("participated")}
> >

View File

@@ -30,6 +30,8 @@ const MyselfPageContent: React.FC = () => {
"hosted" "hosted"
); );
const [collapseProfile, setCollapseProfile] = useState(false);
useEffect(() => { useEffect(() => {
pickerOption.getCities(); pickerOption.getCities();
pickerOption.getProfessions(); pickerOption.getProfessions();
@@ -139,8 +141,13 @@ const MyselfPageContent: React.FC = () => {
setActiveTab(tab); setActiveTab(tab);
}; };
const handleScroll = (event: any) => {
const scrollData = event.detail;
setCollapseProfile(scrollData.scrollTop > 10);
}
return ( return (
<View className={styles.myselfPage}> <ScrollView scrollY className={styles.myselfPage} onScroll={handleScroll}>
<View <View
className={styles.myselfPageContentMain} className={styles.myselfPageContentMain}
style={{ paddingTop: `${totalHeight}px` }} style={{ paddingTop: `${totalHeight}px` }}
@@ -151,9 +158,12 @@ const MyselfPageContent: React.FC = () => {
user_info={user_info} user_info={user_info}
is_current_user={is_current_user} is_current_user={is_current_user}
is_following={is_following} is_following={is_following}
collapseProfile={collapseProfile}
on_follow={handle_follow} on_follow={handle_follow}
onTab={handleOnTab} onTab={handleOnTab}
/> />
{
!collapseProfile ?
<View className={styles.quickActionsSection}> <View className={styles.quickActionsSection}>
<View className={styles.actionCard}> <View className={styles.actionCard}>
<View className={styles.actionContent} onClick={handle_game_orders}> <View className={styles.actionContent} onClick={handle_game_orders}>
@@ -172,7 +182,9 @@ const MyselfPageContent: React.FC = () => {
<Text className={styles.actionText}></Text> <Text className={styles.actionText}></Text>
</View> </View>
</View> </View>
</View> </View> :
null
}
</View> </View>
<View className={styles.testEntryCardBox}> <View className={styles.testEntryCardBox}>
@@ -188,8 +200,7 @@ const MyselfPageContent: React.FC = () => {
<Text className={styles.tabText}></Text> <Text className={styles.tabText}></Text>
</View> </View>
<View <View
className={`${styles.tabItem} ${ className={`${styles.tabItem} ${active_tab === "participated" ? styles.active : ""
active_tab === "participated" ? styles.active : ""
}`} }`}
onClick={() => setActiveTab("participated")} onClick={() => setActiveTab("participated")}
> >
@@ -211,7 +222,7 @@ const MyselfPageContent: React.FC = () => {
btnImg="ICON_ADD" btnImg="ICON_ADD"
reload={goPublish} reload={goPublish}
isShowNoData={game_records.length === 0} isShowNoData={game_records.length === 0}
loadMoreMatches={() => {}} loadMoreMatches={() => { }}
collapse={true} collapse={true}
style={{ paddingBottom: 0, overflow: "hidden" }} style={{ paddingBottom: 0, overflow: "hidden" }}
listLoadErrorHeight="320px" listLoadErrorHeight="320px"
@@ -230,7 +241,7 @@ const MyselfPageContent: React.FC = () => {
error={null} error={null}
errorImg="ICON_LIST_EMPTY" errorImg="ICON_LIST_EMPTY"
isShowNoData={ended_game_records.length === 0} isShowNoData={ended_game_records.length === 0}
loadMoreMatches={() => {}} loadMoreMatches={() => { }}
collapse={true} collapse={true}
style={{ paddingBottom: "90px", overflow: "hidden" }} style={{ paddingBottom: "90px", overflow: "hidden" }}
listLoadErrorHeight="320px" listLoadErrorHeight="320px"
@@ -239,7 +250,7 @@ const MyselfPageContent: React.FC = () => {
</ScrollView> </ScrollView>
</View> </View>
</View> </View>
</View> </ScrollView>
); );
}; };

View File

@@ -80,7 +80,7 @@
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 16px; gap: 16px;
margin-bottom: 16px; // margin-bottom: 16px;
padding: 0 15px; padding: 0 15px;
// margin-top: 98px; // margin-top: 98px;
@@ -273,6 +273,7 @@
line-height: 1.571em; line-height: 1.571em;
color: rgba(0, 0, 0, 0.65); color: rgba(0, 0, 0, 0.65);
white-space: pre-line; white-space: pre-line;
word-break: break-all;
} }
} }
} }

View File

@@ -75,6 +75,8 @@ const OtherUserPage: React.FC = () => {
"hosted" "hosted"
); );
const [collapseProfile, setCollapseProfile] = useState(false);
// 页面加载时获取用户信息 // 页面加载时获取用户信息
useEffect(() => { useEffect(() => {
const load_user_data = async () => { const load_user_data = async () => {
@@ -211,6 +213,11 @@ const OtherUserPage: React.FC = () => {
setActiveTab(tab) setActiveTab(tab)
} }
const handleScroll = (event: any) => {
const scrollData = event.detail;
setCollapseProfile(scrollData.scrollTop > 10);
}
// 处理球局详情 // 处理球局详情
// const handle_game_detail = (game_id: string) => { // const handle_game_detail = (game_id: string) => {
// Taro.navigateTo({ // Taro.navigateTo({
@@ -219,7 +226,7 @@ const OtherUserPage: React.FC = () => {
// }; // };
return ( return (
<View className="other_user_page"> <ScrollView scrollY className="other_user_page" onScroll={handleScroll}>
{/* <CustomNavbar> {/* <CustomNavbar>
<View className="navbar_content"> <View className="navbar_content">
<View className="navbar_back" onClick={() => Taro.navigateBack()}> <View className="navbar_back" onClick={() => Taro.navigateBack()}>
@@ -251,6 +258,8 @@ const OtherUserPage: React.FC = () => {
user_info={user_info} user_info={user_info}
is_current_user={false} is_current_user={false}
is_following={is_following} is_following={is_following}
collapseProfile={collapseProfile}
setMarginBottom={false}
on_follow={handle_follow} on_follow={handle_follow}
on_message={handle_send_message} on_message={handle_send_message}
onTab={handleOnTab} onTab={handleOnTab}
@@ -344,7 +353,7 @@ const OtherUserPage: React.FC = () => {
</View> </View>
</View> </View>
{/* <GuideBar currentPage="personal" /> */} {/* <GuideBar currentPage="personal" /> */}
</View> </ScrollView>
); );
}; };