u
This commit is contained in:
1 parent
eaba6cf17b
commit
1445ed3b6c
19 files changed
+613
-401
No files matched your search
@@ -14,9 +14,9 @@ export default function TabsLayout() {
|
||||
icon: 'CircleDollarSign',
|
||||
},
|
||||
{
|
||||
name: 'user',
|
||||
href: '/user',
|
||||
icon: 'CircleCheckBig',
|
||||
name: 'love',
|
||||
href: '/love',
|
||||
icon: 'Heart',
|
||||
},
|
||||
{
|
||||
name: 'app',
|
||||
@@ -35,9 +35,9 @@ export default function TabsLayout() {
|
||||
icon: 'MessageCircle',
|
||||
},
|
||||
{
|
||||
name: 'love',
|
||||
href: '/love',
|
||||
icon: 'Heart',
|
||||
name: 'user',
|
||||
href: '/user',
|
||||
icon: 'CircleUserRound',
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
import { Pressable, StyleSheet, Text, View } from 'react-native'
|
||||
|
||||
const Locales = ['zh-cn', 'en', 'de', 'es']
|
||||
|
||||
interface Props {
|
||||
locale: string
|
||||
setLocale: (locale: string) => void
|
||||
mainColor?: string
|
||||
activeTextColor?: string
|
||||
}
|
||||
|
||||
export default function LocaleSelector({
|
||||
locale,
|
||||
setLocale,
|
||||
mainColor,
|
||||
activeTextColor,
|
||||
}: Props) {
|
||||
return (
|
||||
<View style={styles.localeContainer}>
|
||||
<Text style={{ marginRight: 8 }}>Locale:</Text>
|
||||
{Locales.map((item, index) => (
|
||||
<Pressable
|
||||
key={index}
|
||||
style={[
|
||||
styles.localeButton,
|
||||
item === locale && {
|
||||
backgroundColor: mainColor,
|
||||
},
|
||||
]}
|
||||
onPress={() => setLocale(item)}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={item.toUpperCase()}
|
||||
>
|
||||
<Text
|
||||
style={[
|
||||
styles.localeButtonText,
|
||||
item === locale && {
|
||||
fontWeight: 'bold',
|
||||
color: activeTextColor,
|
||||
},
|
||||
]}
|
||||
>
|
||||
{item.split('-')[0]?.toUpperCase()}
|
||||
</Text>
|
||||
</Pressable>
|
||||
))}
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
localeContainer: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
marginBottom: 20,
|
||||
},
|
||||
localeButton: {
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
width: 36,
|
||||
height: 36,
|
||||
borderRadius: 36,
|
||||
margin: 2,
|
||||
},
|
||||
localeButtonText: {
|
||||
fontSize: 15,
|
||||
},
|
||||
})
|
||||
@@ -1,54 +0,0 @@
|
||||
import { Pressable, StyleSheet, View } from 'react-native'
|
||||
|
||||
export interface ITheme {
|
||||
mainColor: string
|
||||
activeTextColor: string
|
||||
}
|
||||
|
||||
interface Props {
|
||||
themes: ITheme[]
|
||||
setTheme: (theme: ITheme) => void
|
||||
}
|
||||
|
||||
export default function ThemeSelector({ themes = [], setTheme }: Props) {
|
||||
return (
|
||||
<View style={styles.themeContainer}>
|
||||
{themes.map((item, index) => (
|
||||
<Pressable
|
||||
key={index}
|
||||
style={[
|
||||
styles.themeButton,
|
||||
{
|
||||
borderColor: item.activeTextColor,
|
||||
backgroundColor: item.mainColor,
|
||||
},
|
||||
]}
|
||||
onPress={() => setTheme(item)}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel="Set Active Theme"
|
||||
/>
|
||||
))}
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
themeContainer: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-around',
|
||||
marginBottom: 10,
|
||||
width: 330,
|
||||
},
|
||||
themeButton: {
|
||||
borderWidth: 4,
|
||||
width: 32,
|
||||
height: 32,
|
||||
borderRadius: 32,
|
||||
margin: 5,
|
||||
shadowRadius: 20,
|
||||
shadowColor: '#000',
|
||||
shadowOpacity: 0.1,
|
||||
shadowOffset: { width: 0, height: 0 },
|
||||
},
|
||||
})
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { MoneyRecordGroup, MoneyRecordItem } from '@/types/money/record'
|
||||
import React, { useCallback, useState } from 'react'
|
||||
import { Pressable, SectionList, Text, View } from 'react-native'
|
||||
import React, { useState } from 'react'
|
||||
import { SectionList, Text, TouchableHighlight, View } from 'react-native'
|
||||
import {
|
||||
Menu,
|
||||
MenuOption,
|
||||
@@ -50,7 +50,7 @@ export default function StaticTransactions({
|
||||
stickySectionHeadersEnabled={true}
|
||||
showsVerticalScrollIndicator={false}
|
||||
renderSectionHeader={({ section }) => (
|
||||
<View className="bg-white py-2 px-4 border-b border-gray-100 mb-2">
|
||||
<View className="bg-white py-2 px-4 border-b border-gray-50">
|
||||
<View className="flex-row items-center justify-between">
|
||||
<Text className="text-gray-700 font-semibold text-base">
|
||||
{section.title}
|
||||
@@ -80,7 +80,7 @@ export default function StaticTransactions({
|
||||
</View>
|
||||
)}
|
||||
|
||||
renderItem={({ item }) => {
|
||||
renderItem={({ item, index }) => {
|
||||
return (
|
||||
<Menu
|
||||
renderer={renderers.Popover}
|
||||
@@ -89,36 +89,47 @@ export default function StaticTransactions({
|
||||
preferredPlacement: 'bottom',
|
||||
anchorStyle: { backgroundColor: 'transparent' },
|
||||
}}
|
||||
onSelect={() => setSelectedId(null)}
|
||||
onBackdropPress={() => setSelectedId(null)}
|
||||
onOpen={() => setSelectedId(item.id)}
|
||||
onClose={() => setSelectedId(null)}
|
||||
>
|
||||
<MenuTrigger>
|
||||
<MenuTrigger triggerOnLongPress>
|
||||
<View
|
||||
className="flex-row items-center gap-4 py-2 px-5"
|
||||
style={{
|
||||
backgroundColor: item.id === selectedId ? '#f2f2f2' : 'transparent',
|
||||
}}
|
||||
>
|
||||
<View className="w-10 h-10 rounded-full bg-gray-100 items-center justify-center">
|
||||
<MoneyCategoryIcon name={item.category_icon} size={22} color="#000" />
|
||||
<View className="relative w-10 h-10 rounded-full bg-gray-100 items-center justify-center">
|
||||
<MoneyCategoryIcon name={item.category_icon} size={20} color="#000" />
|
||||
</View>
|
||||
|
||||
<View className="flex-1">
|
||||
<Text className="text-gray-800 font-medium text-base">{item.category_name}</Text>
|
||||
{item.remark && <Text className="text-gray-400 text-sm">{item.remark}</Text>}
|
||||
<Text className="text-gray-800 text-base">
|
||||
{item.remark ? item.remark : item.category_name}
|
||||
</Text>
|
||||
{
|
||||
index === 1 && (
|
||||
<Text className="text-gray-500 text-xs italic">
|
||||
操作人:张三
|
||||
</Text>
|
||||
)
|
||||
}
|
||||
|
||||
</View>
|
||||
|
||||
<View className="flex-row flex">
|
||||
<Text className="text-sm text-gray-500">
|
||||
<View>
|
||||
<Text
|
||||
className="text-sm text-gray-600 text-right"
|
||||
>
|
||||
{item.category_type === 1 ? '+' : '-'}
|
||||
{' '}
|
||||
{item.amount}
|
||||
</Text>
|
||||
</View>
|
||||
<Badge size="sm" variant="solid" action="muted">
|
||||
<BadgeText>宝贝</BadgeText>
|
||||
</Badge>
|
||||
</View>
|
||||
</MenuTrigger>
|
||||
|
||||
<MenuOptions
|
||||
customStyles={{
|
||||
optionsContainer: {
|
||||
@@ -129,25 +140,36 @@ export default function StaticTransactions({
|
||||
>
|
||||
<MenuOption
|
||||
customStyles={{
|
||||
OptionTouchableComponent: TouchableHighlight,
|
||||
optionTouchable: {
|
||||
underlayColor: '#f0f0f0', // 按压时的背景色
|
||||
activeOpacity: 0.7, // (可选)按压时的透明度
|
||||
},
|
||||
// 如果需要,也可以设置默认状态下的背景色
|
||||
optionWrapper: {
|
||||
backgroundColor: 'transparent', // 默认背景色
|
||||
underlayColor: '#f0f0f0',
|
||||
activeOpacity: 0.7,
|
||||
},
|
||||
}}
|
||||
onSelect={() => {
|
||||
onUpdate?.(item)
|
||||
}}
|
||||
>
|
||||
<View className="flex flex-row items-center h-10 px-2">
|
||||
<LucideIcon icon="Pencil" />
|
||||
<Text className="ml-2 text-lg">更新</Text>
|
||||
</View>
|
||||
</MenuOption>
|
||||
<MenuOption onSelect={() => alert(`Clicked Option B fo`)}>
|
||||
<MenuOption
|
||||
customStyles={{
|
||||
OptionTouchableComponent: TouchableHighlight,
|
||||
optionTouchable: {
|
||||
underlayColor: '#f0f0f0',
|
||||
activeOpacity: 0.7,
|
||||
},
|
||||
}}
|
||||
onSelect={() => {
|
||||
onDelete?.(item.id)
|
||||
}}
|
||||
>
|
||||
<View className="flex flex-row items-center h-10 px-2">
|
||||
<LucideIcon icon="Trash" />
|
||||
<Text className=" ml-2 text-lg">删除</Text>
|
||||
<Text className="ml-2 text-lg">删除</Text>
|
||||
</View>
|
||||
</MenuOption>
|
||||
</MenuOptions>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { MoneyRecordGroup, MoneyRecordItem, QueryMoneyRecordsResult } from '@/types/money/record'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { Text, TouchableOpacity, View } from 'react-native'
|
||||
import { Text, TouchableOpacity, TouchableWithoutFeedback, View } from 'react-native'
|
||||
import { useSafeAreaInsets } from 'react-native-safe-area-context'
|
||||
import { ADatePicker, LucideIcon } from '@/components/common/ao'
|
||||
import { Fab, FabIcon } from '@/components/ui/fab'
|
||||
@@ -277,13 +277,17 @@ export default function Money() {
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
<View
|
||||
className="flex-1"
|
||||
style={{
|
||||
paddingTop: insets.top,
|
||||
}}
|
||||
>
|
||||
<View>
|
||||
<View
|
||||
style={{
|
||||
paddingTop: insets.top,
|
||||
backgroundColor: '#fff',
|
||||
elevation: 3,
|
||||
}}
|
||||
>
|
||||
<View className="flex flex-row justify-between items-center px-3">
|
||||
<TouchableOpacity
|
||||
onPress={() => setShowDatePicker(true)}
|
||||
@@ -300,45 +304,51 @@ export default function Money() {
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
|
||||
<View className="mt-3 border border-gray-200 rounded-lg m-4 p-4 shadow-sm bg-white">
|
||||
<View
|
||||
style={{
|
||||
borderTopLeftRadius: 8,
|
||||
borderTopRightRadius: 8,
|
||||
}}
|
||||
>
|
||||
<View className="mb-4">
|
||||
<Text className="text-gray-500 text-sm">
|
||||
余额
|
||||
{/* <Text className="text-red-500">↓20%</Text> */}
|
||||
<View className="mt-3 m-4 ">
|
||||
|
||||
<View className="flex flex-row gap-4">
|
||||
<View className="flex-1">
|
||||
<Text className="text-gray-500 text-sm">余额</Text>
|
||||
<Text
|
||||
className="text-lg font-semibold mt-0.5"
|
||||
numberOfLines={1}
|
||||
ellipsizeMode="tail"
|
||||
>
|
||||
0.00
|
||||
</Text>
|
||||
<Text className="font-bold text-2xl mt-0.5">22,313.00</Text>
|
||||
</View>
|
||||
|
||||
<View className="flex-row flex gap-8">
|
||||
<View>
|
||||
<Text className="text-gray-500 text-sm">
|
||||
收入
|
||||
{/* <Text className="text-green-500">↑20%</Text> */}
|
||||
</Text>
|
||||
<Text className="text-lg font-semibold mt-0.5">{moneyRecords.total_income}</Text>
|
||||
</View>
|
||||
<View className="flex-1">
|
||||
<Text className="text-gray-500 text-sm">收入</Text>
|
||||
<Text
|
||||
className="text-lg font-semibold mt-0.5"
|
||||
numberOfLines={1}
|
||||
ellipsizeMode="tail"
|
||||
>
|
||||
0.00
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
<View>
|
||||
<Text className="text-gray-500 text-sm">
|
||||
支出
|
||||
{/* <Text className="text-green-500">↑20%</Text> */}
|
||||
</Text>
|
||||
<Text className="text-lg font-semibold mt-0.5">{moneyRecords.total_expense}</Text>
|
||||
</View>
|
||||
{/*
|
||||
<View>
|
||||
<Text className="text-gray-500 text-sm">
|
||||
结余
|
||||
<Text className="text-green-500">↑20%</Text>
|
||||
</Text>
|
||||
<Text className="text-lg font-semibold mt-0.5">11.0</Text>
|
||||
</View> */}
|
||||
<View className="flex-1">
|
||||
<Text className="text-gray-500 text-sm">支出</Text>
|
||||
<Text
|
||||
className="text-lg font-semibold mt-0.5"
|
||||
numberOfLines={1}
|
||||
ellipsizeMode="tail"
|
||||
>
|
||||
0.00
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
<View className="flex-1">
|
||||
<Text className="text-gray-500 text-sm">结余</Text>
|
||||
<Text
|
||||
className="text-lg font-semibold mt-0.5"
|
||||
numberOfLines={1}
|
||||
ellipsizeMode="tail"
|
||||
>
|
||||
0.00
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
|
||||
@@ -1,88 +0,0 @@
|
||||
import { router } from 'expo-router'
|
||||
import { StyleSheet, Text, View } from 'react-native'
|
||||
import { AButton, AInput } from '@/components/common/ao'
|
||||
import { LucideIcon } from '@/components/common/ao/index'
|
||||
import { useUserStore } from '@/store/user'
|
||||
|
||||
export default function User() {
|
||||
const userInfo = useUserStore(state => state.userInfo)
|
||||
|
||||
if (!userInfo) {
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Text style={styles.empty}>未登录</Text>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
const genderText = userInfo.gender === 1 ? '男' : userInfo.gender === 2 ? '女' : '未设置'
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Text style={styles.title}>个人信息</Text>
|
||||
<View style={styles.infoBox}>
|
||||
<Text style={styles.item}>
|
||||
账号:
|
||||
{userInfo.account}
|
||||
</Text>
|
||||
<Text style={styles.item}>
|
||||
用户名:
|
||||
{userInfo.username}
|
||||
</Text>
|
||||
<Text style={styles.item}>
|
||||
ID:
|
||||
{userInfo.id}
|
||||
</Text>
|
||||
<Text style={styles.item}>
|
||||
序号:
|
||||
{userInfo.seqNo}
|
||||
</Text>
|
||||
<Text style={styles.item}>
|
||||
性别:
|
||||
{genderText}
|
||||
</Text>
|
||||
{userInfo.rememberMe && <Text style={styles.item}>已开启记住我功能</Text>}
|
||||
</View>
|
||||
|
||||
<AButton onPress={() => {
|
||||
router.replace('/auth')
|
||||
}}
|
||||
>
|
||||
跳转到登陆页面
|
||||
</AButton>
|
||||
|
||||
<AInput value="2" />
|
||||
|
||||
<LucideIcon icon="User" size={32} strokeWidth={1} />
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
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',
|
||||
},
|
||||
})
|
||||
@@ -0,0 +1,45 @@
|
||||
import type { icons } from 'lucide-react-native'
|
||||
import { useRouter } from 'expo-router'
|
||||
import { Text, TouchableOpacity } from 'react-native'
|
||||
import { LucideIcon } from '@/components/common/ao/index'
|
||||
|
||||
export interface ListItemProps {
|
||||
icon: keyof typeof icons
|
||||
text: string
|
||||
href?: string
|
||||
onPress?: () => void
|
||||
}
|
||||
|
||||
export default function ListItem({ icon, text, href, onPress }: ListItemProps) {
|
||||
const router = useRouter()
|
||||
|
||||
const handlePress = () => {
|
||||
if (onPress) {
|
||||
onPress()
|
||||
return
|
||||
}
|
||||
|
||||
if (href) {
|
||||
router.navigate(href)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<TouchableOpacity
|
||||
className="flex flex-row items-center py-3"
|
||||
onPress={handlePress}
|
||||
>
|
||||
<LucideIcon icon={icon} size={24} strokeWidth={1.5} />
|
||||
|
||||
<Text className="text-base ml-3">{text}</Text>
|
||||
|
||||
<LucideIcon
|
||||
icon="ChevronRight"
|
||||
className="ml-auto"
|
||||
color="#ccc"
|
||||
size={22}
|
||||
strokeWidth={1.5}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
import { Text, TouchableOpacity, View } from 'react-native'
|
||||
import { SafeAreaView } from 'react-native-safe-area-context'
|
||||
import { LucideIcon } from '@/components/common/ao/index'
|
||||
import {
|
||||
Avatar,
|
||||
AvatarImage,
|
||||
} from '@/components/ui/avatar'
|
||||
import ListItem from './components/ListItem'
|
||||
|
||||
export default function User() {
|
||||
return (
|
||||
<SafeAreaView className="flex-1 p-4">
|
||||
<View className="flex flex-row items-center ">
|
||||
<Avatar size="md" className="border-0">
|
||||
<AvatarImage
|
||||
className="bg-white"
|
||||
source={require('@/static/images/logo.png')}
|
||||
/>
|
||||
</Avatar>
|
||||
<Text className="text-xl font-semibold ml-3">张三</Text>
|
||||
<LucideIcon icon="ChevronRight" size={26} className="ml-auto" color="#aaa" strokeWidth={1.5} />
|
||||
</View>
|
||||
|
||||
<View className="mt-8 flex flex-col">
|
||||
<ListItem icon="Earth" text="共享空间" href="/system/share" />
|
||||
<ListItem icon="Settings" text="设置"href="/system/settings" />
|
||||
</View>
|
||||
</SafeAreaView>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import { Text, View } from 'react-native'
|
||||
|
||||
export default function Settings() {
|
||||
return (
|
||||
<View>
|
||||
<Text>Settings</Text>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import { Stack } from 'expo-router'
|
||||
import { Text, View } from 'react-native'
|
||||
|
||||
export default function Share() {
|
||||
return (
|
||||
<>
|
||||
<Stack.Screen options={{
|
||||
headerShown: false,
|
||||
}}
|
||||
/>
|
||||
<View>
|
||||
<Text>Share</Text>
|
||||
</View>
|
||||
</>
|
||||
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import { Stack } from 'expo-router'
|
||||
import { SafeAreaView } from 'react-native-safe-area-context'
|
||||
|
||||
export default function CustomHeader() {
|
||||
return (
|
||||
<>
|
||||
<Stack.Screen options={{
|
||||
headerShown: false,
|
||||
}}
|
||||
/>
|
||||
|
||||
<SafeAreaView className='flex-1'>
|
||||
|
||||
</SafeAreaView>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -1,4 +1,3 @@
|
||||
import { Ionicons } from '@expo/vector-icons'
|
||||
import { usePathname, useRouter } from 'expo-router'
|
||||
import { useRef } from 'react'
|
||||
import { Image, Pressable, Text, View } from 'react-native'
|
||||
@@ -55,7 +54,7 @@ export default function StepLayout({ steps, children, showHearder = true }: Step
|
||||
onPress={canGoBack ? (currentStep.onBack ?? (() => router.back())) : undefined}
|
||||
style={{ opacity: canGoBack ? 1 : 0 }}
|
||||
>
|
||||
<Ionicons name="arrow-back" size={22} />
|
||||
{/* <Ionicons name="arrow-back" size={22} /> */}
|
||||
</Pressable>
|
||||
|
||||
<Text className="flex-1 text-center text-lg font-semibold">
|
||||
|
||||
@@ -0,0 +1,185 @@
|
||||
'use client';
|
||||
import React from 'react';
|
||||
import { createAvatar } from '@gluestack-ui/core/avatar/creator';
|
||||
|
||||
import { View, Text, Image, Platform } from 'react-native';
|
||||
|
||||
import { tva } from '@gluestack-ui/utils/nativewind-utils';
|
||||
import {
|
||||
withStyleContext,
|
||||
useStyleContext,
|
||||
} from '@gluestack-ui/utils/nativewind-utils';
|
||||
const SCOPE = 'AVATAR';
|
||||
import type { VariantProps } from '@gluestack-ui/utils/nativewind-utils';
|
||||
|
||||
const UIAvatar = createAvatar({
|
||||
Root: withStyleContext(View, SCOPE),
|
||||
Badge: View,
|
||||
Group: View,
|
||||
Image: Image,
|
||||
FallbackText: Text,
|
||||
});
|
||||
|
||||
const avatarStyle = tva({
|
||||
base: 'rounded-full justify-center items-center relative bg-primary-600 group-[.avatar-group]/avatar-group:-ml-2.5',
|
||||
variants: {
|
||||
size: {
|
||||
'xs': 'w-6 h-6',
|
||||
'sm': 'w-8 h-8',
|
||||
'md': 'w-12 h-12',
|
||||
'lg': 'w-16 h-16',
|
||||
'xl': 'w-24 h-24',
|
||||
'2xl': 'w-32 h-32',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const avatarFallbackTextStyle = tva({
|
||||
base: 'text-typography-0 font-semibold overflow-hidden text-transform:uppercase web:cursor-default',
|
||||
|
||||
parentVariants: {
|
||||
size: {
|
||||
'xs': 'text-2xs',
|
||||
'sm': 'text-xs',
|
||||
'md': 'text-base',
|
||||
'lg': 'text-xl',
|
||||
'xl': 'text-3xl',
|
||||
'2xl': 'text-5xl',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const avatarGroupStyle = tva({
|
||||
base: 'group/avatar-group flex-row-reverse relative avatar-group',
|
||||
});
|
||||
|
||||
const avatarBadgeStyle = tva({
|
||||
base: 'w-5 h-5 bg-success-500 rounded-full absolute right-0 bottom-0 border-background-0 border-2',
|
||||
parentVariants: {
|
||||
size: {
|
||||
'xs': 'w-2 h-2',
|
||||
'sm': 'w-2 h-2',
|
||||
'md': 'w-3 h-3',
|
||||
'lg': 'w-4 h-4',
|
||||
'xl': 'w-6 h-6',
|
||||
'2xl': 'w-8 h-8',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const avatarImageStyle = tva({
|
||||
base: 'h-full w-full rounded-full absolute',
|
||||
});
|
||||
|
||||
type IAvatarProps = Omit<
|
||||
React.ComponentPropsWithoutRef<typeof UIAvatar>,
|
||||
'context'
|
||||
> &
|
||||
VariantProps<typeof avatarStyle>;
|
||||
|
||||
const Avatar = React.forwardRef<
|
||||
React.ComponentRef<typeof UIAvatar>,
|
||||
IAvatarProps
|
||||
>(function Avatar({ className, size = 'md', ...props }, ref) {
|
||||
return (
|
||||
<UIAvatar
|
||||
ref={ref}
|
||||
{...props}
|
||||
className={avatarStyle({ size, class: className })}
|
||||
context={{ size }}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
type IAvatarBadgeProps = React.ComponentPropsWithoutRef<typeof UIAvatar.Badge> &
|
||||
VariantProps<typeof avatarBadgeStyle>;
|
||||
|
||||
const AvatarBadge = React.forwardRef<
|
||||
React.ComponentRef<typeof UIAvatar.Badge>,
|
||||
IAvatarBadgeProps
|
||||
>(function AvatarBadge({ className, size, ...props }, ref) {
|
||||
const { size: parentSize } = useStyleContext(SCOPE);
|
||||
|
||||
return (
|
||||
<UIAvatar.Badge
|
||||
ref={ref}
|
||||
{...props}
|
||||
className={avatarBadgeStyle({
|
||||
parentVariants: {
|
||||
size: parentSize,
|
||||
},
|
||||
size,
|
||||
class: className,
|
||||
})}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
type IAvatarFallbackTextProps = React.ComponentPropsWithoutRef<
|
||||
typeof UIAvatar.FallbackText
|
||||
> &
|
||||
VariantProps<typeof avatarFallbackTextStyle>;
|
||||
const AvatarFallbackText = React.forwardRef<
|
||||
React.ComponentRef<typeof UIAvatar.FallbackText>,
|
||||
IAvatarFallbackTextProps
|
||||
>(function AvatarFallbackText({ className, size, ...props }, ref) {
|
||||
const { size: parentSize } = useStyleContext(SCOPE);
|
||||
|
||||
return (
|
||||
<UIAvatar.FallbackText
|
||||
ref={ref}
|
||||
{...props}
|
||||
className={avatarFallbackTextStyle({
|
||||
parentVariants: {
|
||||
size: parentSize,
|
||||
},
|
||||
size,
|
||||
class: className,
|
||||
})}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
type IAvatarImageProps = React.ComponentPropsWithoutRef<typeof UIAvatar.Image> &
|
||||
VariantProps<typeof avatarImageStyle>;
|
||||
|
||||
const AvatarImage = React.forwardRef<
|
||||
React.ComponentRef<typeof UIAvatar.Image>,
|
||||
IAvatarImageProps
|
||||
>(function AvatarImage({ className, ...props }, ref) {
|
||||
return (
|
||||
<UIAvatar.Image
|
||||
ref={ref}
|
||||
{...props}
|
||||
className={avatarImageStyle({
|
||||
class: className,
|
||||
})}
|
||||
// @ts-expect-error : This is a workaround to fix the issue with the image style on web.
|
||||
style={
|
||||
Platform.OS === 'web'
|
||||
? { height: 'revert-layer', width: 'revert-layer' }
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
type IAvatarGroupProps = React.ComponentPropsWithoutRef<typeof UIAvatar.Group> &
|
||||
VariantProps<typeof avatarGroupStyle>;
|
||||
|
||||
const AvatarGroup = React.forwardRef<
|
||||
React.ComponentRef<typeof UIAvatar.Group>,
|
||||
IAvatarGroupProps
|
||||
>(function AvatarGroup({ className, ...props }, ref) {
|
||||
return (
|
||||
<UIAvatar.Group
|
||||
ref={ref}
|
||||
{...props}
|
||||
className={avatarGroupStyle({
|
||||
class: className,
|
||||
})}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
export { Avatar, AvatarBadge, AvatarFallbackText, AvatarImage, AvatarGroup };
|
||||
@@ -30,8 +30,8 @@ function isWhiteListed(url: string) {
|
||||
}
|
||||
|
||||
const alovaInstance = createAlova({
|
||||
baseURL: 'http://192.168.5.6:8085/app',
|
||||
// baseURL: 'http://192.168.1.136:8085/app',
|
||||
// baseURL: 'http://192.168.5.6:8085/app',
|
||||
baseURL: 'http://192.168.1.136:8085/app',
|
||||
requestAdapter: axiosRequestAdapter(),
|
||||
timeout: 5000,
|
||||
cacheFor: null,
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 122 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 171 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 52 KiB |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="800" height="430.493" viewBox="0 0 800 430.493" xmlns:xlink="http://www.w3.org/1999/xlink" role="img" artist="Katerina Limpitsouni" source="https://undraw.co/"><g transform="translate(-368.866 -254.661)"><ellipse cx="149.199" cy="26.107" rx="149.199" ry="26.107" transform="translate(368.866 481.453)" fill="#090814"/><ellipse cx="191.906" cy="26.107" rx="191.906" ry="26.107" transform="translate(785.054 632.939)" fill="#090814"/><path d="M527.508,703.017a139.876,139.876,0,0,1-34.251-3.807l1.007-3.942c61.693,15.766,176.375-20.484,188.785-59.673l3.879,1.228C676.145,670.877,591.731,703.017,527.508,703.017Z" transform="translate(229.585 -61.587)" fill="#2f2e41"/><path d="M422.29,529.663c-14.453-3.693-21.922-7.965-24.218-13.849-2.856-7.316,2.5-15.561,9.919-26.972,11.315-17.409,26.811-41.252,24.3-77.646-1.418-20.568-8.78-35.7-21.879-44.978-26.656-18.878-68.707-7.567-69.129-7.451l-1.085-3.921c1.8-.5,44.3-11.949,72.551,8.041,14.153,10.017,22.094,26.176,23.6,48.028,2.6,37.751-13.977,63.262-24.949,80.143-6.756,10.394-11.637,17.9-9.54,23.275,1.748,4.477,8.559,8.1,21.436,11.386Z" transform="translate(278.852 29.977)" fill="#2f2e41"/><path d="M816.039,661.123,795.384,696.05s-26.3,13.971-7.807,14.382,105.6,0,105.6,0,16.847,0-9.862-14.793l-19.389-37.478Z" transform="translate(136.968 -68.851)" fill="#e6e6e6"/><rect width="348.044" height="242.439" rx="18.046" transform="translate(801.887 355.38)" fill="#e6e6e6"/><path d="M2.712,0h319.2a2.712,2.712,0,0,1,2.712,2.712V193.294a2.712,2.712,0,0,1-2.712,2.712H2.712A2.712,2.712,0,0,1,0,193.294V2.712A2.712,2.712,0,0,1,2.712,0Z" transform="translate(813.598 368.323)" fill="#6c63ff"/><circle cx="2.055" cy="2.055" r="2.055" transform="translate(973.854 360.516)" fill="#f2f2f2"/><path d="M957.868,621.322v21.252a12.236,12.236,0,0,1-12.239,12.237H622.061a12.236,12.236,0,0,1-12.237-12.239V621.322Z" transform="translate(192.063 -56.993)" fill="#e6e6e6"/><path d="M864.04,660.444v4.109H642.968v-3.288l.3-.822,5.449-14.793h210.8Z" transform="translate(202.681 3.326)" fill="#f2f2f2"/><path d="M1096.367,755.317c-.4,1.722-1.923,3.538-5.358,5.256-12.327,6.164-37.393-1.644-37.393-1.644s-19.313-3.287-19.313-11.917a15.428,15.428,0,0,1,1.693-1.007c5.183-2.742,22.367-9.508,52.837.286a12.709,12.709,0,0,1,5.792,3.8C1095.859,751.534,1096.828,753.367,1096.367,755.317Z" transform="translate(55.429 -95.57)" fill="#f2f2f2"/><path d="M1097.171,755.317c-15.089,5.782-28.538,6.213-42.34-3.374-6.961-4.832-13.285-6.028-18.031-5.938,5.183-2.742,22.367-9.508,52.837.286a12.708,12.708,0,0,1,5.792,3.8C1096.663,751.534,1097.631,753.367,1097.171,755.317Z" transform="translate(54.626 -95.57)" fill="#e6e6e6"/><path d="M864.04,667.466v4.109H642.968v-3.288l.3-.822Z" transform="translate(202.681 -3.696)" opacity="0.1"/><path d="M794.285,488.635H592.452a6.086,6.086,0,0,1-6.079-6.079V414.467a6.086,6.086,0,0,1,6.079-6.079H794.285a6.086,6.086,0,0,1,6.079,6.079v68.088A6.086,6.086,0,0,1,794.285,488.635Z" transform="translate(-181.314 -153.727)" fill="#6c63ff"/><rect width="53.498" height="6.079" transform="translate(436.671 272.899)" fill="#f2f2f2"/><circle cx="3.648" cy="3.648" r="3.648" transform="translate(557.041 272.899)" fill="#f2f2f2"/><circle cx="3.648" cy="3.648" r="3.648" transform="translate(569.2 272.899)" fill="#f2f2f2"/><circle cx="3.648" cy="3.648" r="3.648" transform="translate(581.358 272.899)" fill="#f2f2f2"/><path d="M794.285,601.689H592.452a6.087,6.087,0,0,1-6.079-6.079V527.521a6.086,6.086,0,0,1,6.079-6.079H794.285a6.086,6.086,0,0,1,6.079,6.079v68.088A6.086,6.086,0,0,1,794.285,601.689Z" transform="translate(-181.314 -180.455)" fill="#e6e6e6"/><rect width="53.498" height="6.079" transform="translate(436.671 359.225)" fill="#6c63ff"/><circle cx="3.648" cy="3.648" r="3.648" transform="translate(557.041 359.225)" fill="#6c63ff"/><circle cx="3.648" cy="3.648" r="3.648" transform="translate(569.2 359.225)" fill="#6c63ff"/><circle cx="3.648" cy="3.648" r="3.648" transform="translate(581.358 359.225)" fill="#6c63ff"/><path d="M794.285,714.743H592.452a6.087,6.087,0,0,1-6.079-6.079V640.575a6.086,6.086,0,0,1,6.079-6.079H794.285a6.086,6.086,0,0,1,6.079,6.079v68.089A6.086,6.086,0,0,1,794.285,714.743Z" transform="translate(-181.314 -207.183)" fill="#e6e6e6"/><rect width="53.498" height="6.079" transform="translate(436.671 445.552)" fill="#6c63ff"/><circle cx="3.648" cy="3.648" r="3.648" transform="translate(557.041 445.552)" fill="#6c63ff"/><circle cx="3.648" cy="3.648" r="3.648" transform="translate(569.2 445.552)" fill="#6c63ff"/><circle cx="3.648" cy="3.648" r="3.648" transform="translate(581.358 445.552)" fill="#6c63ff"/><g transform="translate(933.489 421.851)"><path d="M0,0H88.949V88.949H0Z" fill="none"/><path d="M39.062,2A37.062,37.062,0,0,1,76.125,39.062a37.06,37.06,0,1,1-74.11,1.2L2,39.062l.015-1.038A37.061,37.061,0,0,1,39.062,2ZM39.1,50.181l-.471.026a3.706,3.706,0,0,0,0,7.361l.434.026.471-.026a3.706,3.706,0,0,0,0-7.361Zm-.037-32.362a3.706,3.706,0,0,0-3.68,3.272l-.026.43Line truncated
|
||||
|
Before Width: | Height: | Size: 5.0 KiB |
@@ -1,143 +1,232 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>家庭记账列表</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
background: #f9f9f9;
|
||||
margin: 20px;
|
||||
}
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>共享空间</title>
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto;
|
||||
background: #f6f6f7;
|
||||
color: #111;
|
||||
}
|
||||
|
||||
.record-list {
|
||||
max-width: 600px;
|
||||
margin: auto;
|
||||
}
|
||||
.page {
|
||||
max-width: 420px;
|
||||
margin: 0 auto;
|
||||
padding: 16px;
|
||||
background: #fff;
|
||||
min-height: 100vh;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.record {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 10px 15px;
|
||||
margin-bottom: 8px;
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
position: relative;
|
||||
transition: background 0.2s;
|
||||
cursor: default;
|
||||
}
|
||||
h1 {
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.record:hover {
|
||||
background: #f2f2f2;
|
||||
}
|
||||
/* 按钮 */
|
||||
.btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 40px;
|
||||
padding: 0 14px;
|
||||
border-radius: 8px;
|
||||
background: #111;
|
||||
color: #fff;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
margin-top: 12px;
|
||||
border: 1px solid #111;
|
||||
}
|
||||
|
||||
.category-icon {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 50%;
|
||||
background: #eee;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 18px;
|
||||
margin-right: 12px;
|
||||
}
|
||||
.btn-outline {
|
||||
background: transparent;
|
||||
color: #111;
|
||||
}
|
||||
|
||||
.info {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.input-box {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
border-radius: 6px;
|
||||
border: 1px solid #ddd;
|
||||
margin-top: 12px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.name {
|
||||
font-weight: 600;
|
||||
font-size: 16px;
|
||||
color: #333;
|
||||
}
|
||||
.list-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 14px 0;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
.remark {
|
||||
font-size: 12px;
|
||||
color: #888;
|
||||
display: none;
|
||||
margin-top: 2px;
|
||||
}
|
||||
.switcher {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.record:hover .remark {
|
||||
display: block;
|
||||
}
|
||||
.switcher button {
|
||||
flex: 1;
|
||||
height: 36px;
|
||||
border-radius: 6px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
border: 1px solid #ccc;
|
||||
background: #fafafa;
|
||||
}
|
||||
|
||||
.right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
.switcher button.active {
|
||||
background: #111;
|
||||
color: #fff;
|
||||
border-color: #111;
|
||||
}
|
||||
|
||||
.amount {
|
||||
font-weight: 600;
|
||||
font-size: 16px;
|
||||
}
|
||||
/* 弹窗 */
|
||||
.modal-mask {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(0, 0, 0, 0.45);
|
||||
display: none;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.amount.income {
|
||||
color: green;
|
||||
}
|
||||
.modal {
|
||||
background: #fff;
|
||||
padding: 20px;
|
||||
width: 100%;
|
||||
max-width: 360px;
|
||||
text-align: center;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.amount.expense {
|
||||
color: red;
|
||||
}
|
||||
|
||||
.member-avatar {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border-radius: 50%;
|
||||
background: #2196f3;
|
||||
color: #fff;
|
||||
font-size: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
</style>
|
||||
.qr-box {
|
||||
width: 180px;
|
||||
height: 180px;
|
||||
background: #eee;
|
||||
margin: 16px auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 10px;
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="record-list">
|
||||
<div class="record">
|
||||
<div class="category-icon">🛒</div>
|
||||
<div class="info">
|
||||
<div class="name">购物</div>
|
||||
<div class="remark">买菜和日用品</div>
|
||||
</div>
|
||||
<div class="right">
|
||||
<div class="amount expense">-50.00</div>
|
||||
<div class="member-avatar">爸</div>
|
||||
</div>
|
||||
<div class="page">
|
||||
<h1>共享空间(预览)</h1>
|
||||
|
||||
<div class="switcher">
|
||||
<button id="btnNotJoined" class="active" onclick="showPage('not')">未加入</button>
|
||||
<button id="btnJoined" onclick="showPage('yes')">已加入</button>
|
||||
</div>
|
||||
|
||||
<div class="record">
|
||||
<div class="category-icon">🍔</div>
|
||||
<div class="info">
|
||||
<div class="name">午餐</div>
|
||||
<div class="remark">外卖订单</div>
|
||||
</div>
|
||||
<div class="right">
|
||||
<div class="amount expense">-30.00</div>
|
||||
<div class="member-avatar">妈</div>
|
||||
</div>
|
||||
<!-- 未加入 -->
|
||||
<div id="notJoined">
|
||||
<p>你尚未加入共享空间,你可以创建自己的空间或加入他人的。</p>
|
||||
|
||||
<div class="btn" onclick="openInviteModal()">生成邀请码</div>
|
||||
<div class="btn-outline btn" onclick="openScanModal()">扫码加入</div>
|
||||
|
||||
<div class="btn btn-outline" onclick="toggleJoinInput()">输入邀请码加入</div>
|
||||
|
||||
<input id="inviteInput" type="text" class="input-box" placeholder="邀请码" style="display:none;" />
|
||||
<div id="joinBtn" class="btn" style="display:none;">加入</div>
|
||||
</div>
|
||||
|
||||
<div class="record">
|
||||
<div class="category-icon">💰</div>
|
||||
<div class="info">
|
||||
<div class="name">工资</div>
|
||||
<div class="remark">11月工资到账</div>
|
||||
</div>
|
||||
<div class="right">
|
||||
<div class="amount income">+5000.00</div>
|
||||
<div class="member-avatar">我</div>
|
||||
</div>
|
||||
<!-- 已加入 -->
|
||||
<div id="joined" style="display:none;">
|
||||
<p>你已加入共享空间,可查看成员、权限和邀请他人加入。</p>
|
||||
|
||||
<h3 style="margin-top:20px; font-size:16px; font-weight:600;">成员列表</h3>
|
||||
|
||||
<div class="list-item"><span>张三</span><span>管理员</span></div>
|
||||
<div class="list-item"><span>李四</span><span>成员</span></div>
|
||||
<div class="list-item"><span>王五</span><span>成员</span></div>
|
||||
|
||||
<div class="btn" onclick="openInviteModal()">邀请新成员</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 邀请二维码弹窗 -->
|
||||
<div id="inviteModal" class="modal-mask">
|
||||
<div class="modal">
|
||||
<h3>邀请码 / 二维码</h3>
|
||||
<div id="qrBox" class="qr-box">二维码生成中…</div>
|
||||
<div class="btn" onclick="closeInviteModal()">关闭</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 扫码加入弹窗(假装相机界面) -->
|
||||
<div id="scanModal" class="modal-mask">
|
||||
<div class="modal">
|
||||
<h3>扫码加入</h3>
|
||||
<div class="qr-box">[ 相机画面模拟区域 ]</div>
|
||||
<p style="font-size:12px;color:#666;">打开摄像头并扫描共享空间二维码</p>
|
||||
<div class="btn" onclick="closeScanModal()">关闭</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function showPage(type) {
|
||||
const not = document.getElementById("notJoined");
|
||||
const yes = document.getElementById("joined");
|
||||
const btnNot = document.getElementById("btnNotJoined");
|
||||
const btnYes = document.getElementById("btnJoined");
|
||||
|
||||
if (type === 'not') {
|
||||
not.style.display = 'block';
|
||||
yes.style.display = 'none';
|
||||
btnNot.classList.add('active');
|
||||
btnYes.classList.remove('active');
|
||||
} else {
|
||||
not.style.display = 'none';
|
||||
yes.style.display = 'block';
|
||||
btnYes.classList.add('active');
|
||||
btnNot.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------- 邀请码 / 二维码弹窗 -----------------
|
||||
function openInviteModal() {
|
||||
const modal = document.getElementById('inviteModal');
|
||||
const qr = document.getElementById('qrBox');
|
||||
const code = Math.random().toString(36).substring(2, 8).toUpperCase();
|
||||
qr.innerHTML = `二维码:${code}`;
|
||||
modal.style.display = 'flex';
|
||||
}
|
||||
function closeInviteModal() {
|
||||
document.getElementById('inviteModal').style.display = 'none';
|
||||
}
|
||||
|
||||
// ----------------- 扫码加入弹窗 -----------------
|
||||
function openScanModal() {
|
||||
document.getElementById('scanModal').style.display = 'flex';
|
||||
}
|
||||
function closeScanModal() {
|
||||
document.getElementById('scanModal').style.display = 'none';
|
||||
}
|
||||
|
||||
function toggleJoinInput() {
|
||||
const input = document.getElementById("inviteInput");
|
||||
const btn = document.getElementById("joinBtn");
|
||||
const show = input.style.display === 'none';
|
||||
input.style.display = show ? 'block' : 'none';
|
||||
btn.style.display = show ? 'block' : 'none';
|
||||
}
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
Reference in new issue
Block a user