This commit is contained in:
2025-10-16 21:16:57 +08:00
parent 363630cdb5
commit 9e2fa335aa
13 changed files with 172 additions and 57 deletions

View File

@@ -55,17 +55,17 @@ const CommonPopup: React.FC<CommonPopupProps> = ({
const handleTouchStart = (e: any) => { const handleTouchStart = (e: any) => {
if (!enableDragToClose) return if (!enableDragToClose) return
touchStartY.current = e.touches[0].clientY touchStartY.current = e.touches[0].clientY
setIsDragging(true) setIsDragging(true)
} }
const handleTouchMove = (e: any) => { const handleTouchMove = (e: any) => {
if (!enableDragToClose || !isDragging) return if (!enableDragToClose || !isDragging) return
const currentY = e.touches[0].clientY const currentY = e.touches[0].clientY
const deltaY = currentY - touchStartY.current const deltaY = currentY - touchStartY.current
// 只允许向下拖动,限制最大拖动距离 // 只允许向下拖动,限制最大拖动距离
if (deltaY > 0) { if (deltaY > 0) {
setDragOffset(Math.min(deltaY, 200)) setDragOffset(Math.min(deltaY, 200))
@@ -74,14 +74,14 @@ const CommonPopup: React.FC<CommonPopupProps> = ({
const handleTouchEnd = () => { const handleTouchEnd = () => {
if (!enableDragToClose || !isDragging) return if (!enableDragToClose || !isDragging) return
setIsDragging(false) setIsDragging(false)
// 如果拖动距离超过阈值,关闭弹窗 // 如果拖动距离超过阈值,关闭弹窗
if (dragOffset > 100) { if (dragOffset > 100) {
onClose() onClose()
} }
// 重置拖动偏移 // 重置拖动偏移
setDragOffset(0) setDragOffset(0)
} }
@@ -94,14 +94,14 @@ const CommonPopup: React.FC<CommonPopupProps> = ({
closeable={false} closeable={false}
onClose={onClose} onClose={onClose}
className={`${styles['common-popup']} ${className ? className : ''}`} className={`${styles['common-popup']} ${className ? className : ''}`}
style={{ style={{
zIndex: zIndex ? zIndex : undefined, zIndex: zIndex ? zIndex : undefined,
...style ...style
}} }}
> >
{enableDragToClose && ( {enableDragToClose && (
<View className={styles['common-popup__drag-handle-container']}> <View className={styles['common-popup__drag-handle-container']}>
<View <View
className={styles['common-popup__drag-handle']} className={styles['common-popup__drag-handle']}
style={{ style={{
transform: `translateX(-50%) translateY(${dragOffset * 0.3}px)`, transform: `translateX(-50%) translateY(${dragOffset * 0.3}px)`,
@@ -118,6 +118,12 @@ const CommonPopup: React.FC<CommonPopupProps> = ({
{showHeader && ( {showHeader && (
<View className={styles['common-popup__header']}> <View className={styles['common-popup__header']}>
{typeof title === 'string' ? <Text className={styles['common-popup__title']}>{title}</Text> : title} {typeof title === 'string' ? <Text className={styles['common-popup__title']}>{title}</Text> : title}
<View className={styles["close_button"]} onClick={onClose}>
<View className={styles["close_icon"]}>
<View className={styles["close_line"]}></View>
<View className={styles["close_line"]}></View>
</View>
</View>
</View> </View>
)} )}

View File

@@ -3,9 +3,11 @@
.common-popup { .common-popup {
position: fixed; position: fixed;
z-index: 9999 !important; z-index: 9999 !important;
.common-popup__drag-handle-container { .common-popup__drag-handle-container {
position: position; position: position;
} }
.common-popup__drag-handle { .common-popup__drag-handle {
position: absolute; position: absolute;
top: 6px; top: 6px;
@@ -22,22 +24,71 @@
background-color: #9ca3af; background-color: #9ca3af;
} }
} }
padding: 0; padding: 0;
box-sizing: border-box; box-sizing: border-box;
max-height: calc(100vh - 10px); max-height: calc(100vh - 10px);
display: flex; display: flex;
flex-direction: column; flex-direction: column;
background-color: theme.$page-background-color; background-color: theme.$page-background-color;
.common-popup__header {
padding: 12px 16px;
font-size: 16px;
font-weight: 600;
color: #1f2329;
border-bottom: 1px solid #f0f1f5;
}
.common-popup__title { // .common-popup__header {
display: inline-block; // padding: 12px 16px;
// font-size: 16px;
// font-weight: 600;
// color: #1f2329;
// border-bottom: 1px solid #f0f1f5;
// }
.common-popup__header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 16px 20px;
border-bottom: 1px solid rgba(0, 0, 0, 0.06);
.common-popup__title {
font-family: 'PingFang SC';
font-weight: 600;
font-size: 22px;
line-height: 1.27em;
color: #000000;
text-align: center;
}
.close_button {
width: 40px;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
background: #FFFFFF;
border: 1px solid rgba(0, 0, 0, 0.06);
border-radius: 50%;
cursor: pointer;
box-shadow: 0px 4px 36px 0px rgba(0, 0, 0, 0.06);
.close_icon {
position: relative;
width: 24px;
height: 24px;
.close_line {
position: absolute;
top: 50%;
left: 50%;
width: 17px;
height: 3px;
border-radius: 3px;
background: #000000;
transform: translate(-50%, -50%) rotate(45deg);
&:nth-child(2) {
transform: translate(-50%, -50%) rotate(-45deg);
}
}
}
}
} }
.common-popup__body { .common-popup__body {
@@ -83,4 +134,4 @@
border-radius: 12px !important; border-radius: 12px !important;
padding: 4px 10px; padding: 4px 10px;
} }
} }

View File

@@ -34,7 +34,7 @@
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
padding: 16px; padding: 16px 20px;
border-bottom: 1px solid rgba(0, 0, 0, 0.06); border-bottom: 1px solid rgba(0, 0, 0, 0.06);
.modal_title { .modal_title {
@@ -43,7 +43,6 @@
font-size: 22px; font-size: 22px;
line-height: 1.27em; line-height: 1.27em;
color: #000000; color: #000000;
flex: 1;
text-align: center; text-align: center;
} }
@@ -68,8 +67,9 @@
position: absolute; position: absolute;
top: 50%; top: 50%;
left: 50%; left: 50%;
width: 10px; width: 17px;
height: 2px; height: 3px;
border-radius: 3px;
background: #000000; background: #000000;
transform: translate(-50%, -50%) rotate(45deg); transform: translate(-50%, -50%) rotate(45deg);
@@ -176,12 +176,18 @@
box-shadow: 0px 8px 64px 0px rgba(0, 0, 0, 0.1); box-shadow: 0px 8px 64px 0px rgba(0, 0, 0, 0.1);
backdrop-filter: blur(32px); backdrop-filter: blur(32px);
&.disabled {
.save_text {
color: rgba(255, 255, 255, 0.3) !important;
}
}
.save_text { .save_text {
font-family: 'PingFang SC'; font-family: 'PingFang SC';
font-weight: 600; font-weight: 600;
font-size: 16px; font-size: 16px;
line-height: 1.4em; line-height: 1.4em;
color: rgba(255, 255, 255, 0.3); color: #fff
} }
&:active { &:active {

View File

@@ -49,6 +49,8 @@ const EditModal: React.FC<EditModalProps> = ({
useEffect(() => { useEffect(() => {
if (visible) { if (visible) {
setValue(initialValue); setValue(initialValue);
const valid = initialValue.length >= 2 && initialValue.length <= maxLength;
setIsValid(valid);
} }
}, [visible, initialValue]); }, [visible, initialValue]);
@@ -70,7 +72,6 @@ const EditModal: React.FC<EditModalProps> = ({
}); });
return; return;
} }
onSave(value); onSave(value);
}; };
@@ -85,7 +86,7 @@ const EditModal: React.FC<EditModalProps> = ({
return ( return (
<View className="edit_modal_overlay"> <View className="edit_modal_overlay">
<View className="edit_modal_container" style={{ paddingBottom: isKeyboardVisible ? (type === 'nickname' ? `${keyboardHeight + 60}px` : `${keyboardHeight }px`) : undefined }}> <View className="edit_modal_container" style={{ paddingBottom: isKeyboardVisible ? (type === 'nickname' ? `${keyboardHeight + 60}px` : `${keyboardHeight}px`) : undefined }}>
{/* 标题栏 */} {/* 标题栏 */}
<View className="modal_header"> <View className="modal_header">
<Text className="modal_title">{title}</Text> <Text className="modal_title">{title}</Text>
@@ -144,7 +145,7 @@ const EditModal: React.FC<EditModalProps> = ({
{/* 底部按钮 */} {/* 底部按钮 */}
<View className="modal_footer"> <View className="modal_footer">
<View className="save_button" onClick={handle_save}> <View className={`save_button ${!isValid ? "disabled" : ""}`} onClick={handle_save}>
<Text className="save_text"></Text> <Text className="save_text"></Text>
</View> </View>
</View> </View>

View File

@@ -17,6 +17,9 @@ interface PickerOption {
} }
interface PickerProps { interface PickerProps {
title?: string;
showHeader?: boolean;
confirmText?: string;
visible: boolean; visible: boolean;
setvisible: (visible: boolean) => void; setvisible: (visible: boolean) => void;
options?: PickerOption[][] | PickerOption[]; options?: PickerOption[][] | PickerOption[];
@@ -29,6 +32,9 @@ interface PickerProps {
} }
const PopupPicker = ({ const PopupPicker = ({
confirmText,
title,
showHeader,
visible, visible,
setvisible, setvisible,
value = [], value = [],
@@ -99,11 +105,11 @@ const PopupPicker = ({
<CommonPopup <CommonPopup
visible={visible} visible={visible}
onClose={dialogClose} onClose={dialogClose}
showHeader={false} showHeader={showHeader || false}
title={null} title={title || null}
hideFooter={false} hideFooter={false}
cancelText="取消" cancelText="取消"
confirmText="完成" confirmText={confirmText || "完成"}
onConfirm={handleConfirm} onConfirm={handleConfirm}
position="bottom" position="bottom"
round round

View File

@@ -1,10 +1,10 @@
import React, { useState } from "react"; import React, { useState, useEffect } from "react";
import Taro from "@tarojs/taro"; import Taro from "@tarojs/taro";
import { View, Text, Image, Button } from "@tarojs/components"; import { View, Text, Image, Button } from "@tarojs/components";
import "./index.scss"; import "./index.scss";
import { EditModal } from "@/components"; import { EditModal } from "@/components";
import { UserService } from "@/services/userService"; import { UserService, PickerOption } from "@/services/userService";
import { PopupPicker } from "@/components/Picker/index"; import { PopupPicker } from "@/components/Picker/index";
// 用户信息接口 // 用户信息接口
@@ -76,6 +76,34 @@ export const UserInfoCard: React.FC<UserInfoCardProps> = ({
// 表单状态 // 表单状态
const [form_data, setFormData] = useState<UserInfo>({ ...user_info }); const [form_data, setFormData] = useState<UserInfo>({ ...user_info });
// 职业数据
const [professions, setProfessions] = useState<PickerOption[]>([]);
// 城市数据
const [cities, setCities] = useState<PickerOption[]>([]);
// 页面加载时初始化数据
useEffect(() => {
getProfessions();
getCities();
}, []);
const getProfessions = async () => {
try {
const res = await UserService.getProfessions();
setProfessions(res);
} catch (e) {
console.log("获取职业失败:", e);
}
};
const getCities = async () => {
try {
const res = await UserService.getCities();
setCities(res);
} catch (e) {
console.log("获取职业失败:", e);
}
};
// 处理编辑弹窗 // 处理编辑弹窗
const handle_open_edit_modal = (field: string) => { const handle_open_edit_modal = (field: string) => {
if (field === "gender") { if (field === "gender") {
@@ -416,6 +444,8 @@ export const UserInfoCard: React.FC<UserInfoCardProps> = ({
{/* 性别选择弹窗 */} {/* 性别选择弹窗 */}
{gender_picker_visible && ( {gender_picker_visible && (
<PopupPicker <PopupPicker
showHeader={true}
title="选择性别"
options={[ options={[
[ [
{ text: "男", value: "0" }, { text: "男", value: "0" },
@@ -432,14 +462,9 @@ export const UserInfoCard: React.FC<UserInfoCardProps> = ({
{/* 地区选择弹窗 */} {/* 地区选择弹窗 */}
{location_picker_visible && ( {location_picker_visible && (
<PopupPicker <PopupPicker
options={[ showHeader={true}
[{ text: "中国", value: "中国" }], title="选择地区"
[{ text: "上海", value: "上海" }], options={cities}
[
{ text: "浦东新区", value: "浦东新区" },
{ text: "静安区", value: "静安区" },
],
]}
visible={location_picker_visible} visible={location_picker_visible}
setvisible={setLocationPickerVisible} setvisible={setLocationPickerVisible}
value={[form_data.country, form_data.province, form_data.city]} value={[form_data.country, form_data.province, form_data.city]}
@@ -449,6 +474,8 @@ export const UserInfoCard: React.FC<UserInfoCardProps> = ({
{/* NTRP水平选择弹窗 */} {/* NTRP水平选择弹窗 */}
{ntrp_picker_visible && ( {ntrp_picker_visible && (
<PopupPicker <PopupPicker
showHeader={true}
title="选择 NTRP 自评水平"
options={[ options={[
[ [
{ text: "1.5", value: "1.5" }, { text: "1.5", value: "1.5" },
@@ -471,13 +498,9 @@ export const UserInfoCard: React.FC<UserInfoCardProps> = ({
{/* 职业选择弹窗 */} {/* 职业选择弹窗 */}
{occupation_picker_visible && ( {occupation_picker_visible && (
<PopupPicker <PopupPicker
options={[ showHeader={true}
[{ text: "时尚", value: "时尚" }], title="选择职业"
[ options={professions}
{ text: "美妆博主", value: "美妆博主" },
{ text: "设计师", value: "设计师" },
],
]}
visible={occupation_picker_visible} visible={occupation_picker_visible}
setvisible={setOccupationPickerVisible} setvisible={setOccupationPickerVisible}
value={[...form_data.occupation.split(" ")]} value={[...form_data.occupation.split(" ")]}
@@ -621,9 +644,8 @@ 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")}
> >
<Text className="tab_text">{participated_text}</Text> <Text className="tab_text">{participated_text}</Text>

View File

@@ -144,9 +144,9 @@
// 过滤弹窗 // 过滤弹窗
.filter_popup { .filter_popup {
padding: 20px;
.popup_content { .popup_content {
padding: 16px 20px;
.form_section { .form_section {
.form_item { .form_item {
margin-bottom: 20px; margin-bottom: 20px;
@@ -157,7 +157,7 @@
font-weight: 600; font-weight: 600;
font-style: Semibold; font-style: Semibold;
font-size: 16px; font-size: 16px;
margin-bottom: 20px; margin-bottom: 12px;
} }
.options_wrapper { .options_wrapper {

View File

@@ -314,6 +314,7 @@ const DownloadBill: React.FC = () => {
{/* 下载账单输入密码弹窗 */} {/* 下载账单输入密码弹窗 */}
<CommonPopup <CommonPopup
showHeader={true}
visible={show_download_popup} visible={show_download_popup}
onClose={() => set_show_download_popup(false)} onClose={() => set_show_download_popup(false)}
title="提现" title="提现"
@@ -344,6 +345,7 @@ const DownloadBill: React.FC = () => {
{/* 筛选账单弹窗 */} {/* 筛选账单弹窗 */}
<CommonPopup <CommonPopup
showHeader={true}
visible={showFilterPopup} visible={showFilterPopup}
onClose={handleClose} onClose={handleClose}
onConfirm={handleTypeConfirm} onConfirm={handleTypeConfirm}

View File

@@ -629,6 +629,9 @@ const EditProfilePage: React.FC = () => {
{/* 性别选择弹窗 */} {/* 性别选择弹窗 */}
{gender_picker_visible && ( {gender_picker_visible && (
<PopupPicker <PopupPicker
showHeader={true}
title="选择性别"
confirmText="保存"
options={[ options={[
[ [
{ text: "男", value: "0" }, { text: "男", value: "0" },
@@ -645,6 +648,9 @@ const EditProfilePage: React.FC = () => {
{/* 生日选择弹窗 */} {/* 生日选择弹窗 */}
{birthday_picker_visible && ( {birthday_picker_visible && (
<PopupPicker <PopupPicker
showHeader={true}
title="选择生日"
confirmText="保存"
visible={birthday_picker_visible} visible={birthday_picker_visible}
setvisible={setBirthdayPickerVisible} setvisible={setBirthdayPickerVisible}
value={[ value={[
@@ -659,6 +665,9 @@ const EditProfilePage: React.FC = () => {
{/* 地区选择弹窗 */} {/* 地区选择弹窗 */}
{location_picker_visible && ( {location_picker_visible && (
<PopupPicker <PopupPicker
showHeader={true}
title="选择地区"
confirmText="保存"
options={cities} options={cities}
visible={location_picker_visible} visible={location_picker_visible}
setvisible={setLocationPickerVisible} setvisible={setLocationPickerVisible}
@@ -669,6 +678,9 @@ const EditProfilePage: React.FC = () => {
{/* NTRP水平选择弹窗 */} {/* NTRP水平选择弹窗 */}
{ntrp_picker_visible && ( {ntrp_picker_visible && (
<PopupPicker <PopupPicker
showHeader={true}
title="选择 NTRP 自评水平"
confirmText="保存"
options={[ options={[
[ [
{ text: "1.5", value: "1.5" }, { text: "1.5", value: "1.5" },
@@ -691,6 +703,9 @@ const EditProfilePage: React.FC = () => {
{/* 职业选择弹窗 */} {/* 职业选择弹窗 */}
{occupation_picker_visible && ( {occupation_picker_visible && (
<PopupPicker <PopupPicker
showHeader={true}
title="选择职业"
confirmText="保存"
options={professions} options={professions}
visible={occupation_picker_visible} visible={occupation_picker_visible}
setvisible={setOccupationPickerVisible} setvisible={setOccupationPickerVisible}

View File

@@ -1,3 +1,3 @@
export default definePageConfig({ export default definePageConfig({
navigationBarTitleText: "我的钱包", navigationBarTitleText: "钱包",
}); });

View File

@@ -4,6 +4,7 @@
background-color: #f5f5f5; background-color: #f5f5f5;
padding-bottom: 5px; padding-bottom: 5px;
box-sizing: border-box; box-sizing: border-box;
padding: 100px 0 40px;
&::-webkit-scrollbar { &::-webkit-scrollbar {
display: none; display: none;
@@ -364,9 +365,9 @@
// 过滤弹窗 // 过滤弹窗
.filter_popup { .filter_popup {
padding: 20px;
.popup_content { .popup_content {
padding: 16px 20px;
.form_section { .form_section {
.form_item { .form_item {
margin-bottom: 20px; margin-bottom: 20px;
@@ -377,7 +378,7 @@
font-weight: 600; font-weight: 600;
font-style: Semibold; font-style: Semibold;
font-size: 16px; font-size: 16px;
margin-bottom: 20px; margin-bottom: 12px;
} }
.options_wrapper { .options_wrapper {

View File

@@ -578,6 +578,7 @@ const WalletPage: React.FC = () => {
{/* 提现弹窗 */} {/* 提现弹窗 */}
<CommonPopup <CommonPopup
showHeader={true}
visible={show_withdraw_popup} visible={show_withdraw_popup}
onClose={() => set_show_withdraw_popup(false)} onClose={() => set_show_withdraw_popup(false)}
onConfirm={submit_withdraw} onConfirm={submit_withdraw}
@@ -619,6 +620,8 @@ const WalletPage: React.FC = () => {
{/* 选择月份弹窗 */} {/* 选择月份弹窗 */}
{showMonthPicker && ( {showMonthPicker && (
<PopupPicker <PopupPicker
showHeader={true}
title="选择月份"
visible={showMonthPicker} visible={showMonthPicker}
setvisible={setShowMonthPicker} setvisible={setShowMonthPicker}
value={[ value={[
@@ -639,6 +642,7 @@ const WalletPage: React.FC = () => {
)} )}
{/* 筛选账单弹窗 */} {/* 筛选账单弹窗 */}
<CommonPopup <CommonPopup
showHeader={true}
visible={showFilterPopup} visible={showFilterPopup}
onClose={handleFilterCancel} onClose={handleFilterCancel}
onConfirm={modify_load_transactions_params} onConfirm={modify_load_transactions_params}

View File

@@ -292,6 +292,7 @@ const Withdrawal: React.FC = () => {
{/* 提现输入密码弹窗 */} {/* 提现输入密码弹窗 */}
<CommonPopup <CommonPopup
showHeader={true}
visible={show_withdraw_popup} visible={show_withdraw_popup}
onClose={() => set_show_withdraw_popup(false)} onClose={() => set_show_withdraw_popup(false)}
title="提现" title="提现"
@@ -310,7 +311,7 @@ const Withdrawal: React.FC = () => {
)) ))
} }
</View> </View>
<Input holdKeyboard={true} ref={inputRef} focus={isFocus} type="number" adjustPosition={false} style={{ width: "0", height: "0", opacity: "0" }} value={password.filter(item => item !== "").join("")} maxlength={6} onInput={handlePasswordInput} onBlur={() => {set_show_withdraw_popup(false)}} /> <Input holdKeyboard={true} ref={inputRef} focus={isFocus} type="number" adjustPosition={false} style={{ width: "0", height: "0", opacity: "0" }} value={password.filter(item => item !== "").join("")} maxlength={6} onInput={handlePasswordInput} onBlur={() => { set_show_withdraw_popup(false) }} />
</View> </View>
</CommonPopup> </CommonPopup>
</View > </View >