jinyaqiu 30d22715fd
All checks were successful
Dev Deploy / Explore-Gitea-Actions (push) Successful in 31s
feat: 注释掉地区搜索
2025-08-08 19:16:51 +08:00

349 lines
14 KiB
TypeScript

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 { CascaderItem } from '@/components/cascader';
import ClassifyModal from '@/components/owner/classify';
import LocationModal from '@/components/owner/location';
import PodiumComponent from '@/components/owner/podium';
import RankList from '@/components/owner/rankList';
import { findInnermostElement } from '@/components/owner/utils';
import { ThemedText } from '@/components/ThemedText';
import { convertRegions } from '@/components/utils/cascaderData';
import { transformData } from '@/components/utils/objectToCascader';
import { fetchApi } from '@/lib/server-api-util';
import { GroupedData, RankingItem, TargetItem } from '@/types/user';
import { useRouter } from "expo-router";
import * as SecureStore from 'expo-secure-store';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { Keyboard, LayoutChangeEvent, Platform, StyleSheet, TouchableOpacity, TouchableWithoutFeedback, View } from 'react-native';
import { useSafeAreaInsets } from "react-native-safe-area-context";
interface LocationData {
id: number;
name: string;
children: LocationData[];
}
export default function OwnerPage() {
const insets = useSafeAreaInsets();
const router = useRouter();
const HOT_CITIES = [
['北京', '上海', '广州', '深圳'],
['杭州', '成都', '乌鲁木齐', '武汉'],
['西安', '重庆', '西宁', '哈尔滨'],
['长沙', '南宁', '贵阳', '昆明']
];
// 位置搜索数据
const [locationSearch, setLocationSearch] = useState('');
// 位置弹窗
const [locationModalVisible, setLocationModalVisible] = useState(false);
// 分类弹窗
const [classifyModalVisible, setClassifyModalVisible] = useState(false);
// 地区数据
const [locationData, setLocationData] = useState<CascaderItem[]>([]);
// 在组件内部添加:
const podiumRef = useRef<View>(null);
const [podiumPosition, setPodiumPosition] = useState({ x: 0, y: 0, width: 0, height: 0 });
// 获取分类
const [classify, setClassify] = useState<TargetItem[]>([]);
const getClassify = () => {
fetchApi<GroupedData>("/title-tags").then((res: GroupedData) => {
setSelectedClassify([transformData(res)?.[0]?.children?.[0]]);
setClassify(transformData(res));
});
}
// 选择地区
const [selectedLocation, setSelectedLocation] = useState<any>();
// 选择分类
const [selectedClassify, setSelectedClassify] = useState<any>();
const onPodiumLayout = (event: LayoutChangeEvent) => {
if (podiumRef.current) {
podiumRef.current.measure((x, y, width, height, pageX, pageY) => {
setPodiumPosition({
x: pageX,
y: pageY,
width,
height
});
});
}
};
// 地区选择
const handleLocationChange = useCallback((selectedItems: CascaderItem[]) => {
if (selectedItems.length > 0) {
const lastItem = selectedItems[selectedItems.length - 1];
// 只有当选择完成时才更新状态
if (!lastItem.children || lastItem.children.length === 0) {
setSelectedLocation(selectedItems);
}
}
}, []);
// 分类选择
const handleClassifyChange = useCallback((selectedItems: CascaderItem[]) => {
if (selectedItems.length > 0) {
const lastItem = selectedItems[selectedItems.length - 1];
// 只有当选择完成时才更新状态
if (!lastItem.children || lastItem.children.length === 0) {
setSelectedClassify(selectedItems);
}
}
}, []);
// 获取本地存储的地址信息
const getLocation = async () => {
let location;
if (Platform.OS === 'web') {
location = localStorage.getItem('location');
} else {
location = await SecureStore.getItemAsync('location');
}
return location;
};
// 获取排名信息
const [ranking, setRanking] = useState<RankingItem[]>([]);
const getRanking = () => {
fetchApi<RankingItem[]>("/title-rank", {
method: "POST",
body: JSON.stringify({
"title_tag_id": selectedClassify?.length > 0 ? selectedClassify[selectedClassify?.length - 1].value : null,
"area_id": selectedLocation?.length > 0 ? selectedLocation[selectedLocation?.length - 1].value : null
})
}).then((res) => {
setRanking(res);
});
}
// 当用户选择发生变化时,重新获取排名
useEffect(() => {
if (selectedLocation?.length > 0 && selectedClassify?.length > 0) {
getRanking();
}
}, [selectedLocation, selectedClassify])
// 初始化获取分类
useEffect(() => {
const start = async () => {
await getClassify();
}
start();
}, [])
const fetchLocationData = useMemo(() => async () => {
try {
const res = await fetchApi<LocationData>("/area/tree");
const transformed = convertRegions(res?.children, {
nameKey: 'name', // 源数据中表示"名称"的字段
valueKey: 'id', // 源数据中作为 value 的字段
regionsKey: 'children', // 源数据中表示"子级区域"的字段
childrenKey: 'children' // 输出结构中表示"子级区域"的字段
});
return transformed;
} catch (error) {
return [];
}
}, []);
useEffect(() => {
let isMounted = true;
const loadLocationData = async () => {
const data = await fetchLocationData();
if (isMounted) {
setLocationData(data);
// 获取本地存储的地址信息
const location = await getLocation();
const xuhuiElement = findInnermostElement(data?.filter((item) => {
return item.name === JSON.parse(location || "").city
}) || [], JSON.parse(location || "").district);
if (location) {
setSelectedLocation([xuhuiElement]);
}
}
};
loadLocationData();
return () => {
isMounted = false;
};
}, [fetchLocationData]);
useEffect(() => {
// console.log(locationData);
}, [locationSearch])
return (
<TouchableWithoutFeedback onPress={() => {
Keyboard.dismiss();
}}>
<View style={[styles.container, { paddingTop: insets.top }]}>
{/* 导航栏 */}
<View
style={styles.header}
ref={podiumRef}
onLayout={onPodiumLayout}
>
<TouchableOpacity onPress={() => { router.push('/owner') }} style={{ padding: 16 }}>
<ReturnArrowSvg />
</TouchableOpacity>
<ThemedText style={styles.headerTitle} onPress={() => { setClassifyModalVisible(true) }}>
{selectedClassify?.length > 0 ? selectedClassify[selectedClassify?.length - 1].name : "分类"}
</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
?
<ArrowSvg style={{ transform: [{ rotate: '90deg' }], width: 12, height: 12 }} />
:
<ReturnArrowSvg style={{ transform: [{ rotate: '270deg' }], width: 12, height: 12 }} />
} */}
</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' }}>
{selectedClassify?.length > 0 ? selectedClassify[selectedClassify?.length - 1].name : "分类"}
</ThemedText>
{selectedClassify?.length > 0
?
<ArrowSvg style={{ transform: [{ rotate: '90deg' }], width: 12, height: 12 }} />
:
<ReturnArrowSvg style={{ transform: [{ rotate: '270deg' }], width: 12, height: 12 }} />
}
</TouchableOpacity> */}
</View>
{/* 热门城市 */}
{/* <View style={styles.hotCity}>
<ThemedText size="base" color="textSecondary" style={{ marginBottom: 16 }}>热门城市</ThemedText>
{HOT_CITIES.map((row, rowIndex) => (
<View
key={`row-${rowIndex}`}
style={[styles.cityRow, rowIndex === HOT_CITIES.length - 1 && { marginBottom: 0 }]}
>
{row.map((city, cityIndex) => (
<ThemedText
key={`${city}-${cityIndex}`}
style={styles.item}
onPress={() => {
setLocationSearch(city);
}}
>
{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>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'white',
},
header: {
display: 'flex',
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginVertical: 16,
},
headerTitle: {
fontSize: 20,
fontWeight: '700',
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
}
});