All checks were successful
Dev Deploy / Explore-Gitea-Actions (push) Successful in 24s
182 lines
7.3 KiB
TypeScript
182 lines
7.3 KiB
TypeScript
import ArrowSvg from '@/assets/icons/svg/arrow.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 { ThemedText } from '@/components/ThemedText';
|
|
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 { useCallback, useEffect, useRef, useState } from 'react';
|
|
import { LayoutChangeEvent, StyleSheet, TouchableOpacity, View } from 'react-native';
|
|
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
|
export default function OwnerPage() {
|
|
const insets = useSafeAreaInsets();
|
|
const router = useRouter();
|
|
// 位置弹窗
|
|
const [locationModalVisible, setLocationModalVisible] = useState(false);
|
|
// 分类弹窗
|
|
const [classifyModalVisible, setClassifyModalVisible] = useState(false);
|
|
|
|
// 在组件内部添加:
|
|
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) => {
|
|
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[]) => {
|
|
console.log('SelectedLocation:', selectedItems);
|
|
if (selectedItems.length > 0) {
|
|
const lastItem = selectedItems[selectedItems.length - 1];
|
|
// 只有当选择完成时才更新状态
|
|
if (!lastItem.children || lastItem.children.length === 0) {
|
|
setSelectedLocation(selectedItems);
|
|
}
|
|
}
|
|
}, []);
|
|
|
|
// 分类选择
|
|
const handleClassifyChange = useCallback((selectedItems: CascaderItem[]) => {
|
|
console.log('SelectedClassify:', selectedItems);
|
|
if (selectedItems.length > 0) {
|
|
const lastItem = selectedItems[selectedItems.length - 1];
|
|
// 只有当选择完成时才更新状态
|
|
if (!lastItem.children || lastItem.children.length === 0) {
|
|
setSelectedClassify(selectedItems);
|
|
}
|
|
}
|
|
}, []);
|
|
|
|
// 获取排名信息
|
|
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 : 3,
|
|
"area_id": 1
|
|
})
|
|
}).then((res) => {
|
|
setRanking(res);
|
|
});
|
|
}
|
|
|
|
// 当用户选择发生变化时,重新获取排名
|
|
useEffect(() => {
|
|
getRanking();
|
|
}, [selectedLocation, selectedClassify])
|
|
|
|
// 初始化获取分类
|
|
useEffect(() => {
|
|
getClassify();
|
|
}, [])
|
|
|
|
return (
|
|
<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}>
|
|
Top Memory Makers
|
|
</ThemedText>
|
|
<View className='opacity-0'>123</View>
|
|
</View>
|
|
<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>
|
|
{
|
|
selectedLocation?.length > 0
|
|
?
|
|
<ArrowSvg style={{ transform: [{ rotate: '90deg' }], 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 }}>
|
|
<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>
|
|
{/* 颁奖台 */}
|
|
<PodiumComponent data={ranking} />
|
|
{/* 排名区域 */}
|
|
<RankList data={ranking} />
|
|
|
|
{/* 地区选择弹窗 */}
|
|
<LocationModal
|
|
modalVisible={locationModalVisible}
|
|
setModalVisible={setLocationModalVisible}
|
|
podiumPosition={podiumPosition}
|
|
handleChange={handleLocationChange}
|
|
/>
|
|
{/* 分类选择弹窗 */}
|
|
<ClassifyModal
|
|
data={classify}
|
|
modalVisible={classifyModalVisible}
|
|
setModalVisible={setClassifyModalVisible}
|
|
podiumPosition={podiumPosition}
|
|
handleChange={handleClassifyChange}
|
|
/>
|
|
</View>
|
|
);
|
|
}
|
|
|
|
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',
|
|
}
|
|
}); |