Compare commits

..

2 Commits

Author SHA1 Message Date
03b570450e feat: 城市搜索卡顿 2025-08-08 18:52:52 +08:00
5653058bc1 feat: 地区搜索样式 2025-08-08 18:44:09 +08:00
4 changed files with 162 additions and 48 deletions

View File

@ -1,5 +1,7 @@
import ArrowSvg from '@/assets/icons/svg/arrow.svg'; import DownSvg from '@/assets/icons/svg/down.svg';
import PlaceSvg from '@/assets/icons/svg/place.svg';
import ReturnArrowSvg from '@/assets/icons/svg/returnArrow.svg'; import ReturnArrowSvg from '@/assets/icons/svg/returnArrow.svg';
import SearchSvg from '@/assets/icons/svg/search.svg';
import { CascaderItem } from '@/components/cascader'; import { CascaderItem } from '@/components/cascader';
import ClassifyModal from '@/components/owner/classify'; import ClassifyModal from '@/components/owner/classify';
import LocationModal from '@/components/owner/location'; import LocationModal from '@/components/owner/location';
@ -14,7 +16,8 @@ import { GroupedData, RankingItem, TargetItem } from '@/types/user';
import { useRouter } from "expo-router"; import { useRouter } from "expo-router";
import * as SecureStore from 'expo-secure-store'; import * as SecureStore from 'expo-secure-store';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { LayoutChangeEvent, Platform, StyleSheet, TouchableOpacity, View } from 'react-native'; import { Keyboard, LayoutChangeEvent, Platform, StyleSheet, TouchableOpacity, TouchableWithoutFeedback, View } from 'react-native';
import { TextInput } from 'react-native-gesture-handler';
import { useSafeAreaInsets } from "react-native-safe-area-context"; import { useSafeAreaInsets } from "react-native-safe-area-context";
interface LocationData { interface LocationData {
id: number; id: number;
@ -25,6 +28,14 @@ interface LocationData {
export default function OwnerPage() { export default function OwnerPage() {
const insets = useSafeAreaInsets(); const insets = useSafeAreaInsets();
const router = useRouter(); const router = useRouter();
const HOT_CITIES = [
['北京', '上海', '广州', '深圳'],
['杭州', '成都', '乌鲁木齐', '武汉'],
['西安', '重庆', '西宁', '哈尔滨'],
['长沙', '南宁', '贵阳', '昆明']
];
// 位置搜索数据
const [locationSearch, setLocationSearch] = useState('');
// 位置弹窗 // 位置弹窗
const [locationModalVisible, setLocationModalVisible] = useState(false); const [locationModalVisible, setLocationModalVisible] = useState(false);
// 分类弹窗 // 分类弹窗
@ -161,36 +172,62 @@ export default function OwnerPage() {
}; };
}, [fetchLocationData]); }, [fetchLocationData]);
useEffect(() => {
// console.log(locationData);
}, [locationSearch])
return ( return (
<View style={[styles.container, { paddingTop: insets.top }]}> <TouchableWithoutFeedback onPress={() => {
{/* 导航栏 */} Keyboard.dismiss();
<View }}>
style={styles.header} <View style={[styles.container, { paddingTop: insets.top }]}>
ref={podiumRef} {/* 导航栏 */}
onLayout={onPodiumLayout} <View
> style={styles.header}
<TouchableOpacity onPress={() => { router.push('/owner') }} style={{ padding: 16 }}> ref={podiumRef}
<ReturnArrowSvg /> onLayout={onPodiumLayout}
</TouchableOpacity> >
<ThemedText style={styles.headerTitle} > <TouchableOpacity onPress={() => { router.push('/owner') }} style={{ padding: 16 }}>
Top Memory Makers <ReturnArrowSvg />
</ThemedText> </TouchableOpacity>
<ThemedText className='opacity-0'>123</ThemedText> <ThemedText style={styles.headerTitle} onPress={() => { setClassifyModalVisible(true) }}>
</View> {selectedClassify?.length > 0 ? selectedClassify[selectedClassify?.length - 1].name : "分类"}
<View style={{ display: 'flex', flexDirection: 'row', alignItems: 'center', gap: 16, marginHorizontal: 16 }}>
<TouchableOpacity onPress={() => { setLocationModalVisible(true) }} style={{ display: 'flex', flexDirection: 'row', alignItems: 'center', gap: 8 }}>
<ThemedText style={{ color: selectedLocation?.length > 0 ? '#FFB645' : '#4C320C' }}>
{selectedLocation?.length > 0 ? selectedLocation[selectedLocation?.length - 1].name : "地区"}
</ThemedText> </ThemedText>
{ <ThemedText className='opacity-0'>123</ThemedText>
</View>
<View style={{ display: 'flex', flexDirection: 'column', gap: 16, marginHorizontal: 16, paddingHorizontal: 32 }}>
<View style={{ display: 'flex', flexDirection: 'row', alignItems: 'center', justifyContent: "space-between", gap: 16 }}>
<TouchableOpacity onPress={() => { setLocationModalVisible(true) }} style={{ display: 'flex', flexDirection: 'row', alignItems: 'center', gap: 8 }}>
<PlaceSvg />
<ThemedText style={{ color: selectedLocation?.length > 0 ? '#FFB645' : '#4C320C' }}>
{selectedLocation?.length > 0 ? selectedLocation[selectedLocation?.length - 1].name : "地区"}
</ThemedText>
<DownSvg />
{/* {
selectedLocation?.length > 0 selectedLocation?.length > 0
? ?
<ArrowSvg style={{ transform: [{ rotate: '90deg' }], width: 12, height: 12 }} /> <ArrowSvg style={{ transform: [{ rotate: '90deg' }], width: 12, height: 12 }} />
: :
<ReturnArrowSvg style={{ transform: [{ rotate: '270deg' }], width: 12, height: 12 }} /> <ReturnArrowSvg style={{ transform: [{ rotate: '270deg' }], width: 12, height: 12 }} />
} } */}
</TouchableOpacity>
<TouchableOpacity onPress={() => { setClassifyModalVisible(true) }} style={{ display: 'flex', flexDirection: 'row', alignItems: 'center', gap: 8 }}> </TouchableOpacity>
<View style={styles.searchContainer}>
<View style={styles.searchIcon}>
<SearchSvg width={12} height={12} />
</View>
<TextInput
style={styles.input}
onChangeText={setLocationSearch}
value={locationSearch}
placeholder="输入城市名进行搜索"
onEndEditing={(text) => {
setLocationSearch(text.nativeEvent.text)
}}
/>
</View>
{/* <TouchableOpacity onPress={() => { setClassifyModalVisible(true) }} style={{ display: 'flex', flexDirection: 'row', alignItems: 'center', gap: 8 }}>
<ThemedText style={{ color: selectedClassify?.length > 0 ? '#FFB645' : '#4C320C' }}> <ThemedText style={{ color: selectedClassify?.length > 0 ? '#FFB645' : '#4C320C' }}>
{selectedClassify?.length > 0 ? selectedClassify[selectedClassify?.length - 1].name : "分类"} {selectedClassify?.length > 0 ? selectedClassify[selectedClassify?.length - 1].name : "分类"}
</ThemedText> </ThemedText>
@ -200,30 +237,56 @@ export default function OwnerPage() {
: :
<ReturnArrowSvg style={{ transform: [{ rotate: '270deg' }], width: 12, height: 12 }} /> <ReturnArrowSvg style={{ transform: [{ rotate: '270deg' }], width: 12, height: 12 }} />
} }
</TouchableOpacity> </TouchableOpacity> */}
</View>
{/* 颁奖台 */}
<PodiumComponent data={ranking} />
{/* 排名区域 */}
<RankList data={ranking} />
{/* 地区选择弹窗 */} </View>
<LocationModal {/* 热门城市 */}
modalVisible={locationModalVisible} <View style={styles.hotCity}>
setModalVisible={setLocationModalVisible} <ThemedText size="base" color="textSecondary" style={{ marginBottom: 16 }}></ThemedText>
podiumPosition={podiumPosition} {HOT_CITIES.map((row, rowIndex) => (
handleChange={handleLocationChange} <View
data={locationData} key={`row-${rowIndex}`}
/> style={[styles.cityRow, rowIndex === HOT_CITIES.length - 1 && { marginBottom: 0 }]}
{/* 分类选择弹窗 */} >
<ClassifyModal {row.map((city, cityIndex) => (
data={classify} <ThemedText
modalVisible={classifyModalVisible} key={`${city}-${cityIndex}`}
setModalVisible={setClassifyModalVisible} style={styles.item}
podiumPosition={podiumPosition} onPress={() => {
handleChange={handleClassifyChange} setLocationSearch(city);
/> }}
</View> >
{city}
</ThemedText>
))}
</View>
))}
</View>
</View>
{/* 颁奖台 */}
<PodiumComponent data={ranking} />
{/* 排名区域 */}
<RankList data={ranking} />
{/* 地区选择弹窗 */}
<LocationModal
modalVisible={locationModalVisible}
setModalVisible={setLocationModalVisible}
podiumPosition={podiumPosition}
handleChange={handleLocationChange}
data={locationData}
/>
{/* 分类选择弹窗 */}
<ClassifyModal
data={classify}
modalVisible={classifyModalVisible}
setModalVisible={setClassifyModalVisible}
podiumPosition={podiumPosition}
handleChange={handleClassifyChange}
/>
</View>
</TouchableWithoutFeedback>
); );
} }
@ -243,5 +306,46 @@ const styles = StyleSheet.create({
fontSize: 20, fontSize: 20,
fontWeight: '700', fontWeight: '700',
color: '#4C320C', color: '#4C320C',
},
searchContainer: {
position: 'relative',
flex: 1,
},
input: {
backgroundColor: '#D9D9D9',
borderRadius: 24,
paddingLeft: 40, // 给图标留出空间
paddingVertical: 8,
paddingRight: 16,
fontSize: 14,
lineHeight: 16,
},
searchIcon: {
position: 'absolute',
left: 20,
top: '50%',
zIndex: 10,
transform: [{ translateY: -6 }]
},
item: {
paddingVertical: 8,
borderRadius: 12,
fontSize: 14,
backgroundColor: '#D9D9D9',
width: '23%',
textAlign: 'center',
color: "#4C320C"
},
hotCity: {
backgroundColor: "#FBFBFB",
padding: 16,
borderRadius: 24
},
cityRow: {
display: 'flex',
flexDirection: 'row',
flexWrap: 'wrap',
justifyContent: 'space-between',
marginBottom: 16
} }
}); });

