From 8a6d29f5d03b16cd70a1c6ebcc7e5a09bd1bf852 Mon Sep 17 00:00:00 2001 From: ZhangAo Date: Fri, 7 Nov 2025 17:29:49 +0800 Subject: [PATCH] u --- code/base-project/base-app/package.json | 2 + code/base-project/base-app/pnpm-lock.yaml | 40 ++++ .../base-app/src/app/(tabs)/_layout.tsx | 16 +- .../base-app/src/app/(tabs)/money.tsx | 52 ++++- .../base-app/src/app/(tabs)/user.tsx | 80 ++++++- .../base-project/base-app/src/app/_layout.tsx | 10 +- .../base-app/src/app/auth/_layout.tsx | 1 - .../base-app/src/app/auth/login.tsx | 59 ++++-- .../src/components/common/ao/Input.tsx | 21 +- .../src/components/custom/StepLayout.tsx | 11 +- .../src/components/custom/Tabbar2.tsx | 199 ++++++++++++++++++ .../base-app/src/components/custom/tabbar.tsx | 30 ++- .../base-app/src/request/api/auth.ts | 11 +- .../base-app/src/types/global.d.ts | 1 + .../base-go-v2/internal/api/auth/index.go | 11 +- .../base-go-v2/internal/utils/timex/index.go | 39 ++++ 16 files changed, 514 insertions(+), 69 deletions(-) create mode 100644 code/base-project/base-app/src/components/custom/Tabbar2.tsx create mode 100644 code/base-project/base-go-v2/internal/utils/timex/index.go diff --git a/code/base-project/base-app/package.json b/code/base-project/base-app/package.json index a5d493f7..02f62c2b 100644 --- a/code/base-project/base-app/package.json +++ b/code/base-project/base-app/package.json @@ -19,8 +19,10 @@ "alova": "^3.3.4", "axios": "^1.13.2", "expo": "~54.0.20", + "expo-blur": "~15.0.7", "expo-constants": "~18.0.10", "expo-font": "~14.0.9", + "expo-linear-gradient": "~15.0.7", "expo-linking": "~8.0.8", "expo-router": "~6.0.14", "expo-splash-screen": "~31.0.10", diff --git a/code/base-project/base-app/pnpm-lock.yaml b/code/base-project/base-app/pnpm-lock.yaml index 1b98d64a..fba97d33 100644 --- a/code/base-project/base-app/pnpm-lock.yaml +++ b/code/base-project/base-app/pnpm-lock.yaml @@ -35,12 +35,18 @@ importers: expo: specifier: ~54.0.20 version: 54.0.21(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.14)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + expo-blur: + specifier: ~15.0.7 + version: 15.0.7(expo@54.0.21)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) expo-constants: specifier: ~18.0.10 version: 18.0.10(expo@54.0.21)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0)) expo-font: specifier: ~14.0.9 version: 14.0.9(expo@54.0.21)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + expo-linear-gradient: + specifier: ~15.0.7 + version: 15.0.7(expo@54.0.21)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) expo-linking: specifier: ~8.0.8 version: 8.0.8(expo@54.0.21)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) @@ -3012,6 +3018,13 @@ packages: react: '*' react-native: '*' + expo-blur@15.0.7: + resolution: {integrity: sha512-SugQQbQd+zRPy8z2G5qDD4NqhcD7srBF7fN7O7yq6q7ZFK59VWvpDxtMoUkmSfdxgqONsrBN/rLdk00USADrMg==} + peerDependencies: + expo: '*' + react: '*' + react-native: '*' + expo-constants@18.0.10: resolution: {integrity: sha512-Rhtv+X974k0Cahmvx6p7ER5+pNhBC0XbP1lRviL2J1Xl4sT2FBaIuIxF/0I0CbhOsySf0ksqc5caFweAy9Ewiw==} peerDependencies: @@ -3037,6 +3050,13 @@ packages: expo: '*' react: '*' + expo-linear-gradient@15.0.7: + resolution: {integrity: sha512-yF+y+9Shpr/OQFfy/wglB/0bykFMbwHBTuMRa5Of/r2P1wbkcacx8rg0JsUWkXH/rn2i2iWdubyqlxSJa3ggZA==} + peerDependencies: + expo: '*' + react: '*' + react-native: '*' + expo-linking@8.0.8: resolution: {integrity: sha512-MyeMcbFDKhXh4sDD1EHwd0uxFQNAc6VCrwBkNvvvufUsTYFq3glTA9Y8a+x78CPpjNqwNAamu74yIaIz7IEJyg==} peerDependencies: @@ -3670,48 +3690,56 @@ packages: engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] + libc: [glibc] lightningcss-linux-arm64-gnu@1.30.2: resolution: {integrity: sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] + libc: [glibc] lightningcss-linux-arm64-musl@1.27.0: resolution: {integrity: sha512-rCGBm2ax7kQ9pBSeITfCW9XSVF69VX+fm5DIpvDZQl4NnQoMQyRwhZQm9pd59m8leZ1IesRqWk2v/DntMo26lg==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] + libc: [musl] lightningcss-linux-arm64-musl@1.30.2: resolution: {integrity: sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] + libc: [musl] lightningcss-linux-x64-gnu@1.27.0: resolution: {integrity: sha512-Dk/jovSI7qqhJDiUibvaikNKI2x6kWPN79AQiD/E/KeQWMjdGe9kw51RAgoWFDi0coP4jinaH14Nrt/J8z3U4A==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] + libc: [glibc] lightningcss-linux-x64-gnu@1.30.2: resolution: {integrity: sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] + libc: [glibc] lightningcss-linux-x64-musl@1.27.0: resolution: {integrity: sha512-QKjTxXm8A9s6v9Tg3Fk0gscCQA1t/HMoF7Woy1u68wCk5kS4fR+q3vXa1p3++REW784cRAtkYKrPy6JKibrEZA==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] + libc: [musl] lightningcss-linux-x64-musl@1.30.2: resolution: {integrity: sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] + libc: [musl] lightningcss-win32-arm64-msvc@1.27.0: resolution: {integrity: sha512-/wXegPS1hnhkeG4OXQKEMQeJd48RDC3qdh+OA8pCuOPCyvnm/yEayrJdJVqzBsqpy1aJklRCVxscpFur80o6iQ==} @@ -9246,6 +9274,12 @@ snapshots: transitivePeerDependencies: - supports-color + expo-blur@15.0.7(expo@54.0.21)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0): + dependencies: + expo: 54.0.21(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.14)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + react: 19.1.0 + react-native: 0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0) + expo-constants@18.0.10(expo@54.0.21)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0)): dependencies: '@expo/config': 12.0.10 @@ -9272,6 +9306,12 @@ snapshots: expo: 54.0.21(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.14)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) react: 19.1.0 + expo-linear-gradient@15.0.7(expo@54.0.21)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0): + dependencies: + expo: 54.0.21(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.14)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + react: 19.1.0 + react-native: 0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0) + expo-linking@8.0.8(expo@54.0.21)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0): dependencies: expo-constants: 18.0.10(expo@54.0.21)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0)) diff --git a/code/base-project/base-app/src/app/(tabs)/_layout.tsx b/code/base-project/base-app/src/app/(tabs)/_layout.tsx index bafd2e05..634be18f 100644 --- a/code/base-project/base-app/src/app/(tabs)/_layout.tsx +++ b/code/base-project/base-app/src/app/(tabs)/_layout.tsx @@ -2,19 +2,22 @@ import type { TabbarItem } from '@/components/custom/Tabbar' import CustomTabbar from '@/components/custom/Tabbar' import { toast } from '@/utils/toast' +export const unstable_settings = { + initialRouteName: 'money', +} + export default function TabsLayout() { const tabbarOptions: TabbarItem[] = [ - { - name: 'user', - href: '/user', - icon: 'CircleCheckBig', - }, - { name: 'money', href: '/money', icon: 'CircleDollarSign', }, + { + name: 'user', + href: '/user', + icon: 'CircleCheckBig', + }, { name: 'app', icon: 'LayoutGrid', @@ -37,6 +40,7 @@ export default function TabsLayout() { icon: 'Heart', }, ] + return ( ) diff --git a/code/base-project/base-app/src/app/(tabs)/money.tsx b/code/base-project/base-app/src/app/(tabs)/money.tsx index c74a72fd..10f87ba7 100644 --- a/code/base-project/base-app/src/app/(tabs)/money.tsx +++ b/code/base-project/base-app/src/app/(tabs)/money.tsx @@ -1,9 +1,53 @@ -import { Text, View } from 'react-native' +import { LinearGradient } from 'expo-linear-gradient' +import { useState } from 'react' +import { Pressable, Text, View } from 'react-native' +import { useSafeAreaInsets } from 'react-native-safe-area-context' export default function Money() { + const insets = useSafeAreaInsets() + const [active, setActive] = useState<'明细' | '视图'>('明细') + return ( - - Money - + + + {['明细', '视图'].map((item) => { + const isActive = active === item + return ( + setActive(item as '明细' | '视图')} + style={{ alignItems: 'center', paddingVertical: 12 }} + > + + {item} + + {/* 底部指示线 */} + {isActive && ( + + )} + + ) + })} + + ) } diff --git a/code/base-project/base-app/src/app/(tabs)/user.tsx b/code/base-project/base-app/src/app/(tabs)/user.tsx index 042ec7bd..62855fad 100644 --- a/code/base-project/base-app/src/app/(tabs)/user.tsx +++ b/code/base-project/base-app/src/app/(tabs)/user.tsx @@ -1,9 +1,83 @@ -import { Text, View } from 'react-native' +import { router } from 'expo-router' +import { StyleSheet, Text, View } from 'react-native' +import { AButton } from '@/components/common/ao' +import { useUserStore } from '@/store/user' export default function User() { + const userInfo = useUserStore(state => state.userInfo) + + if (!userInfo) { + return ( + + 未登录 + + ) + } + + const genderText = userInfo.gender === 1 ? '男' : userInfo.gender === 2 ? '女' : '未设置' + return ( - - User + + 个人信息 + + + 账号: + {userInfo.account} + + + 用户名: + {userInfo.username} + + + ID: + {userInfo.id} + + + 序号: + {userInfo.seqNo} + + + 性别: + {genderText} + + {userInfo.rememberMe && 已开启记住我功能} + + + { + router.replace('/auth') + }} + > + 跳转到登陆页面 + ) } + +const styles = StyleSheet.create({ + container: { + flex: 1, + backgroundColor: '#fff', + padding: 20, + }, + title: { + fontSize: 20, + fontWeight: 'bold', + marginBottom: 16, + }, + infoBox: { + backgroundColor: '#f8f8f8', + borderRadius: 12, + padding: 16, + }, + item: { + fontSize: 16, + marginBottom: 8, + color: '#333', + }, + empty: { + textAlign: 'center', + marginTop: 100, + fontSize: 18, + color: '#888', + }, +}) diff --git a/code/base-project/base-app/src/app/_layout.tsx b/code/base-project/base-app/src/app/_layout.tsx index d1c0789f..d927b3da 100644 --- a/code/base-project/base-app/src/app/_layout.tsx +++ b/code/base-project/base-app/src/app/_layout.tsx @@ -59,8 +59,8 @@ export default function RootLayout() { screenOptions={{ headerTitleAlign: 'center', animation: 'slide_from_right', - gestureEnabled: true, // 启用手势返回 - gestureDirection: 'horizontal', // 水平手势 + gestureEnabled: true, + gestureDirection: 'horizontal', contentStyle: { backgroundColor: '#fff', }, @@ -68,14 +68,18 @@ export default function RootLayout() { > + + diff --git a/code/base-project/base-app/src/app/auth/_layout.tsx b/code/base-project/base-app/src/app/auth/_layout.tsx index 212f2238..9fd52344 100644 --- a/code/base-project/base-app/src/app/auth/_layout.tsx +++ b/code/base-project/base-app/src/app/auth/_layout.tsx @@ -25,6 +25,5 @@ export default function AuthLayout() { - ) } diff --git a/code/base-project/base-app/src/app/auth/login.tsx b/code/base-project/base-app/src/app/auth/login.tsx index a034b58e..2d288fb2 100644 --- a/code/base-project/base-app/src/app/auth/login.tsx +++ b/code/base-project/base-app/src/app/auth/login.tsx @@ -6,10 +6,11 @@ import { IconQq, IconWeixin, IconZhiwen } from '@/components/iconfont' import { Divider } from '@/components/ui/divider' import useFormValidation from '@/hooks/useFormValidation' import { loginDefaultApi } from '@/request/api/auth' +import { useUserStore } from '@/store/user' import { toast } from '@/utils/toast' interface LoginForm { - username: string + account: string password: string rememberMe: boolean socialType?: 'qq' | 'weixin' | 'fingerprint' @@ -17,10 +18,11 @@ interface LoginForm { export default function AuthLogin() { const router = useRouter() + const userStore = useUserStore() const [loading, setLoading] = useState(false) const [form, setForm] = useState({ - username: '', + account: userStore.userInfo?.account || '', password: '', rememberMe: true, }) @@ -30,7 +32,7 @@ export default function AuthLogin() { } const rules = { - username: { required: true, message: '账号是必填项' }, + account: { required: true, message: '账号是必填项' }, password: { required: true, message: '密码是必填项' }, } @@ -38,23 +40,38 @@ export default function AuthLogin() { const loginDefault = () => { setLoading(true) - // loginDefaultApi({ - // account: form.username, - // password: form.password, - // }).then((res) => { - // if (res.isSuccess) { - // router.replace('/') - // } - // else { - // toast.show({ - // title: res.message || '登录失败', - // }) - // } - // }).catch((_err) => { + loginDefaultApi({ + account: form.account, + password: form.password, + }).then((res) => { + if (res.isSuccess) { + toast.show({ + title: '登录成功', + }) - // }).finally(() => { - // setLoading(false) - // }) + userStore.setUserInfo({ + account: form.account, + id: res.data.userId, + seqNo: res.data.seqNo, + username: res.data.username, + gender: res.data.gender, + token: res.data.token, + refreshToken: res.data.refreshToken, + rememberMe: form.rememberMe, + }) + + router.replace('/user') + } + else { + toast.show({ + title: res.message || '登录失败', + }) + } + }).catch((_err) => { + + }).finally(() => { + setLoading(false) + }) } const loginQq = () => { @@ -78,8 +95,8 @@ export default function AuthLogin() { handleInputChange('username', text)} + value={form.account} + onChange={text => handleInputChange('account', text)} /> diff --git a/code/base-project/base-app/src/components/common/ao/Input.tsx b/code/base-project/base-app/src/components/common/ao/Input.tsx index f5fb2ae4..a36de902 100644 --- a/code/base-project/base-app/src/components/common/ao/Input.tsx +++ b/code/base-project/base-app/src/components/common/ao/Input.tsx @@ -1,6 +1,7 @@ import type { ComponentType } from 'react' import type { StyleProp, ViewStyle } from 'react-native' import { useState } from 'react' +import { Pressable } from 'react-native' import { EyeIcon, EyeOffIcon } from '@/components/ui/icon' import { InputField, InputIcon, InputSlot, Input as NativeInput } from '@/components/ui/input' @@ -52,23 +53,23 @@ export default function Input({ className={className} style={style} > - {leftIcon - ? ( - - - - ) - : null} + {leftIcon && ( + + + + )} {isPassword && ( - setShowPassword(prev => !prev)}> - + + setShowPassword(prev => !prev)}> + + )} diff --git a/code/base-project/base-app/src/components/custom/StepLayout.tsx b/code/base-project/base-app/src/components/custom/StepLayout.tsx index bc94f898..763e0565 100644 --- a/code/base-project/base-app/src/components/custom/StepLayout.tsx +++ b/code/base-project/base-app/src/components/custom/StepLayout.tsx @@ -1,5 +1,6 @@ import { Ionicons } from '@expo/vector-icons' import { usePathname, useRouter } from 'expo-router' +import { useRef } from 'react' import { Image, Pressable, Text, View } from 'react-native' import Animated, { SlideInLeft, SlideInRight } from 'react-native-reanimated' import { SafeAreaView } from 'react-native-safe-area-context' @@ -31,7 +32,15 @@ export default function StepLayout({ steps, children, showHearder = true }: Step // 是否显示header const headerVisible = showHearder !== false && currentStep.showHearder !== false - const enteringAnimation = canGoBack ? SlideInRight.duration(300) : SlideInLeft.duration(300) + const hasMounted = useRef(false) + + const enteringAnimation = hasMounted.current + ? canGoBack + ? SlideInRight.duration(300) + : SlideInLeft.duration(300) + : undefined + + hasMounted.current = true return ( diff --git a/code/base-project/base-app/src/components/custom/Tabbar2.tsx b/code/base-project/base-app/src/components/custom/Tabbar2.tsx new file mode 100644 index 00000000..62575ca6 --- /dev/null +++ b/code/base-project/base-app/src/components/custom/Tabbar2.tsx @@ -0,0 +1,199 @@ +import { usePathname, useRouter } from 'expo-router' +import { TabList, Tabs, TabSlot, TabTrigger } from 'expo-router/ui' +import { icons } from 'lucide-react-native' +import { useEffect, useState } from 'react' +import { Pressable, View } from 'react-native' +import { Gesture, GestureDetector, GestureHandlerRootView } from 'react-native-gesture-handler' +import Animated, { useAnimatedStyle, useSharedValue, withSequence, withTiming } from 'react-native-reanimated' +import { scheduleOnRN } from 'react-native-worklets' + +interface AppTabbarProps { + data: TabbarItem[] +} + +export interface TabbarItem { + icon: keyof typeof icons + iconSize?: number + activeIcon?: keyof typeof icons + name: string + href?: string + onPress?: () => void +} + +function TabButton({ + icon, + activeIcon, + iconSize = 24, + isFocused = false, + onPress, +}: { + icon: keyof typeof icons + activeIcon?: keyof typeof icons + iconSize?: number + isFocused?: boolean + onPress?: () => void +}) { + const scale = useSharedValue(1) + + const animatedStyle = useAnimatedStyle(() => ({ + transform: [{ scale: scale.value }], + })) + + useEffect(() => { + if (isFocused) { + scale.value = withSequence( + withTiming(0.9, { duration: 120 }), + withTiming(1, { duration: 160 }), + ) + } + }, [isFocused]) + + const IconComponent = icons[isFocused && activeIcon ? activeIcon : icon] + + return ( + + + + + + ) +} + +export default function CustomTabbar({ data }: AppTabbarProps) { + const router = useRouter() + const pathname = usePathname() + + const [activeTab, setActiveTab] = useState(() => { + const match = data.find(item => item.href === pathname) + return match?.name ?? data[0]?.name + }) + + useEffect(() => { + console.log(`pathname:${pathname}`) + }, [pathname]) + + // 反转方向 + const [swipeDirection, setSwipeDirection] = useState<'left' | 'right'>('left') + function updateSwipeDirection(tabName: string) { + const currentIndex = data.findIndex(d => d.name === activeTab) + const targetIndex = data.findIndex(d => d.name === tabName) + + if (currentIndex === -1 || targetIndex === -1 || currentIndex === targetIndex) + return + + setSwipeDirection(targetIndex > currentIndex ? 'left' : 'right') + setActiveTab(tabName) + } + + // 点击调用 + const handlPressTab = (item: TabbarItem) => { + updateSwipeDirection(item.name) + + setActiveTab(item.name) + if (typeof item.onPress === 'function') + item.onPress() + } + + // 定义 JS 线程安全函数 + const handlePanTab = (tabName: string) => { + const tab = data.find(item => item.name === tabName) + if (!tab) + return + + updateSwipeDirection(tabName) + + setActiveTab(tab.name) + + if (typeof tab.onPress === 'function') { + tab.onPress() + } + + if (tab.href && tab.href !== pathname) { + router.push(tab.href) + } + } + + // 手势滑动 + const panGesture = Gesture.Pan() + .activeOffsetX([-20, 20]) + .failOffsetY([-10, 10]) + .onEnd((event) => { + const threshold = 60 + const currentIndex = data.findIndex(item => item.name === activeTab) + let targetIndex: number | undefined + + if (event.translationX < -threshold && currentIndex < data.length - 1) { + targetIndex = currentIndex + 1 + } + else if (event.translationX > threshold && currentIndex > 0) { + targetIndex = currentIndex - 1 + } + + if (targetIndex !== undefined) { + const tabName = data[targetIndex].name + scheduleOnRN(handlePanTab, tabName) + } + }) + + return ( + + + + + {(() => { + const currentTab = data.find(d => d.name === activeTab) + if (!currentTab) + return null + + return ( + + + + ) + })()} + + + + + + {data.map((item) => { + if (item.href) { + return ( + + handlPressTab(item)} + /> + + ) + } + + return ( + handlPressTab(item)} + /> + ) + })} + + + + + ) +} diff --git a/code/base-project/base-app/src/components/custom/tabbar.tsx b/code/base-project/base-app/src/components/custom/tabbar.tsx index e189fe36..317176d1 100644 --- a/code/base-project/base-app/src/components/custom/tabbar.tsx +++ b/code/base-project/base-app/src/components/custom/tabbar.tsx @@ -1,10 +1,10 @@ -import { usePathname, useRouter } from 'expo-router' +import { Redirect, usePathname, useRouter, useSegments } from 'expo-router' import { TabList, Tabs, TabSlot, TabTrigger } from 'expo-router/ui' import { icons } from 'lucide-react-native' import { useEffect, useState } from 'react' import { Pressable, View } from 'react-native' import { Gesture, GestureDetector, GestureHandlerRootView } from 'react-native-gesture-handler' -import Animated, { SlideInLeft, SlideInRight, useAnimatedStyle, useSharedValue, withSequence, withTiming } from 'react-native-reanimated' +import Animated, { useAnimatedStyle, useSharedValue, withSequence, withTiming } from 'react-native-reanimated' import { scheduleOnRN } from 'react-native-worklets' interface AppTabbarProps { @@ -141,29 +141,25 @@ export default function CustomTabbar({ data }: AppTabbarProps) { {(() => { const currentTab = data.find(d => d.name === activeTab) - if (!currentTab) return null - return currentTab.href - ? ( - - - - ) - : ( - - ) + return ( + + + + ) })()} - + {data.map((item) => { if (item.href) { return ( diff --git a/code/base-project/base-app/src/request/api/auth.ts b/code/base-project/base-app/src/request/api/auth.ts index 92ac44ea..b4a777c1 100644 --- a/code/base-project/base-app/src/request/api/auth.ts +++ b/code/base-project/base-app/src/request/api/auth.ts @@ -15,7 +15,14 @@ interface LoginDefaultParams { account: string password: string } - +interface LoginDefaultResult { + token: string + refreshToken: string + userId: string + username: string + seqNo: number + gender: number +} export function loginDefaultApi(params: LoginDefaultParams) { - return alovaInstance.Post>(`${API_PREFIX}/login/default`, params) + return alovaInstance.Post>(`${API_PREFIX}/login/default`, params) } diff --git a/code/base-project/base-app/src/types/global.d.ts b/code/base-project/base-app/src/types/global.d.ts index 36ca2dcc..f6f3db87 100644 --- a/code/base-project/base-app/src/types/global.d.ts +++ b/code/base-project/base-app/src/types/global.d.ts @@ -17,5 +17,6 @@ declare global { gender: number token?: string refreshToken?: string + rememberMe?: boolean } } diff --git a/code/base-project/base-go-v2/internal/api/auth/index.go b/code/base-project/base-go-v2/internal/api/auth/index.go index 65e65dcb..5cc7188b 100644 --- a/code/base-project/base-go-v2/internal/api/auth/index.go +++ b/code/base-project/base-go-v2/internal/api/auth/index.go @@ -4,8 +4,10 @@ import ( "base-go-v2/internal/db" response "base-go-v2/internal/reponse" "base-go-v2/internal/router" + "base-go-v2/internal/routinex" "base-go-v2/internal/utils/jwtx" "base-go-v2/internal/utils/mapx" + "base-go-v2/internal/utils/timex" "base-go-v2/internal/utils/uid" ) @@ -102,10 +104,17 @@ func loginDefault(c *router.Context) error { "username", user.GetString("username"), "seqNo", user.GetInt("seq_no"), "gender", user.GetInt("gender"), - "accessToken", accessToken, + "token", accessToken, "refreshToken", refreshToken, ) + routinex.Go(func() { + kv := mapx.New().SetKV( + "last_login_time", timex.NowString(), + ) + _, _ = db.UpdateOne("user_info", "id", userId, kv) + }) + return response.Success(c). Data(respData). Send() diff --git a/code/base-project/base-go-v2/internal/utils/timex/index.go b/code/base-project/base-go-v2/internal/utils/timex/index.go new file mode 100644 index 00000000..d7d547b2 --- /dev/null +++ b/code/base-project/base-go-v2/internal/utils/timex/index.go @@ -0,0 +1,39 @@ +package timex + +import "time" + +// NowTime 返回当前时间对象(本地时区) +func NowTime() time.Time { + return time.Now() +} + +// NowString 返回当前时间的字符串格式:YYYY-MM-DD HH:mm:ss +func NowString() string { + return time.Now().Format("2006-01-02 15:04:05") +} + +// DateString 返回当前日期(年月日):YYYY-MM-DD +func DateString() string { + return time.Now().Format("2006-01-02") +} + +// TimeString 返回当前时间(时分秒):HH:mm:ss +func TimeString() string { + return time.Now().Format("15:04:05") +} + +// CustomFormat 返回指定格式的当前时间 +// 例如:timeutil.CustomFormat("2006年01月02日 15时04分05秒") +func CustomFormat(format string) string { + return time.Now().Format(format) +} + +// NowUnix 返回当前时间戳(秒) +func NowUnix() int64 { + return time.Now().Unix() +} + +// NowUnixMilli 返回当前时间戳(毫秒) +func NowUnixMilli() int64 { + return time.Now().UnixMilli() +}