diff --git a/app/(tabs)/owner.tsx b/app/(tabs)/owner.tsx index 19dcac5..1486e8a 100644 --- a/app/(tabs)/owner.tsx +++ b/app/(tabs)/owner.tsx @@ -5,21 +5,19 @@ import StoriesSvg from '@/assets/icons/svg/stories.svg'; import UsedStorageSvg from '@/assets/icons/svg/usedStorage.svg'; import AskNavbar from '@/components/layout/ask'; import AlbumComponent from '@/components/owner/album'; -import CategoryComponent from '@/components/owner/category'; -import CountComponent from '@/components/owner/count'; +import CarouselComponent from '@/components/owner/carousel'; import CreateCountComponent from '@/components/owner/createCount'; import Ranking from '@/components/owner/ranking'; import ResourceComponent from '@/components/owner/resource'; import SettingModal from '@/components/owner/setting'; import UserInfo from '@/components/owner/userName'; -import { formatDuration } from '@/components/utils/time'; import { checkAuthStatus } from '@/lib/auth'; import { fetchApi } from '@/lib/server-api-util'; import { CountData, UserInfoDetails } from '@/types/user'; import { useRouter } from 'expo-router'; import { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { FlatList, ScrollView, StyleSheet, View } from 'react-native'; +import { FlatList, StyleSheet, View } from 'react-native'; import { useSafeAreaInsets } from "react-native-safe-area-context"; export default function OwnerPage() { @@ -97,27 +95,8 @@ export default function OwnerPage() { - {/* 数据统计 */} - - {/* 分类 */} - - - {countData?.counter?.category_count && Object.entries(countData?.counter?.category_count).map(([key, value], index) => { - return ( - - ) - })} - - + {/* 作品数据 */} diff --git a/components/owner/carousel.tsx b/components/owner/carousel.tsx new file mode 100644 index 0000000..9d2e76d --- /dev/null +++ b/components/owner/carousel.tsx @@ -0,0 +1,127 @@ +import { Counter, UserCountData } from "@/types/user"; +import * as React from "react"; +import { Dimensions, StyleSheet, View } from "react-native"; +import { useSharedValue } from "react-native-reanimated"; +import Carousel, { + ICarouselInstance +} from "react-native-reanimated-carousel"; +import { ThemedText } from "../ThemedText"; +import { formatDuration } from "../utils/time"; +import CategoryComponent from "./category"; +interface Props { + data: Counter +} + +interface CarouselData { + key: string, + value: UserCountData + +}[] +const width = Dimensions.get("window").width; + +function CarouselComponent(props: Props) { + const { data } = props; + + const ref = React.useRef(null); + const progress = useSharedValue(0); + const [carouselDataValue, setCarouselDataValue] = React.useState([]); + const dataHandle = () => { + const carouselData = { ...data?.category_count, total_count: data?.total_count } + // 1. 转换为数组并过滤掉 'total' + const entries = Object?.entries(carouselData) + ?.filter(([key]) => key !== 'total_count') + ?.map(([key, value]) => ({ key, value })); + + // 2. 找到 total 数据 + const totalEntry = { + key: 'total_count', + value: carouselData?.total_count + }; + + // 3. 插入到中间位置 + const middleIndex = Math.floor((entries || [])?.length / 2); + entries?.splice(middleIndex, 0, totalEntry); + setCarouselDataValue(entries) + return entries; + } + + const totleItem = (data: UserCountData) => { + return + {Object?.entries(data)?.filter(([key]) => key !== 'cover_url')?.map((item, index) => ( + + {item[0]} + {item[1]} + + ))} + + } + + React.useEffect(() => { + if (data) { + dataHandle() + } + }, [data]); + + return ( + + item?.key === 'total_count') - 1 || 0} + renderItem={({ item }) => { + if (item?.key === 'total_count') { + return totleItem(item.value) + } + return CategoryComponent( + { + title: item?.key, + data: + [ + { title: 'Video', number: item?.value?.video_count }, + { title: 'Photo', number: item?.value?.photo_count }, + { title: 'Length', number: formatDuration(item?.value?.video_length || 0) } + ], + bgSvg: item?.value?.cover_url, + }) + }} + /> + + ); +} + +const styles = StyleSheet.create({ + container: { + backgroundColor: "#FFB645", + padding: 16, + borderRadius: 20, + display: "flex", + flexDirection: "column", + justifyContent: "space-between", + height: '100%', + }, + item: { + flexDirection: "row", + alignItems: "center", + justifyContent: "space-between", + paddingVertical: 8 + }, + title: { + color: "#4C320C", + fontWeight: "700", + fontSize: 14, + }, + number: { + color: "#fff", + fontWeight: "700", + fontSize: 32, + textAlign: 'right', + flex: 1, + paddingTop: 8 + } +}) + +export default CarouselComponent; \ No newline at end of file diff --git a/components/owner/category.tsx b/components/owner/category.tsx index 06a0a15..f3f79ce 100644 --- a/components/owner/category.tsx +++ b/components/owner/category.tsx @@ -14,8 +14,11 @@ const CategoryComponent = ({ title, data, bgSvg, style }: CategoryProps) => { @@ -37,11 +40,12 @@ const styles = StyleSheet.create({ borderRadius: 32, overflow: 'hidden', position: 'relative', + aspectRatio: 1, }, backgroundContainer: { ...StyleSheet.absoluteFillObject, - width: '100%', - height: '100%', + width: "100%", + height: "100%", }, overlay: { ...StyleSheet.absoluteFillObject, diff --git a/package-lock.json b/package-lock.json index 2d3c8d1..66a15e1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -60,6 +60,7 @@ "react-native-picker-select": "^9.3.1", "react-native-progress": "^5.0.1", "react-native-reanimated": "~3.17.4", + "react-native-reanimated-carousel": "^4.0.2", "react-native-render-html": "^6.3.4", "react-native-safe-area-context": "5.4.0", "react-native-screens": "~4.11.1", @@ -14826,6 +14827,18 @@ "react-native": "*" } }, + "node_modules/react-native-reanimated-carousel": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/react-native-reanimated-carousel/-/react-native-reanimated-carousel-4.0.2.tgz", + "integrity": "sha512-vNpCfPlFoOVKHd+oB7B0luoJswp+nyz0NdJD8+LCrf25JiNQXfM22RSJhLaksBHqk3fm8R4fKWPNcfy5w7wL1Q==", + "license": "MIT", + "peerDependencies": { + "react": ">=18.0.0", + "react-native": ">=0.70.3", + "react-native-gesture-handler": ">=2.9.0", + "react-native-reanimated": ">=3.0.0" + } + }, "node_modules/react-native-reanimated/node_modules/react-native-is-edge-to-edge": { "version": "1.1.7", "resolved": "https://registry.npmjs.org/react-native-is-edge-to-edge/-/react-native-is-edge-to-edge-1.1.7.tgz", diff --git a/package.json b/package.json index 50c1a34..bf8c777 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "expo-audio": "~0.4.8", "expo-background-task": "^0.2.8", "expo-blur": "~14.1.5", + "expo-clipboard": "~7.1.5", "expo-constants": "~17.1.6", "expo-dev-client": "~5.2.4", "expo-device": "~7.1.4", @@ -33,6 +34,7 @@ "expo-haptics": "~14.1.4", "expo-image-manipulator": "~13.1.7", "expo-image-picker": "~16.1.4", + "expo-linear-gradient": "~14.1.5", "expo-linking": "~7.1.7", "expo-localization": "^16.1.5", "expo-location": "~18.1.5", @@ -64,6 +66,7 @@ "react-native-picker-select": "^9.3.1", "react-native-progress": "^5.0.1", "react-native-reanimated": "~3.17.4", + "react-native-reanimated-carousel": "^4.0.2", "react-native-render-html": "^6.3.4", "react-native-safe-area-context": "5.4.0", "react-native-screens": "~4.11.1", @@ -72,9 +75,7 @@ "react-native-uuid": "^2.0.3", "react-native-web": "~0.20.0", "react-native-webview": "13.13.5", - "react-redux": "^9.2.0", - "expo-clipboard": "~7.1.5", - "expo-linear-gradient": "~14.1.5" + "react-redux": "^9.2.0" }, "devDependencies": { "@babel/core": "^7.25.2", diff --git a/types/user.ts b/types/user.ts index ff1665d..bb3a291 100644 --- a/types/user.ts +++ b/types/user.ts @@ -12,7 +12,7 @@ export interface User { avatar_file_url?: string } -interface UserCountData { +export interface UserCountData { video_count: number, photo_count: number, live_count: number, @@ -31,7 +31,7 @@ export interface CountData { } } -interface Counter { +export interface Counter { user_id: number, total_count: UserCountData, category_count: {