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()
+}