feat: 轮播图
This commit is contained in:
parent
20c1b2b767
commit
80eaad039e
@ -5,21 +5,19 @@ import StoriesSvg from '@/assets/icons/svg/stories.svg';
|
|||||||
import UsedStorageSvg from '@/assets/icons/svg/usedStorage.svg';
|
import UsedStorageSvg from '@/assets/icons/svg/usedStorage.svg';
|
||||||
import AskNavbar from '@/components/layout/ask';
|
import AskNavbar from '@/components/layout/ask';
|
||||||
import AlbumComponent from '@/components/owner/album';
|
import AlbumComponent from '@/components/owner/album';
|
||||||
import CategoryComponent from '@/components/owner/category';
|
import CarouselComponent from '@/components/owner/carousel';
|
||||||
import CountComponent from '@/components/owner/count';
|
|
||||||
import CreateCountComponent from '@/components/owner/createCount';
|
import CreateCountComponent from '@/components/owner/createCount';
|
||||||
import Ranking from '@/components/owner/ranking';
|
import Ranking from '@/components/owner/ranking';
|
||||||
import ResourceComponent from '@/components/owner/resource';
|
import ResourceComponent from '@/components/owner/resource';
|
||||||
import SettingModal from '@/components/owner/setting';
|
import SettingModal from '@/components/owner/setting';
|
||||||
import UserInfo from '@/components/owner/userName';
|
import UserInfo from '@/components/owner/userName';
|
||||||
import { formatDuration } from '@/components/utils/time';
|
|
||||||
import { checkAuthStatus } from '@/lib/auth';
|
import { checkAuthStatus } from '@/lib/auth';
|
||||||
import { fetchApi } from '@/lib/server-api-util';
|
import { fetchApi } from '@/lib/server-api-util';
|
||||||
import { CountData, UserInfoDetails } from '@/types/user';
|
import { CountData, UserInfoDetails } from '@/types/user';
|
||||||
import { useRouter } from 'expo-router';
|
import { useRouter } from 'expo-router';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
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";
|
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
||||||
|
|
||||||
export default function OwnerPage() {
|
export default function OwnerPage() {
|
||||||
@ -97,27 +95,8 @@ export default function OwnerPage() {
|
|||||||
<MoreArrowSvg />
|
<MoreArrowSvg />
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
{/* 数据统计 */}
|
|
||||||
<CountComponent
|
|
||||||
data={[{ title: t("generalSetting.totalVideo", { ns: "personal" }), number: countData?.counter?.total_count?.video_count || 0 }, { title: t("generalSetting.totalPhoto", { ns: "personal" }), number: countData?.counter?.total_count?.photo_count || 0 }, { title: t("generalSetting.live", { ns: "personal" }), number: countData?.counter?.total_count?.live_count || 0 }, { title: t("generalSetting.videoLength", { ns: "personal" }), number: formatDuration(countData?.counter?.total_count?.video_length || 0) }]}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{/* 分类 */}
|
{/* 分类 */}
|
||||||
<View style={{ height: 145 }}>
|
<CarouselComponent data={userInfoDetails?.material_counter} />
|
||||||
<ScrollView horizontal showsHorizontalScrollIndicator={false} contentContainerStyle={{ gap: 16 }} >
|
|
||||||
{countData?.counter?.category_count && Object.entries(countData?.counter?.category_count).map(([key, value], index) => {
|
|
||||||
return (
|
|
||||||
<CategoryComponent
|
|
||||||
key={index}
|
|
||||||
title={key}
|
|
||||||
data={[{ title: 'Video', number: value.video_count }, { title: 'Photo', number: value.photo_count }, { title: 'Length', number: formatDuration(value.video_length || 0) }]}
|
|
||||||
bgSvg={value.cover_url}
|
|
||||||
style={{ aspectRatio: 1, flex: 1 }}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
</ScrollView>
|
|
||||||
</View>
|
|
||||||
|
|
||||||
{/* 作品数据 */}
|
{/* 作品数据 */}
|
||||||
<View className='flex flex-row justify-between gap-[1rem]'>
|
<View className='flex flex-row justify-between gap-[1rem]'>
|
||||||
|
|||||||
127
components/owner/carousel.tsx
Normal file
127
components/owner/carousel.tsx
Normal file
@ -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<ICarouselInstance>(null);
|
||||||
|
const progress = useSharedValue<number>(0);
|
||||||
|
const [carouselDataValue, setCarouselDataValue] = React.useState<CarouselData[]>([]);
|
||||||
|
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 <View style={[styles.container]}>
|
||||||
|
{Object?.entries(data)?.filter(([key]) => key !== 'cover_url')?.map((item, index) => (
|
||||||
|
<View style={styles.item} key={index}>
|
||||||
|
<ThemedText style={styles.title}>{item[0]}</ThemedText>
|
||||||
|
<ThemedText style={styles.number}>{item[1]}</ThemedText>
|
||||||
|
</View>
|
||||||
|
))}
|
||||||
|
</View>
|
||||||
|
}
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (data) {
|
||||||
|
dataHandle()
|
||||||
|
}
|
||||||
|
}, [data]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
|
||||||
|
<Carousel
|
||||||
|
ref={ref}
|
||||||
|
width={width * 0.8}
|
||||||
|
height={width * 0.8}
|
||||||
|
data={carouselDataValue || []}
|
||||||
|
mode="parallax"
|
||||||
|
onProgressChange={progress}
|
||||||
|
defaultIndex={carouselDataValue?.findIndex((item) => 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,
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
@ -14,8 +14,11 @@ const CategoryComponent = ({ title, data, bgSvg, style }: CategoryProps) => {
|
|||||||
<View style={styles.backgroundContainer}>
|
<View style={styles.backgroundContainer}>
|
||||||
<Image
|
<Image
|
||||||
source={bgSvg !== "" && bgSvg !== null ? { uri: bgSvg } : require('@/assets/images/png/owner/animals.png')}
|
source={bgSvg !== "" && bgSvg !== null ? { uri: bgSvg } : require('@/assets/images/png/owner/animals.png')}
|
||||||
style={{ width: '100%', height: '100%' }}
|
style={{
|
||||||
resizeMode="cover"
|
width: "100%",
|
||||||
|
height: "100%",
|
||||||
|
resizeMode: "cover"
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
<View style={styles.overlay} />
|
<View style={styles.overlay} />
|
||||||
</View>
|
</View>
|
||||||
@ -37,11 +40,12 @@ const styles = StyleSheet.create({
|
|||||||
borderRadius: 32,
|
borderRadius: 32,
|
||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
|
aspectRatio: 1,
|
||||||
},
|
},
|
||||||
backgroundContainer: {
|
backgroundContainer: {
|
||||||
...StyleSheet.absoluteFillObject,
|
...StyleSheet.absoluteFillObject,
|
||||||
width: '100%',
|
width: "100%",
|
||||||
height: '100%',
|
height: "100%",
|
||||||
},
|
},
|
||||||
overlay: {
|
overlay: {
|
||||||
...StyleSheet.absoluteFillObject,
|
...StyleSheet.absoluteFillObject,
|
||||||
|
|||||||
13
package-lock.json
generated
13
package-lock.json
generated
@ -60,6 +60,7 @@
|
|||||||
"react-native-picker-select": "^9.3.1",
|
"react-native-picker-select": "^9.3.1",
|
||||||
"react-native-progress": "^5.0.1",
|
"react-native-progress": "^5.0.1",
|
||||||
"react-native-reanimated": "~3.17.4",
|
"react-native-reanimated": "~3.17.4",
|
||||||
|
"react-native-reanimated-carousel": "^4.0.2",
|
||||||
"react-native-render-html": "^6.3.4",
|
"react-native-render-html": "^6.3.4",
|
||||||
"react-native-safe-area-context": "5.4.0",
|
"react-native-safe-area-context": "5.4.0",
|
||||||
"react-native-screens": "~4.11.1",
|
"react-native-screens": "~4.11.1",
|
||||||
@ -14826,6 +14827,18 @@
|
|||||||
"react-native": "*"
|
"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": {
|
"node_modules/react-native-reanimated/node_modules/react-native-is-edge-to-edge": {
|
||||||
"version": "1.1.7",
|
"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",
|
"resolved": "https://registry.npmjs.org/react-native-is-edge-to-edge/-/react-native-is-edge-to-edge-1.1.7.tgz",
|
||||||
|
|||||||
@ -25,6 +25,7 @@
|
|||||||
"expo-audio": "~0.4.8",
|
"expo-audio": "~0.4.8",
|
||||||
"expo-background-task": "^0.2.8",
|
"expo-background-task": "^0.2.8",
|
||||||
"expo-blur": "~14.1.5",
|
"expo-blur": "~14.1.5",
|
||||||
|
"expo-clipboard": "~7.1.5",
|
||||||
"expo-constants": "~17.1.6",
|
"expo-constants": "~17.1.6",
|
||||||
"expo-dev-client": "~5.2.4",
|
"expo-dev-client": "~5.2.4",
|
||||||
"expo-device": "~7.1.4",
|
"expo-device": "~7.1.4",
|
||||||
@ -33,6 +34,7 @@
|
|||||||
"expo-haptics": "~14.1.4",
|
"expo-haptics": "~14.1.4",
|
||||||
"expo-image-manipulator": "~13.1.7",
|
"expo-image-manipulator": "~13.1.7",
|
||||||
"expo-image-picker": "~16.1.4",
|
"expo-image-picker": "~16.1.4",
|
||||||
|
"expo-linear-gradient": "~14.1.5",
|
||||||
"expo-linking": "~7.1.7",
|
"expo-linking": "~7.1.7",
|
||||||
"expo-localization": "^16.1.5",
|
"expo-localization": "^16.1.5",
|
||||||
"expo-location": "~18.1.5",
|
"expo-location": "~18.1.5",
|
||||||
@ -64,6 +66,7 @@
|
|||||||
"react-native-picker-select": "^9.3.1",
|
"react-native-picker-select": "^9.3.1",
|
||||||
"react-native-progress": "^5.0.1",
|
"react-native-progress": "^5.0.1",
|
||||||
"react-native-reanimated": "~3.17.4",
|
"react-native-reanimated": "~3.17.4",
|
||||||
|
"react-native-reanimated-carousel": "^4.0.2",
|
||||||
"react-native-render-html": "^6.3.4",
|
"react-native-render-html": "^6.3.4",
|
||||||
"react-native-safe-area-context": "5.4.0",
|
"react-native-safe-area-context": "5.4.0",
|
||||||
"react-native-screens": "~4.11.1",
|
"react-native-screens": "~4.11.1",
|
||||||
@ -72,9 +75,7 @@
|
|||||||
"react-native-uuid": "^2.0.3",
|
"react-native-uuid": "^2.0.3",
|
||||||
"react-native-web": "~0.20.0",
|
"react-native-web": "~0.20.0",
|
||||||
"react-native-webview": "13.13.5",
|
"react-native-webview": "13.13.5",
|
||||||
"react-redux": "^9.2.0",
|
"react-redux": "^9.2.0"
|
||||||
"expo-clipboard": "~7.1.5",
|
|
||||||
"expo-linear-gradient": "~14.1.5"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.25.2",
|
"@babel/core": "^7.25.2",
|
||||||
|
|||||||
@ -12,7 +12,7 @@ export interface User {
|
|||||||
avatar_file_url?: string
|
avatar_file_url?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
interface UserCountData {
|
export interface UserCountData {
|
||||||
video_count: number,
|
video_count: number,
|
||||||
photo_count: number,
|
photo_count: number,
|
||||||
live_count: number,
|
live_count: number,
|
||||||
@ -31,7 +31,7 @@ export interface CountData {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Counter {
|
export interface Counter {
|
||||||
user_id: number,
|
user_id: number,
|
||||||
total_count: UserCountData,
|
total_count: UserCountData,
|
||||||
category_count: {
|
category_count: {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user