View File

@ -0,0 +1,3 @@
<svg width="11" height="10" viewBox="0 0 11 10" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.67965 8.83066C5.21851 9.764 6.56567 9.764 7.10453 8.83066L10.7418 2.53066C11.2807 1.59733 10.6071 0.430664 9.5294 0.430664H2.25478C1.17706 0.430664 0.503487 1.59733 1.04235 2.53066L4.67965 8.83066Z" fill="#635848"/>
</svg>

After

Width:  |  Height:  |  Size: 331 B

View File

@ -0,0 +1,4 @@
<svg width="14" height="19" viewBox="0 0 14 19" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M3.69653 1.76888C5.6629 0.626328 8.07989 0.646298 10.0278 1.82119C11.9566 3.02 13.1288 5.15954 13.1179 7.46108C13.073 9.74752 11.816 11.8968 10.2448 13.5583C9.33792 14.5215 8.32342 15.3733 7.22203 16.0962C7.10859 16.1618 6.98434 16.2057 6.8554 16.2258C6.73131 16.2205 6.61045 16.1838 6.50374 16.1191C4.82225 15.0329 3.34707 13.6464 2.14917 12.0263C1.1468 10.674 0.577319 9.04019 0.518067 7.34676C0.516766 5.0408 1.73016 2.91142 3.69653 1.76888ZM4.83313 8.30119C5.1639 9.11664 5.94465 9.64854 6.81083 9.64855C7.37828 9.65262 7.92375 9.42533 8.32571 9.01731C8.72767 8.6093 8.95272 8.05446 8.95071 7.47643C8.95374 6.59412 8.4343 5.79697 7.63492 5.45718C6.83555 5.1174 5.91392 5.302 5.30037 5.9248C4.68682 6.5476 4.50236 7.48574 4.83313 8.30119Z" fill="#E2793F"/>
<ellipse opacity="0.4" cx="6.81738" cy="18.026" rx="4.5" ry="0.9" fill="#E2793F"/>
</svg>

After

Width:  |  Height:  |  Size: 995 B

View File

@ -0,0 +1,3 @@
<svg width="12" height="13" viewBox="0 0 12 13" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M5.61478 9.40119C6.08057 9.40119 6.5418 9.30411 6.97213 9.1155C7.40246 8.92689 7.79347 8.65043 8.12283 8.30192C8.45219 7.95341 8.71346 7.53967 8.89171 7.08432C9.06996 6.62897 9.1617 6.14092 9.1617 5.64805C9.1617 5.15518 9.06996 4.66714 8.89171 4.21179C8.71346 3.75644 8.45219 3.34269 8.12283 2.99418C7.79347 2.64567 7.40246 2.36922 6.97213 2.18061C6.5418 1.99199 6.08057 1.89492 5.61478 1.89492C4.67408 1.89492 3.7719 2.29033 3.10673 2.99418C2.44155 3.69803 2.06786 4.65266 2.06786 5.64805C2.06786 6.64345 2.44155 7.59807 3.10673 8.30192C3.7719 9.00577 4.67408 9.40119 5.61478 9.40119ZM9.35087 8.71687L11.4672 10.9562C11.5236 11.014 11.5686 11.083 11.5995 11.1594C11.6305 11.2357 11.6467 11.3178 11.6473 11.4009C11.648 11.4839 11.633 11.5663 11.6032 11.6431C11.5734 11.72 11.5295 11.7898 11.4739 11.8485C11.4184 11.9072 11.3523 11.9536 11.2797 11.985C11.207 12.0164 11.1292 12.0321 11.0507 12.0314C10.9722 12.0306 10.8946 12.0133 10.8225 11.9804C10.7504 11.9476 10.6852 11.8999 10.6307 11.8401L8.51439 9.60073C7.56409 10.3813 6.36838 10.7493 5.17068 10.6298C3.97298 10.5104 2.86332 9.91244 2.06759 8.95773C1.27187 8.00303 0.849896 6.76333 0.887577 5.491C0.925257 4.21867 1.41976 3.00936 2.27042 2.10925C3.12107 1.20914 4.26394 0.685884 5.46636 0.646012C6.66877 0.606141 7.84036 1.05265 8.74261 1.89463C9.64486 2.73662 10.2099 3.9108 10.3228 5.17813C10.4357 6.44547 10.0879 7.71069 9.35028 8.71624L9.35087 8.71687Z" fill="#7C7C7C"/>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB