refactor(ui): modernize components with flat design and UX refinements
- Redesign chat screen header with improved layout and panel styling - Update emoji panel tab bar from emoji to icon-based navigation - Simplify trade detail view with inline content attributes - Add masonry layout support to market view grid - Refactor privacy settings from card-based to list-based dropdown UI - Improve comment item actions layout and styling - Update trade creation flow with buy/sell specific text - Adjust padding, gaps, and font sizes across multiple components - Clean up unused code and imports
This commit is contained in:
@@ -143,7 +143,6 @@ function createCommentItemStyles(colors: AppColors) {
|
||||
paddingVertical: spacing.xs,
|
||||
borderLeftWidth: 2,
|
||||
borderLeftColor: colors.divider,
|
||||
backgroundColor: colors.background.default,
|
||||
},
|
||||
subReplyItem: {
|
||||
flexDirection: 'row',
|
||||
@@ -154,8 +153,8 @@ function createCommentItemStyles(colors: AppColors) {
|
||||
marginBottom: 0,
|
||||
},
|
||||
subReplyBody: {
|
||||
fontSize: fontSizes.sm,
|
||||
lineHeight: 20,
|
||||
fontSize: fontSizes.md,
|
||||
lineHeight: 22,
|
||||
marginTop: 2,
|
||||
},
|
||||
subReplyContent: {
|
||||
@@ -177,10 +176,10 @@ function createCommentItemStyles(colors: AppColors) {
|
||||
paddingVertical: spacing.xs,
|
||||
},
|
||||
replyToText: {
|
||||
fontSize: fontSizes.xs,
|
||||
fontSize: fontSizes.sm,
|
||||
},
|
||||
replyToName: {
|
||||
fontSize: fontSizes.xs,
|
||||
fontSize: fontSizes.sm,
|
||||
fontWeight: '500',
|
||||
},
|
||||
subReplyActions: {
|
||||
@@ -483,7 +482,7 @@ const CommentItem: React.FC<CommentItemProps> = ({
|
||||
{targetNickname && (
|
||||
<>
|
||||
<Text variant="caption" color={colors.text.hint} style={styles.replyToText}>
|
||||
{' '}回复
|
||||
{' '}回复{' '}
|
||||
</Text>
|
||||
<Text variant="caption" color={colors.primary.main} style={styles.replyToName}>
|
||||
{targetNickname}
|
||||
@@ -501,15 +500,6 @@ const CommentItem: React.FC<CommentItemProps> = ({
|
||||
{renderSubReplyImages(reply)}
|
||||
{/* 子评论操作按钮 */}
|
||||
<View style={styles.subReplyActions}>
|
||||
<TouchableOpacity
|
||||
style={styles.subActionButton}
|
||||
onPress={() => onReplyPress?.(reply)}
|
||||
>
|
||||
<MaterialCommunityIcons name="reply" size={12} color={colors.text.hint} />
|
||||
<Text variant="caption" color={colors.text.hint} style={styles.subActionText}>
|
||||
回复
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
{/* 点赞按钮 */}
|
||||
<TouchableOpacity
|
||||
style={styles.subActionButton}
|
||||
@@ -518,7 +508,7 @@ const CommentItem: React.FC<CommentItemProps> = ({
|
||||
>
|
||||
<MaterialCommunityIcons
|
||||
name={reply.is_liked ? 'heart' : 'heart-outline'}
|
||||
size={12}
|
||||
size={14}
|
||||
color={reply.is_liked ? colors.error.main : colors.text.hint}
|
||||
/>
|
||||
<Text
|
||||
@@ -529,6 +519,15 @@ const CommentItem: React.FC<CommentItemProps> = ({
|
||||
{reply.likes_count > 0 ? formatNumber(reply.likes_count) : '赞'}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity
|
||||
style={styles.subActionButton}
|
||||
onPress={() => onReplyPress?.(reply)}
|
||||
>
|
||||
<MaterialCommunityIcons name="reply" size={14} color={colors.text.hint} />
|
||||
<Text variant="caption" color={colors.text.hint} style={styles.subActionText}>
|
||||
回复
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
{/* 删除按钮 - 子评论作者可见 */}
|
||||
{isSubReplyAuthor && (
|
||||
<TouchableOpacity
|
||||
@@ -538,7 +537,7 @@ const CommentItem: React.FC<CommentItemProps> = ({
|
||||
>
|
||||
<MaterialCommunityIcons
|
||||
name={isDeleting ? 'loading' : 'delete-outline'}
|
||||
size={12}
|
||||
size={14}
|
||||
color={colors.text.hint}
|
||||
/>
|
||||
<Text variant="caption" color={colors.text.hint} style={styles.subActionText}>
|
||||
@@ -546,13 +545,12 @@ const CommentItem: React.FC<CommentItemProps> = ({
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
{/* 举报按钮 - 对非子评论作者显示 */}
|
||||
{!isSubReplyAuthor && onReport && (
|
||||
<TouchableOpacity
|
||||
style={styles.subActionButton}
|
||||
onPress={() => onReport(reply)}
|
||||
>
|
||||
<MaterialCommunityIcons name="flag-outline" size={12} color={colors.text.hint} />
|
||||
<MaterialCommunityIcons name="flag-outline" size={14} color={colors.text.hint} />
|
||||
<Text variant="caption" color={colors.text.hint} style={styles.subActionText}>
|
||||
举报
|
||||
</Text>
|
||||
|
||||
@@ -41,6 +41,7 @@ function createTradeCardStyles(colors: AppColors) {
|
||||
position: 'relative',
|
||||
aspectRatio: 1,
|
||||
backgroundColor: colors.background.default,
|
||||
minHeight: 140,
|
||||
},
|
||||
listImageContainer: {
|
||||
width: 120,
|
||||
@@ -104,7 +105,9 @@ function createTradeCardStyles(colors: AppColors) {
|
||||
fontWeight: '600',
|
||||
},
|
||||
gridContent: {
|
||||
padding: spacing.sm,
|
||||
paddingHorizontal: spacing.sm,
|
||||
paddingTop: spacing.xs,
|
||||
paddingBottom: spacing.sm,
|
||||
},
|
||||
titleRow: {
|
||||
flexDirection: 'row',
|
||||
@@ -155,7 +158,7 @@ function createTradeCardStyles(colors: AppColors) {
|
||||
flexDirection: 'row',
|
||||
flexWrap: 'wrap',
|
||||
gap: 4,
|
||||
marginTop: spacing.xs,
|
||||
marginTop: 2,
|
||||
},
|
||||
tag: {
|
||||
paddingHorizontal: 5,
|
||||
@@ -191,7 +194,7 @@ function createTradeCardStyles(colors: AppColors) {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
marginTop: spacing.sm,
|
||||
marginTop: spacing.xs,
|
||||
},
|
||||
authorRow: {
|
||||
flexDirection: 'row',
|
||||
@@ -354,6 +357,9 @@ const TradeCardBase: React.FC<TradeCardProps> = ({ item, variant = 'list', onPre
|
||||
);
|
||||
}
|
||||
|
||||
// grid variant is used in masonry layout; list variant below is unused but kept for compatibility
|
||||
|
||||
|
||||
return (
|
||||
<TouchableOpacity
|
||||
style={styles.listCard}
|
||||
|
||||
@@ -15,7 +15,7 @@ interface CustomTextProps extends Omit<TextProps, 'style'> {
|
||||
color?: string;
|
||||
numberOfLines?: number;
|
||||
onPress?: () => void;
|
||||
style?: TextStyle | TextStyle[];
|
||||
style?: TextStyle | TextStyle[] | (TextStyle | undefined)[];
|
||||
weight?: 'regular' | 'medium' | 'semibold' | 'bold';
|
||||
}
|
||||
|
||||
|
||||
@@ -90,9 +90,6 @@ export const AppsScreen: React.FC = () => {
|
||||
|
||||
const renderTiles = () => (
|
||||
<>
|
||||
<Text variant="caption" color={colors.text.secondary} style={styles.pageSubtitle}>
|
||||
与社区账号打通的轻应用入口
|
||||
</Text>
|
||||
{APP_ENTRIES.map(item => (
|
||||
<TouchableOpacity
|
||||
key={item.id}
|
||||
|
||||
@@ -130,19 +130,19 @@ function createHomeStyles(colors: AppColors, responsivePadding: number) {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
paddingHorizontal: spacing.md,
|
||||
paddingVertical: spacing.xs,
|
||||
paddingVertical: spacing.sm,
|
||||
},
|
||||
sortBarCapsuleScroll: {
|
||||
flex: 1,
|
||||
marginHorizontal: spacing.xs,
|
||||
},
|
||||
sortBarCapsuleContent: {
|
||||
gap: spacing.xs,
|
||||
gap: spacing.sm,
|
||||
alignItems: 'center',
|
||||
paddingVertical: spacing.xs,
|
||||
},
|
||||
capsuleItem: {
|
||||
paddingHorizontal: spacing.md,
|
||||
paddingVertical: spacing.xs,
|
||||
paddingVertical: spacing.sm,
|
||||
borderRadius: 999,
|
||||
},
|
||||
capsuleText: {
|
||||
@@ -156,8 +156,8 @@ function createHomeStyles(colors: AppColors, responsivePadding: number) {
|
||||
sortTrigger: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
paddingVertical: 4,
|
||||
paddingHorizontal: 2,
|
||||
paddingVertical: spacing.sm,
|
||||
paddingHorizontal: spacing.xs,
|
||||
},
|
||||
sortTriggerText: {
|
||||
fontSize: 13,
|
||||
@@ -986,10 +986,7 @@ export const HomeScreen: React.FC = () => {
|
||||
renderItem={({ item }: { item: { key: string; label: string } }) => (
|
||||
<TouchableOpacity
|
||||
activeOpacity={0.7}
|
||||
style={[
|
||||
styles.capsuleItem,
|
||||
marketCategory === item.key && { backgroundColor: `${colors.primary.main}18` },
|
||||
]}
|
||||
style={styles.capsuleItem}
|
||||
onPress={() => setMarketCategory(item.key as TradeCategory | 'all')}
|
||||
>
|
||||
<Text style={[
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { useState, useEffect, useCallback, useRef, useMemo } from 'react';
|
||||
import { View, StyleSheet, RefreshControl, Pressable } from 'react-native';
|
||||
import { View, StyleSheet, RefreshControl } from 'react-native';
|
||||
import { FlashList, FlashListRef } from '@shopify/flash-list';
|
||||
import type { ListRenderItem } from '@shopify/flash-list';
|
||||
import { useAppColors, type AppColors } from '../../theme';
|
||||
@@ -27,12 +27,9 @@ function createMarketStyles(colors: AppColors, gap: number, padding: number) {
|
||||
},
|
||||
listContent: {
|
||||
paddingHorizontal: padding,
|
||||
paddingTop: gap,
|
||||
paddingTop: gap / 2,
|
||||
paddingBottom: 80,
|
||||
},
|
||||
columnWrapper: {
|
||||
justifyContent: 'space-between',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -54,7 +51,7 @@ export function MarketView({
|
||||
const colors = useAppColors();
|
||||
const router = useRouter();
|
||||
const isAuth = useIsAuthenticated();
|
||||
const { width, isMobile } = useResponsive();
|
||||
const { width } = useResponsive();
|
||||
|
||||
const gap = useResponsiveSpacing({ xs: 6, sm: 8, md: 10, lg: 12, xl: 16 });
|
||||
const padding = useResponsiveSpacing({ xs: 8, sm: 10, md: 12, lg: 16, xl: 20 });
|
||||
@@ -74,7 +71,7 @@ export function MarketView({
|
||||
const setFilterType = onFilterTypeChange ?? setFilterTypeLocal;
|
||||
const setSelectedCategory = onSelectedCategoryChange ?? setSelectedCategoryLocal;
|
||||
|
||||
const flashListRef = useRef<any>(null);
|
||||
const flashListRef = useRef<FlashListRef<any>>(null);
|
||||
|
||||
// 网格列数:移动端2列,平板3列,桌面4列
|
||||
const numColumns = useMemo(() => {
|
||||
@@ -83,12 +80,6 @@ export function MarketView({
|
||||
return 2;
|
||||
}, [width]);
|
||||
|
||||
// 计算卡片宽度(用于固定宽高比)
|
||||
const cardWidth = useMemo(() => {
|
||||
const totalGap = gap * (numColumns - 1);
|
||||
return Math.floor((width - padding * 2 - totalGap) / numColumns);
|
||||
}, [width, padding, gap, numColumns]);
|
||||
|
||||
const fetchItems = useCallback(async (isRefresh = false) => {
|
||||
try {
|
||||
if (isRefresh) {
|
||||
@@ -172,7 +163,7 @@ export function MarketView({
|
||||
}, [items, isAuth]);
|
||||
|
||||
const renderItem = useCallback<ListRenderItem<TradeItemDTO>>(({ item }) => (
|
||||
<View style={{ width: cardWidth, marginBottom: gap }}>
|
||||
<View style={{ marginBottom: gap }}>
|
||||
<TradeCard
|
||||
item={item}
|
||||
variant="grid"
|
||||
@@ -180,7 +171,7 @@ export function MarketView({
|
||||
onFavorite={handleFavorite}
|
||||
/>
|
||||
</View>
|
||||
), [handlePressItem, handleFavorite, cardWidth, gap]);
|
||||
), [handlePressItem, handleFavorite, gap]);
|
||||
|
||||
const keyExtractor = useCallback((item: TradeItemDTO) => item.id, []);
|
||||
|
||||
@@ -217,6 +208,7 @@ export function MarketView({
|
||||
}
|
||||
ListFooterComponent={isLoadingMore ? <Loading size="sm" /> : null}
|
||||
drawDistance={250}
|
||||
masonry
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/**
|
||||
* 资料详情页:展示资料详细信息
|
||||
* 现代化扁平化设计,风格统一
|
||||
*/
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import {
|
||||
@@ -114,16 +115,17 @@ export const MaterialDetailScreen: React.FC = () => {
|
||||
});
|
||||
}, []);
|
||||
|
||||
// 扁平化卡片样式:更柔和的阴影,更大的圆角
|
||||
const cardStyle = useMemo(
|
||||
() => ({
|
||||
backgroundColor: colors.background.paper,
|
||||
borderRadius: borderRadius.lg,
|
||||
borderRadius: borderRadius.xl,
|
||||
overflow: 'hidden' as const,
|
||||
shadowColor: '#000',
|
||||
shadowOffset: { width: 0, height: 1 },
|
||||
shadowOpacity: 0.04,
|
||||
shadowRadius: 4,
|
||||
elevation: 1,
|
||||
shadowColor: colors.chat.shadow,
|
||||
shadowOffset: { width: 0, height: 2 },
|
||||
shadowOpacity: 0.06,
|
||||
shadowRadius: 8,
|
||||
elevation: 2,
|
||||
}),
|
||||
[colors]
|
||||
);
|
||||
@@ -159,14 +161,20 @@ export const MaterialDetailScreen: React.FC = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* 文件信息卡片 */}
|
||||
{/* 文件信息卡片 - 现代化设计 */}
|
||||
<View style={[styles.fileCard, cardStyle]}>
|
||||
{/* 顶部文件类型标识区 - 扁平化渐变 */}
|
||||
<View style={[styles.fileTypeBanner, { backgroundColor: fileColor }]}>
|
||||
<MaterialCommunityIcons name={iconName as any} size={48} color="#FFFFFF" />
|
||||
<Text variant="body" style={styles.fileTypeName}>
|
||||
{fileTypeName}
|
||||
</Text>
|
||||
<View style={styles.fileIconWrapper}>
|
||||
<MaterialCommunityIcons name={iconName as any} size={40} color="#FFFFFF" />
|
||||
</View>
|
||||
<View style={styles.fileTypeBadge}>
|
||||
<Text variant="caption" style={styles.fileTypeName}>
|
||||
{fileTypeName}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<View style={styles.fileInfo}>
|
||||
<Text variant="h3" style={styles.fileName}>
|
||||
{material.title}
|
||||
@@ -176,16 +184,18 @@ export const MaterialDetailScreen: React.FC = () => {
|
||||
{material.description}
|
||||
</Text>
|
||||
)}
|
||||
|
||||
{/* 元数据 - 现代化标签式布局 */}
|
||||
<View style={styles.fileMetaRow}>
|
||||
<View style={styles.metaItem}>
|
||||
<MaterialCommunityIcons name="file-outline" size={16} color={colors.text.hint} />
|
||||
<Text variant="caption" color={colors.text.hint}>
|
||||
<View style={[styles.metaBadge, { backgroundColor: `${colors.neutral.main}12` }]}>
|
||||
<MaterialCommunityIcons name="file-outline" size={14} color={colors.neutral.main} />
|
||||
<Text variant="caption" color={colors.neutral.dark} style={styles.metaBadgeText}>
|
||||
{formatFileSize(material.file_size)}
|
||||
</Text>
|
||||
</View>
|
||||
<View style={styles.metaItem}>
|
||||
<MaterialCommunityIcons name="download" size={16} color={colors.text.hint} />
|
||||
<Text variant="caption" color={colors.text.hint}>
|
||||
<View style={[styles.metaBadge, { backgroundColor: `${colors.primary.main}12` }]}>
|
||||
<MaterialCommunityIcons name="download" size={14} color={colors.primary.main} />
|
||||
<Text variant="caption" color={colors.primary.dark} style={styles.metaBadgeText}>
|
||||
{material.download_count} 次下载
|
||||
</Text>
|
||||
</View>
|
||||
@@ -193,46 +203,69 @@ export const MaterialDetailScreen: React.FC = () => {
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* 详细信息 */}
|
||||
{/* 详细信息卡片 - 扁平化列表 */}
|
||||
<View style={[styles.detailCard, cardStyle]}>
|
||||
<Text variant="body" style={styles.sectionTitle}>
|
||||
详细信息
|
||||
</Text>
|
||||
<View style={styles.detailRow}>
|
||||
<Text variant="caption" color={colors.text.hint} style={styles.detailLabel}>
|
||||
文件名
|
||||
</Text>
|
||||
<Text variant="body" color={colors.text.primary} style={styles.detailValue}>
|
||||
{material.file_name}
|
||||
<View style={styles.sectionHeader}>
|
||||
<View style={[styles.sectionIcon, { backgroundColor: `${colors.info.main}12` }]}>
|
||||
<MaterialCommunityIcons name="information-outline" size={18} color={colors.info.main} />
|
||||
</View>
|
||||
<Text variant="body" style={styles.sectionTitle}>
|
||||
详细信息
|
||||
</Text>
|
||||
</View>
|
||||
<View style={styles.detailRow}>
|
||||
<Text variant="caption" color={colors.text.hint} style={styles.detailLabel}>
|
||||
上传时间
|
||||
</Text>
|
||||
<Text variant="body" color={colors.text.primary} style={styles.detailValue}>
|
||||
{formatDate(material.created_at)}
|
||||
</Text>
|
||||
</View>
|
||||
{material.author_name && (
|
||||
|
||||
<View style={styles.detailList}>
|
||||
<View style={styles.detailRow}>
|
||||
<Text variant="caption" color={colors.text.hint} style={styles.detailLabel}>
|
||||
上传者
|
||||
</Text>
|
||||
<View style={styles.detailLabelWrapper}>
|
||||
<MaterialCommunityIcons name="file-document-outline" size={16} color={colors.text.hint} />
|
||||
<Text variant="caption" color={colors.text.hint} style={styles.detailLabel}>
|
||||
文件名
|
||||
</Text>
|
||||
</View>
|
||||
<Text variant="body" color={colors.text.primary} style={styles.detailValue}>
|
||||
{material.author_name}
|
||||
{material.file_name}
|
||||
</Text>
|
||||
</View>
|
||||
)}
|
||||
{material.tags && material.tags.length > 0 && (
|
||||
<View style={styles.tagContainer}>
|
||||
<Text variant="caption" color={colors.text.hint} style={styles.detailLabel}>
|
||||
标签
|
||||
|
||||
<View style={styles.detailRow}>
|
||||
<View style={styles.detailLabelWrapper}>
|
||||
<MaterialCommunityIcons name="calendar-outline" size={16} color={colors.text.hint} />
|
||||
<Text variant="caption" color={colors.text.hint} style={styles.detailLabel}>
|
||||
上传时间
|
||||
</Text>
|
||||
</View>
|
||||
<Text variant="body" color={colors.text.primary} style={styles.detailValue}>
|
||||
{formatDate(material.created_at)}
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
{material.author_name && (
|
||||
<View style={styles.detailRow}>
|
||||
<View style={styles.detailLabelWrapper}>
|
||||
<MaterialCommunityIcons name="account-outline" size={16} color={colors.text.hint} />
|
||||
<Text variant="caption" color={colors.text.hint} style={styles.detailLabel}>
|
||||
上传者
|
||||
</Text>
|
||||
</View>
|
||||
<Text variant="body" color={colors.text.primary} style={styles.detailValue}>
|
||||
{material.author_name}
|
||||
</Text>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
|
||||
{material.tags && material.tags.length > 0 && (
|
||||
<View style={styles.tagSection}>
|
||||
<View style={styles.tagSectionHeader}>
|
||||
<MaterialCommunityIcons name="tag-outline" size={16} color={colors.text.hint} />
|
||||
<Text variant="caption" color={colors.text.hint} style={styles.tagSectionTitle}>
|
||||
标签
|
||||
</Text>
|
||||
</View>
|
||||
<View style={styles.tags}>
|
||||
{material.tags.map((tag, index) => (
|
||||
<View key={index} style={styles.tag}>
|
||||
<Text variant="caption" color={colors.primary.main}>
|
||||
<View key={index} style={[styles.tag, { backgroundColor: `${colors.primary.main}10` }]}>
|
||||
<Text variant="caption" color={colors.primary.main} style={styles.tagText}>
|
||||
{tag}
|
||||
</Text>
|
||||
</View>
|
||||
@@ -242,24 +275,29 @@ export const MaterialDetailScreen: React.FC = () => {
|
||||
)}
|
||||
</View>
|
||||
|
||||
{/* 操作按钮 */}
|
||||
{/* 操作按钮 - 现代化扁平按钮 */}
|
||||
<View style={styles.actionButtons}>
|
||||
<TouchableOpacity
|
||||
style={[styles.previewButton, { backgroundColor: colors.background.paper }]}
|
||||
onPress={handlePreview}
|
||||
activeOpacity={0.8}
|
||||
activeOpacity={0.7}
|
||||
>
|
||||
<MaterialCommunityIcons name="eye-outline" size={20} color={colors.text.primary} />
|
||||
<View style={[styles.actionIconWrapper, { backgroundColor: `${colors.neutral.main}12` }]}>
|
||||
<MaterialCommunityIcons name="eye-outline" size={18} color={colors.neutral.dark} />
|
||||
</View>
|
||||
<Text variant="body" color={colors.text.primary} style={styles.actionButtonText}>
|
||||
在线预览
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
|
||||
<TouchableOpacity
|
||||
style={[styles.downloadButton, { backgroundColor: colors.primary.main }]}
|
||||
onPress={handleDownload}
|
||||
activeOpacity={0.8}
|
||||
>
|
||||
<MaterialCommunityIcons name="download" size={20} color={colors.primary.contrast} />
|
||||
<View style={[styles.actionIconWrapper, { backgroundColor: 'rgba(255,255,255,0.2)' }]}>
|
||||
<MaterialCommunityIcons name="download" size={18} color="#FFFFFF" />
|
||||
</View>
|
||||
<Text variant="body" color={colors.primary.contrast} style={styles.actionButtonText}>
|
||||
下载资料
|
||||
</Text>
|
||||
@@ -273,7 +311,7 @@ export const MaterialDetailScreen: React.FC = () => {
|
||||
<SafeAreaView style={styles.container} edges={['top', 'bottom']}>
|
||||
<StatusBar barStyle={statusBarStyle} backgroundColor={colors.background.default} />
|
||||
|
||||
{/* Header */}
|
||||
{/* Header - 扁平化设计 */}
|
||||
<View style={[styles.header, isWideScreen && styles.headerWide]}>
|
||||
<View style={styles.headerLeft}>
|
||||
<AppBackButton onPress={() => router.back()} />
|
||||
@@ -284,15 +322,15 @@ export const MaterialDetailScreen: React.FC = () => {
|
||||
</Text>
|
||||
</View>
|
||||
<View style={styles.headerRight}>
|
||||
<TouchableOpacity style={styles.shareButton}>
|
||||
<MaterialCommunityIcons name="share-variant" size={22} color={colors.text.primary} />
|
||||
<TouchableOpacity style={styles.shareButton} activeOpacity={0.7}>
|
||||
<MaterialCommunityIcons name="share-variant" size={20} color={colors.text.secondary} />
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* Content */}
|
||||
{isWideScreen ? (
|
||||
<ResponsiveContainer maxWidth={800}>
|
||||
<ResponsiveContainer maxWidth={720}>
|
||||
<ScrollView
|
||||
style={styles.scroll}
|
||||
contentContainerStyle={[styles.scrollContent, { paddingBottom: scrollBottomInset }]}
|
||||
@@ -326,6 +364,7 @@ function createMaterialDetailStyles(colors: AppColors) {
|
||||
flex: 1,
|
||||
backgroundColor: colors.background.default,
|
||||
},
|
||||
// Header
|
||||
header: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
@@ -333,7 +372,8 @@ function createMaterialDetailStyles(colors: AppColors) {
|
||||
paddingHorizontal: spacing.md,
|
||||
paddingVertical: spacing.md,
|
||||
backgroundColor: headerBg,
|
||||
...shadows.sm,
|
||||
borderBottomWidth: 1,
|
||||
borderBottomColor: colors.chat.borderHairline,
|
||||
},
|
||||
headerWide: {
|
||||
paddingHorizontal: spacing.lg,
|
||||
@@ -348,12 +388,13 @@ function createMaterialDetailStyles(colors: AppColors) {
|
||||
alignItems: 'center',
|
||||
},
|
||||
headerTitle: {
|
||||
fontSize: 19,
|
||||
fontWeight: '700',
|
||||
fontSize: fontSizes.lg,
|
||||
fontWeight: '600',
|
||||
color: colors.text.primary,
|
||||
letterSpacing: 0.3,
|
||||
},
|
||||
headerTitleWide: {
|
||||
fontSize: 22,
|
||||
fontSize: fontSizes.xl,
|
||||
},
|
||||
headerRight: {
|
||||
width: 44,
|
||||
@@ -361,16 +402,19 @@ function createMaterialDetailStyles(colors: AppColors) {
|
||||
},
|
||||
shareButton: {
|
||||
padding: spacing.xs,
|
||||
borderRadius: borderRadius.md,
|
||||
},
|
||||
// Scroll
|
||||
scroll: {
|
||||
flex: 1,
|
||||
backgroundColor: headerBg,
|
||||
},
|
||||
scrollContent: {
|
||||
paddingTop: spacing.md,
|
||||
paddingTop: spacing.lg,
|
||||
flexGrow: 1,
|
||||
backgroundColor: headerBg,
|
||||
},
|
||||
// Center states
|
||||
centerContainer: {
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
@@ -386,19 +430,36 @@ function createMaterialDetailStyles(colors: AppColors) {
|
||||
paddingVertical: spacing.sm,
|
||||
paddingHorizontal: spacing.lg,
|
||||
},
|
||||
// File card
|
||||
fileCard: {
|
||||
marginBottom: spacing.md,
|
||||
marginBottom: spacing.lg,
|
||||
},
|
||||
fileTypeBanner: {
|
||||
height: 88,
|
||||
height: 120,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
position: 'relative',
|
||||
},
|
||||
fileIconWrapper: {
|
||||
width: 72,
|
||||
height: 72,
|
||||
borderRadius: borderRadius['2xl'],
|
||||
backgroundColor: 'rgba(255,255,255,0.2)',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
marginBottom: spacing.sm,
|
||||
},
|
||||
fileTypeBadge: {
|
||||
paddingVertical: spacing.xs,
|
||||
paddingHorizontal: spacing.md,
|
||||
borderRadius: borderRadius.full,
|
||||
backgroundColor: 'rgba(255,255,255,0.25)',
|
||||
},
|
||||
fileTypeName: {
|
||||
color: '#FFFFFF',
|
||||
fontWeight: '700',
|
||||
fontSize: fontSizes.lg,
|
||||
marginTop: spacing.sm,
|
||||
fontSize: fontSizes.sm,
|
||||
letterSpacing: 0.5,
|
||||
},
|
||||
fileInfo: {
|
||||
padding: spacing.lg,
|
||||
@@ -406,69 +467,114 @@ function createMaterialDetailStyles(colors: AppColors) {
|
||||
fileName: {
|
||||
color: colors.text.primary,
|
||||
fontWeight: '700',
|
||||
fontSize: fontSizes.lg,
|
||||
fontSize: fontSizes.xl,
|
||||
lineHeight: fontSizes.xl * 1.4,
|
||||
},
|
||||
fileDescription: {
|
||||
marginTop: spacing.sm,
|
||||
lineHeight: fontSizes.md * 1.5,
|
||||
lineHeight: fontSizes.md * 1.6,
|
||||
color: colors.text.secondary,
|
||||
},
|
||||
fileMetaRow: {
|
||||
flexDirection: 'row',
|
||||
marginTop: spacing.md,
|
||||
gap: spacing.md,
|
||||
marginTop: spacing.lg,
|
||||
gap: spacing.sm,
|
||||
flexWrap: 'wrap',
|
||||
},
|
||||
metaItem: {
|
||||
metaBadge: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
gap: spacing.xs,
|
||||
paddingVertical: spacing.xs + 2,
|
||||
paddingHorizontal: spacing.md,
|
||||
borderRadius: borderRadius.full,
|
||||
},
|
||||
metaBadgeText: {
|
||||
fontWeight: '500',
|
||||
fontSize: fontSizes.sm,
|
||||
},
|
||||
// Detail card
|
||||
detailCard: {
|
||||
padding: spacing.lg,
|
||||
marginBottom: spacing.lg,
|
||||
},
|
||||
sectionHeader: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
marginBottom: spacing.md,
|
||||
gap: spacing.sm,
|
||||
},
|
||||
sectionIcon: {
|
||||
width: 32,
|
||||
height: 32,
|
||||
borderRadius: borderRadius.md,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
sectionTitle: {
|
||||
fontWeight: '700',
|
||||
fontSize: fontSizes.md,
|
||||
color: colors.text.primary,
|
||||
marginBottom: spacing.md,
|
||||
letterSpacing: 0.2,
|
||||
},
|
||||
detailList: {
|
||||
gap: 0,
|
||||
},
|
||||
detailRow: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
paddingVertical: spacing.sm,
|
||||
paddingVertical: spacing.md,
|
||||
borderBottomWidth: 1,
|
||||
borderBottomColor: colors.divider,
|
||||
borderBottomColor: colors.chat.borderHairline,
|
||||
},
|
||||
detailLabel: {
|
||||
width: 80,
|
||||
},
|
||||
detailValue: {
|
||||
flex: 1,
|
||||
textAlign: 'right',
|
||||
},
|
||||
tagContainer: {
|
||||
detailLabelWrapper: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
marginTop: spacing.sm,
|
||||
gap: spacing.sm,
|
||||
flex: 1,
|
||||
},
|
||||
detailLabel: {
|
||||
fontWeight: '500',
|
||||
},
|
||||
detailValue: {
|
||||
flex: 1.5,
|
||||
textAlign: 'right',
|
||||
fontWeight: '500',
|
||||
},
|
||||
// Tags
|
||||
tagSection: {
|
||||
marginTop: spacing.md,
|
||||
},
|
||||
tagSectionHeader: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
gap: spacing.sm,
|
||||
marginBottom: spacing.sm,
|
||||
},
|
||||
tagSectionTitle: {
|
||||
fontWeight: '500',
|
||||
},
|
||||
tags: {
|
||||
flexDirection: 'row',
|
||||
flexWrap: 'wrap',
|
||||
gap: spacing.xs,
|
||||
flex: 1,
|
||||
gap: spacing.sm,
|
||||
},
|
||||
tag: {
|
||||
paddingVertical: spacing.xs,
|
||||
paddingHorizontal: spacing.sm,
|
||||
backgroundColor: `${colors.primary.main}10`,
|
||||
borderRadius: borderRadius.md,
|
||||
borderWidth: 0,
|
||||
paddingVertical: spacing.xs + 2,
|
||||
paddingHorizontal: spacing.md,
|
||||
borderRadius: borderRadius.full,
|
||||
},
|
||||
tagText: {
|
||||
fontWeight: '600',
|
||||
fontSize: fontSizes.sm,
|
||||
},
|
||||
// Action buttons
|
||||
actionButtons: {
|
||||
flexDirection: 'row',
|
||||
gap: spacing.md,
|
||||
marginTop: spacing.lg,
|
||||
marginTop: spacing.sm,
|
||||
marginBottom: spacing.lg,
|
||||
},
|
||||
previewButton: {
|
||||
flex: 1,
|
||||
@@ -476,10 +582,15 @@ function createMaterialDetailStyles(colors: AppColors) {
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
paddingVertical: spacing.md,
|
||||
borderRadius: borderRadius.md,
|
||||
gap: spacing.xs,
|
||||
borderWidth: StyleSheet.hairlineWidth,
|
||||
borderColor: colors.divider,
|
||||
borderRadius: borderRadius.xl,
|
||||
gap: spacing.sm,
|
||||
borderWidth: 1,
|
||||
borderColor: colors.chat.border,
|
||||
shadowColor: colors.chat.shadow,
|
||||
shadowOffset: { width: 0, height: 1 },
|
||||
shadowOpacity: 0.04,
|
||||
shadowRadius: 4,
|
||||
elevation: 1,
|
||||
},
|
||||
downloadButton: {
|
||||
flex: 1,
|
||||
@@ -487,11 +598,25 @@ function createMaterialDetailStyles(colors: AppColors) {
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
paddingVertical: spacing.md,
|
||||
borderRadius: borderRadius.xl,
|
||||
gap: spacing.sm,
|
||||
shadowColor: colors.primary.main,
|
||||
shadowOffset: { width: 0, height: 4 },
|
||||
shadowOpacity: 0.25,
|
||||
shadowRadius: 8,
|
||||
elevation: 4,
|
||||
},
|
||||
actionIconWrapper: {
|
||||
width: 32,
|
||||
height: 32,
|
||||
borderRadius: borderRadius.md,
|
||||
gap: spacing.xs,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
actionButtonText: {
|
||||
fontWeight: '600',
|
||||
fontWeight: '700',
|
||||
fontSize: fontSizes.md,
|
||||
letterSpacing: 0.3,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,56 +79,52 @@ export const ChatHeader: React.FC<ChatHeaderProps> = ({
|
||||
return (
|
||||
<View style={styles.headerContainer}>
|
||||
<View style={styles.header}>
|
||||
<TouchableOpacity style={styles.backButton} onPress={onBack} activeOpacity={0.7}>
|
||||
<MaterialCommunityIcons name="chevron-left" size={28} color={iconColor} />
|
||||
</TouchableOpacity>
|
||||
|
||||
<View style={styles.headerLeft}>
|
||||
<TouchableOpacity style={styles.backButton} onPress={onBack} activeOpacity={0.7}>
|
||||
<MaterialCommunityIcons name="chevron-left" size={28} color={iconColor} />
|
||||
</TouchableOpacity>
|
||||
{isGroupChat ? (
|
||||
<Avatar
|
||||
source={groupInfo?.avatar || null}
|
||||
size={36}
|
||||
name={groupInfo?.name || '群'}
|
||||
style={styles.groupAvatar}
|
||||
/>
|
||||
) : (
|
||||
<Avatar
|
||||
source={otherUser?.avatar || null}
|
||||
size={36}
|
||||
name={otherUser?.nickname || ''}
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
|
||||
<View style={styles.headerCenter}>
|
||||
<TouchableOpacity
|
||||
style={styles.titleRow}
|
||||
onPress={onTitlePress}
|
||||
activeOpacity={0.75}
|
||||
>
|
||||
{isGroupChat ? (
|
||||
<>
|
||||
<View style={styles.titleLeading}>
|
||||
<Avatar
|
||||
source={groupInfo?.avatar || null}
|
||||
size={36}
|
||||
name={groupInfo?.name || '群'}
|
||||
style={styles.groupAvatar}
|
||||
/>
|
||||
</View>
|
||||
<View style={styles.titleContent}>
|
||||
<Text style={styles.headerName} numberOfLines={1}>{getDisplayTitle()}</Text>
|
||||
<View style={styles.subtitleRow}>
|
||||
<Text style={styles.memberCount}>{groupInfo?.member_count || 0}位成员</Text>
|
||||
{typingHint && (
|
||||
<Text style={styles.typingHint} numberOfLines={1}>{typingHint}</Text>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Avatar
|
||||
source={otherUser?.avatar || null}
|
||||
size={36}
|
||||
name={otherUser?.nickname || ''}
|
||||
/>
|
||||
<View style={styles.titleContent}>
|
||||
<Text style={styles.headerName} numberOfLines={1}>{getDisplayTitle()}</Text>
|
||||
<View style={styles.titleContent}>
|
||||
<Text style={styles.headerName} numberOfLines={1}>{getDisplayTitle()}</Text>
|
||||
{isGroupChat ? (
|
||||
<View style={styles.subtitleRow}>
|
||||
<Text style={styles.memberCount}>{groupInfo?.member_count || 0}位成员</Text>
|
||||
{typingHint && (
|
||||
<View style={styles.subtitleRow}>
|
||||
<Text style={styles.typingHint} numberOfLines={1}>{typingHint}</Text>
|
||||
</View>
|
||||
<Text style={styles.typingHint} numberOfLines={1}>{typingHint}</Text>
|
||||
)}
|
||||
</View>
|
||||
</>
|
||||
)}
|
||||
) : (
|
||||
typingHint && (
|
||||
<View style={styles.subtitleRow}>
|
||||
<Text style={styles.typingHint} numberOfLines={1}>{typingHint}</Text>
|
||||
</View>
|
||||
)
|
||||
)}
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
|
||||
|
||||
{/* 大屏幕 + 群聊时显示群信息按钮,否则显示三点菜单 */}
|
||||
{isWideScreen && isGroupChat && onGroupInfoPress ? (
|
||||
<TouchableOpacity
|
||||
|
||||
@@ -74,7 +74,9 @@ export const EmojiPanel: React.FC<EmojiPanelProps> = ({
|
||||
...baseStyles,
|
||||
emojiContainer: {
|
||||
flex: 1,
|
||||
padding: spacing.sm,
|
||||
paddingTop: spacing.sm,
|
||||
paddingHorizontal: spacing.sm,
|
||||
backgroundColor: baseStyles.panelContainer.backgroundColor,
|
||||
},
|
||||
emojiItem: {
|
||||
width: 40,
|
||||
@@ -281,21 +283,19 @@ export const EmojiPanel: React.FC<EmojiPanelProps> = ({
|
||||
);
|
||||
};
|
||||
|
||||
// 渲染管理按钮(表情面板第一位)- 现代扁平化设计
|
||||
// 渲染管理按钮(表情面板第一位)- 填满格子
|
||||
const renderManageButton = () => (
|
||||
<TouchableOpacity
|
||||
key="manage-button"
|
||||
style={[styles.stickerItem, stickerButtonStyles.manageButton]}
|
||||
style={[styles.stickerItem, { alignItems: 'center', justifyContent: 'center' }]}
|
||||
onPress={handleOpenManage}
|
||||
activeOpacity={0.7}
|
||||
>
|
||||
<View style={stickerButtonStyles.iconContainer}>
|
||||
<MaterialCommunityIcons
|
||||
name="cog-outline"
|
||||
size={28}
|
||||
color={colors.chat.textSecondary}
|
||||
/>
|
||||
</View>
|
||||
<MaterialCommunityIcons
|
||||
name="cog-outline"
|
||||
size={32}
|
||||
color={colors.chat.textSecondary}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
|
||||
@@ -561,24 +561,28 @@ export const EmojiPanel: React.FC<EmojiPanelProps> = ({
|
||||
{activeTab === 'emoji' ? renderEmojiPanel() : renderStickerPanel()}
|
||||
</View>
|
||||
|
||||
{/* 底部 Tab 栏 - 现代胶囊式设计 */}
|
||||
{/* 底部 Tab 栏 - QQ/微信风格横向导航 */}
|
||||
<View style={styles.panelTabBar}>
|
||||
<View style={styles.panelTabContainer}>
|
||||
<View style={styles.panelTabScroll}>
|
||||
<TouchableOpacity
|
||||
style={[styles.panelTab, activeTab === 'emoji' && styles.panelTabActive]}
|
||||
style={[styles.panelTabItem, activeTab === 'emoji' && styles.panelTabItemActive]}
|
||||
onPress={() => setActiveTab('emoji')}
|
||||
activeOpacity={0.8}
|
||||
>
|
||||
<Text style={[styles.panelTabEmoji, activeTab === 'emoji' ? { opacity: 1 } : {}]}>😊</Text>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity
|
||||
style={[styles.panelTab, activeTab === 'sticker' && styles.panelTabActive]}
|
||||
onPress={() => setActiveTab('sticker')}
|
||||
activeOpacity={0.8}
|
||||
>
|
||||
<MaterialCommunityIcons
|
||||
name="emoticon-happy-outline"
|
||||
size={22}
|
||||
color={activeTab === 'emoji' ? colors.primary.main : colors.chat.textSecondary}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity
|
||||
style={[styles.panelTabItem, activeTab === 'sticker' && styles.panelTabItemActive]}
|
||||
onPress={() => setActiveTab('sticker')}
|
||||
activeOpacity={0.8}
|
||||
>
|
||||
<MaterialCommunityIcons
|
||||
name="image-multiple-outline"
|
||||
size={22}
|
||||
color={activeTab === 'sticker' ? colors.primary.main : colors.chat.textSecondary}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
|
||||
@@ -105,6 +105,12 @@ export function createChatScreenStyles(colors: AppColors, dynamicStyles?: {
|
||||
backgroundColor: cardBgColor,
|
||||
height: 56,
|
||||
},
|
||||
headerLeft: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
gap: spacing.xs,
|
||||
zIndex: 2,
|
||||
},
|
||||
backButton: {
|
||||
width: 40,
|
||||
height: 40,
|
||||
@@ -114,27 +120,21 @@ export function createChatScreenStyles(colors: AppColors, dynamicStyles?: {
|
||||
padding: 0,
|
||||
},
|
||||
headerCenter: {
|
||||
flex: 1,
|
||||
position: 'absolute',
|
||||
left: 0,
|
||||
right: 0,
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
alignItems: 'center',
|
||||
marginHorizontal: spacing.sm,
|
||||
justifyContent: 'center',
|
||||
paddingHorizontal: 50,
|
||||
},
|
||||
titleRow: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
gap: spacing.sm,
|
||||
paddingHorizontal: spacing.sm,
|
||||
paddingVertical: spacing.xs,
|
||||
borderRadius: 8,
|
||||
},
|
||||
titleLeading: {
|
||||
width: 36,
|
||||
height: 36,
|
||||
borderRadius: 10,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
backgroundColor: panelBgColor,
|
||||
overflow: 'hidden',
|
||||
},
|
||||
groupAvatar: {
|
||||
width: 36,
|
||||
height: 36,
|
||||
@@ -429,17 +429,17 @@ export function createChatScreenStyles(colors: AppColors, dynamicStyles?: {
|
||||
|
||||
// 输入框区域 - 与列表背景同色底栏,顶部细分隔
|
||||
inputWrapper: {
|
||||
backgroundColor: screenBgColor,
|
||||
backgroundColor: cardBgColor,
|
||||
borderTopWidth: StyleSheet.hairlineWidth,
|
||||
borderTopColor: colors.chat.border,
|
||||
borderTopColor: colors.chat.borderHairline,
|
||||
},
|
||||
inputContainer: {
|
||||
backgroundColor: 'transparent',
|
||||
borderWidth: 0,
|
||||
borderRadius: 0,
|
||||
marginHorizontal: 10,
|
||||
marginBottom: 10,
|
||||
marginTop: 2,
|
||||
marginBottom: 6,
|
||||
marginTop: 6,
|
||||
paddingHorizontal: spacing.sm,
|
||||
paddingVertical: spacing.sm,
|
||||
shadowColor: 'transparent',
|
||||
@@ -451,7 +451,7 @@ export function createChatScreenStyles(colors: AppColors, dynamicStyles?: {
|
||||
inputInner: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
backgroundColor: inputBgColor,
|
||||
backgroundColor: colors.chat.surfaceRaised,
|
||||
borderRadius: 20,
|
||||
paddingHorizontal: spacing.xs,
|
||||
paddingVertical: 4,
|
||||
@@ -464,7 +464,7 @@ export function createChatScreenStyles(colors: AppColors, dynamicStyles?: {
|
||||
elevation: 0,
|
||||
},
|
||||
inputInnerMuted: {
|
||||
backgroundColor: colors.chat.borderHairline,
|
||||
backgroundColor: colors.chat.surfaceMuted,
|
||||
opacity: 0.7,
|
||||
},
|
||||
|
||||
@@ -585,14 +585,9 @@ export function createChatScreenStyles(colors: AppColors, dynamicStyles?: {
|
||||
|
||||
// 面板
|
||||
panelWrapper: {
|
||||
backgroundColor: panelBgColor,
|
||||
borderTopWidth: 1,
|
||||
borderTopColor: colors.chat.border,
|
||||
shadowColor: colors.chat.shadow,
|
||||
shadowOffset: { width: 0, height: -2 },
|
||||
shadowOpacity: 0.1,
|
||||
shadowRadius: 8,
|
||||
elevation: 8,
|
||||
backgroundColor: cardBgColor,
|
||||
borderTopWidth: StyleSheet.hairlineWidth,
|
||||
borderTopColor: colors.chat.borderHairline,
|
||||
},
|
||||
// 表情面板包装器 - 底部 tab 栏是白色,安全区域也用白色填充
|
||||
emojiPanelWrapper: {
|
||||
@@ -600,7 +595,11 @@ export function createChatScreenStyles(colors: AppColors, dynamicStyles?: {
|
||||
},
|
||||
panelContainer: {
|
||||
flex: 1,
|
||||
backgroundColor: panelBgColor,
|
||||
backgroundColor: cardBgColor,
|
||||
},
|
||||
morePanelBackground: {
|
||||
flex: 1,
|
||||
backgroundColor: cardBgColor,
|
||||
},
|
||||
|
||||
// 表情面板
|
||||
@@ -611,6 +610,7 @@ export function createChatScreenStyles(colors: AppColors, dynamicStyles?: {
|
||||
paddingTop: spacing.md,
|
||||
paddingBottom: spacing.xl,
|
||||
alignItems: 'flex-start',
|
||||
backgroundColor: cardBgColor,
|
||||
},
|
||||
emojiItem: {
|
||||
width: '12.5%',
|
||||
@@ -636,55 +636,41 @@ export function createChatScreenStyles(colors: AppColors, dynamicStyles?: {
|
||||
flex: 1,
|
||||
},
|
||||
|
||||
// Tab 栏 - 现代胶囊式设计
|
||||
// Tab 栏 - 底部横向导航条(QQ/微信风格)
|
||||
panelTabBar: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
paddingHorizontal: spacing.md,
|
||||
paddingVertical: spacing.sm + 2,
|
||||
paddingHorizontal: 0,
|
||||
paddingVertical: 0,
|
||||
backgroundColor: cardBgColor,
|
||||
borderTopWidth: StyleSheet.hairlineWidth,
|
||||
borderTopColor: colors.chat.borderHairline,
|
||||
backgroundColor: cardBgColor,
|
||||
},
|
||||
panelTabContainer: {
|
||||
panelTabScroll: {
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
backgroundColor: panelBgColor,
|
||||
borderRadius: 12,
|
||||
padding: 3,
|
||||
paddingHorizontal: spacing.sm,
|
||||
gap: spacing.sm,
|
||||
},
|
||||
panelTab: {
|
||||
width: 42,
|
||||
height: 36,
|
||||
panelTabItem: {
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
borderRadius: 10,
|
||||
width: 48,
|
||||
height: 44,
|
||||
borderBottomWidth: 2.5,
|
||||
borderBottomColor: 'transparent',
|
||||
},
|
||||
panelTabActive: {
|
||||
backgroundColor: cardBgColor,
|
||||
shadowColor: colors.chat.shadow,
|
||||
shadowOffset: { width: 0, height: 1 },
|
||||
shadowOpacity: 0.08,
|
||||
shadowRadius: 2,
|
||||
elevation: 1,
|
||||
},
|
||||
panelTabEmoji: {
|
||||
fontSize: 22,
|
||||
},
|
||||
panelTabDivider: {
|
||||
width: 1,
|
||||
height: 20,
|
||||
backgroundColor: colors.chat.border,
|
||||
marginHorizontal: spacing.sm,
|
||||
panelTabItemActive: {
|
||||
borderBottomColor: colors.primary.main,
|
||||
},
|
||||
panelCloseButton: {
|
||||
width: 36,
|
||||
height: 36,
|
||||
width: 44,
|
||||
height: 44,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
borderRadius: 10,
|
||||
backgroundColor: panelBgColor,
|
||||
marginRight: spacing.sm,
|
||||
},
|
||||
|
||||
// 自定义表情面板
|
||||
@@ -692,12 +678,14 @@ export function createChatScreenStyles(colors: AppColors, dynamicStyles?: {
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
backgroundColor: cardBgColor,
|
||||
},
|
||||
stickerEmptyContainer: {
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
padding: spacing.xl,
|
||||
backgroundColor: cardBgColor,
|
||||
},
|
||||
stickerEmptyText: {
|
||||
fontSize: 16,
|
||||
@@ -715,6 +703,7 @@ export function createChatScreenStyles(colors: AppColors, dynamicStyles?: {
|
||||
paddingHorizontal: spacing.md,
|
||||
paddingTop: spacing.md,
|
||||
paddingBottom: spacing.xl,
|
||||
backgroundColor: cardBgColor,
|
||||
},
|
||||
stickerItem: {
|
||||
width: (SCREEN_WIDTH - spacing.md * 2) / 4 - 8,
|
||||
@@ -734,9 +723,10 @@ export function createChatScreenStyles(colors: AppColors, dynamicStyles?: {
|
||||
flexDirection: 'row',
|
||||
flexWrap: 'wrap',
|
||||
paddingHorizontal: spacing.lg + 4,
|
||||
paddingTop: spacing.lg,
|
||||
paddingTop: spacing.md,
|
||||
paddingBottom: spacing.xl,
|
||||
justifyContent: 'flex-start',
|
||||
backgroundColor: cardBgColor,
|
||||
},
|
||||
moreItem: {
|
||||
width: (SCREEN_WIDTH - spacing.lg * 2 - 8) / 4,
|
||||
@@ -779,12 +769,6 @@ export function createChatScreenStyles(colors: AppColors, dynamicStyles?: {
|
||||
fontWeight: '500',
|
||||
letterSpacing: 0.3,
|
||||
},
|
||||
// MorePanel 整体背景
|
||||
morePanelBackground: {
|
||||
flex: 1,
|
||||
backgroundColor: panelBgColor,
|
||||
},
|
||||
|
||||
// @成员选择浮层 - 绝对定位浮在输入框上方
|
||||
mentionPanelFloat: {
|
||||
position: 'absolute',
|
||||
|
||||
@@ -524,9 +524,6 @@ export const AboutScreen: React.FC = () => {
|
||||
>
|
||||
<View style={styles.content}>
|
||||
<View style={styles.header}>
|
||||
<View style={styles.logoContainer}>
|
||||
<Text style={styles.logoText}>萝卜</Text>
|
||||
</View>
|
||||
<Text style={styles.appName}>{APP_NAME}</Text>
|
||||
<Text style={styles.appSlogan}>{APP_SLOGAN}</Text>
|
||||
<Text style={styles.versionText}>版本 {APP_VERSION}</Text>
|
||||
|
||||
@@ -52,12 +52,8 @@ const PRIVACY_SECTIONS = [
|
||||
1.2 我们在您使用服务时收集的信息
|
||||
• 设备信息:包括设备型号、操作系统版本、设备设置、唯一设备标识符(如IMEI、Android ID、IDFA、OAID、MAC地址等)、IP地址等
|
||||
• 日志信息:IP地址、访问时间、浏览记录、操作日志等
|
||||
• 位置信息:为了向您提供"校园圈"、"附近的人"等基于位置的服务,经您授权,我们会收集您的精确地理位置信息。该信息属于敏感个人信息,拒绝提供仅会影响相关功能,不影响其他服务的使用
|
||||
• 相机/相册权限:用于发布动态、更换头像。我们仅在您主动触发相关功能时申请权限,且不会在后台持续收集
|
||||
• 通讯录权限:经您授权后用于查找好友
|
||||
|
||||
1.3 从第三方获取的信息
|
||||
如您使用第三方账号登录功能(具体以应用实际提供的登录方式为准),我们会从第三方获取您授权共享的账号信息(如昵称、头像等)。`,
|
||||
• 通讯录权限:经您授权后用于查找好友`,
|
||||
},
|
||||
{
|
||||
title: '二、我们如何使用信息',
|
||||
|
||||
@@ -18,10 +18,10 @@ import type { PrivacySettingsDTO, VisibilityLevel } from '../../types/dto';
|
||||
|
||||
const CONTENT_MAX_WIDTH = 720;
|
||||
|
||||
const VISIBILITY_OPTIONS: { value: VisibilityLevel; label: string; description: string }[] = [
|
||||
{ value: 'everyone', label: '所有人可见', description: '任何人都可查看' },
|
||||
{ value: 'following', label: '仅关注的人可见', description: '只有你关注的人可查看' },
|
||||
{ value: 'self', label: '仅自己可见', description: '只有自己可查看' },
|
||||
const VISIBILITY_OPTIONS: { value: VisibilityLevel; label: string }[] = [
|
||||
{ value: 'everyone', label: '所有人可见' },
|
||||
{ value: 'following', label: '仅关注的人可见' },
|
||||
{ value: 'self', label: '仅自己可见' },
|
||||
];
|
||||
|
||||
const PRIVACY_ITEMS: { key: keyof PrivacySettingsDTO; title: string; description: string }[] = [
|
||||
@@ -65,55 +65,78 @@ function createPrivacySettingsStyles(colors: AppColors) {
|
||||
textTransform: 'uppercase',
|
||||
letterSpacing: 0.5,
|
||||
},
|
||||
// 隐私设置项 - 扁平化卡片风格
|
||||
// 扁平式布局:整行横向排列,底部边框分隔
|
||||
item: {
|
||||
backgroundColor: colors.background.default,
|
||||
borderRadius: 14,
|
||||
padding: spacing.lg,
|
||||
marginHorizontal: spacing['2xl'],
|
||||
marginBottom: spacing.md,
|
||||
},
|
||||
itemHeader: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
marginBottom: spacing.sm,
|
||||
paddingVertical: spacing.lg,
|
||||
marginHorizontal: spacing['2xl'],
|
||||
borderBottomWidth: StyleSheet.hairlineWidth,
|
||||
borderBottomColor: colors.divider,
|
||||
},
|
||||
itemLast: {
|
||||
borderBottomWidth: 0,
|
||||
},
|
||||
itemTextWrap: {
|
||||
flex: 1,
|
||||
marginRight: spacing.md,
|
||||
},
|
||||
itemTitle: {
|
||||
fontWeight: '600',
|
||||
fontSize: fontSizes.md,
|
||||
color: colors.text.primary,
|
||||
marginBottom: spacing.xs,
|
||||
},
|
||||
itemDescription: {
|
||||
fontSize: fontSizes.sm,
|
||||
color: colors.text.secondary,
|
||||
marginBottom: spacing.md,
|
||||
},
|
||||
optionsRow: {
|
||||
// 无背景色的选择框,紧贴右侧
|
||||
selectBox: {
|
||||
flexDirection: 'row',
|
||||
flexWrap: 'wrap',
|
||||
gap: spacing.sm,
|
||||
alignItems: 'center',
|
||||
paddingVertical: spacing.xs,
|
||||
},
|
||||
// 选项按钮 - 扁平化风格
|
||||
optionButton: {
|
||||
selectBoxText: {
|
||||
fontSize: fontSizes.sm,
|
||||
color: colors.text.secondary,
|
||||
marginRight: spacing.xs,
|
||||
},
|
||||
// 原位展开的下拉菜单
|
||||
dropdownMenu: {
|
||||
position: 'absolute',
|
||||
right: 0,
|
||||
top: '100%',
|
||||
marginTop: 4,
|
||||
backgroundColor: colors.background.paper,
|
||||
borderRadius: borderRadius.md,
|
||||
borderWidth: StyleSheet.hairlineWidth,
|
||||
borderColor: colors.divider,
|
||||
shadowColor: '#000',
|
||||
shadowOffset: { width: 0, height: 2 },
|
||||
shadowOpacity: 0.1,
|
||||
shadowRadius: 4,
|
||||
elevation: 4,
|
||||
zIndex: 100,
|
||||
minWidth: 160,
|
||||
},
|
||||
dropdownOption: {
|
||||
paddingHorizontal: spacing.md,
|
||||
paddingVertical: spacing.sm,
|
||||
borderRadius: borderRadius.full,
|
||||
borderWidth: 1,
|
||||
borderColor: colors.divider,
|
||||
backgroundColor: colors.background.paper,
|
||||
borderBottomWidth: StyleSheet.hairlineWidth,
|
||||
borderBottomColor: colors.divider,
|
||||
},
|
||||
optionButtonActive: {
|
||||
backgroundColor: colors.primary.main,
|
||||
borderColor: colors.primary.main,
|
||||
dropdownOptionLast: {
|
||||
borderBottomWidth: 0,
|
||||
},
|
||||
optionText: {
|
||||
fontSize: 13,
|
||||
dropdownOptionText: {
|
||||
fontSize: fontSizes.sm,
|
||||
color: colors.text.primary,
|
||||
},
|
||||
optionTextActive: {
|
||||
color: colors.text.inverse,
|
||||
fontWeight: '500',
|
||||
dropdownOptionTextActive: {
|
||||
color: colors.primary.main,
|
||||
fontWeight: '600',
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -127,6 +150,7 @@ export const PrivacySettingsScreen: React.FC = () => {
|
||||
const [settings, setSettings] = useState<PrivacySettingsDTO | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [saving, setSaving] = useState(false);
|
||||
const [openPicker, setOpenPicker] = useState<keyof PrivacySettingsDTO | null>(null);
|
||||
const responsivePadding = useResponsiveSpacing({ xs: 8, sm: 12, md: 16, lg: 24, xl: 32 });
|
||||
|
||||
const scrollBottomInset = isMobile ? 64 + 24 + insets.bottom + spacing.md : spacing.md;
|
||||
@@ -150,10 +174,10 @@ export const PrivacySettingsScreen: React.FC = () => {
|
||||
|
||||
const updateSetting = useCallback(async (key: keyof PrivacySettingsDTO, value: VisibilityLevel) => {
|
||||
if (!settings) return;
|
||||
|
||||
|
||||
const newSettings = { ...settings, [key]: value };
|
||||
setSettings(newSettings);
|
||||
|
||||
|
||||
setSaving(true);
|
||||
try {
|
||||
const ok = await authService.updatePrivacySettings({
|
||||
@@ -171,6 +195,9 @@ export const PrivacySettingsScreen: React.FC = () => {
|
||||
}
|
||||
}, [settings]);
|
||||
|
||||
const getLabel = (value?: VisibilityLevel) =>
|
||||
VISIBILITY_OPTIONS.find((o) => o.value === value)?.label ?? '请选择';
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<SafeAreaView style={styles.container} edges={['bottom']}>
|
||||
@@ -186,42 +213,75 @@ export const PrivacySettingsScreen: React.FC = () => {
|
||||
<ScrollView contentContainerStyle={[styles.scrollContent, { paddingBottom: scrollBottomInset, paddingHorizontal: responsivePadding }]}>
|
||||
<View style={styles.content}>
|
||||
<View style={styles.section}>
|
||||
<View style={styles.sectionHeader}>
|
||||
<Text variant="caption" style={styles.sectionTitle}>
|
||||
隐私设置
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
{PRIVACY_ITEMS.map((item) => (
|
||||
<View key={item.key} style={styles.item}>
|
||||
<Text variant="body" style={styles.itemTitle}>
|
||||
{item.title}
|
||||
</Text>
|
||||
<Text variant="caption" color={colors.text.secondary} style={styles.itemDescription}>
|
||||
{item.description}
|
||||
</Text>
|
||||
<View style={styles.optionsRow}>
|
||||
{VISIBILITY_OPTIONS.map((option) => {
|
||||
const isActive = settings?.[item.key] === option.value;
|
||||
return (
|
||||
<TouchableOpacity
|
||||
key={option.value}
|
||||
style={[styles.optionButton, isActive && styles.optionButtonActive]}
|
||||
onPress={() => updateSetting(item.key, option.value)}
|
||||
activeOpacity={0.7}
|
||||
>
|
||||
<Text
|
||||
variant="caption"
|
||||
style={[styles.optionText, isActive ? styles.optionTextActive : {}]}
|
||||
>
|
||||
{option.label}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
})}
|
||||
{PRIVACY_ITEMS.map((item, index) => {
|
||||
const isOpen = openPicker === item.key;
|
||||
return (
|
||||
<View
|
||||
key={item.key}
|
||||
style={[
|
||||
styles.item,
|
||||
index === PRIVACY_ITEMS.length - 1 && styles.itemLast,
|
||||
]}
|
||||
>
|
||||
<View style={styles.itemTextWrap}>
|
||||
<Text variant="body" style={styles.itemTitle}>
|
||||
{item.title}
|
||||
</Text>
|
||||
<Text variant="caption" color={colors.text.secondary} style={styles.itemDescription}>
|
||||
{item.description}
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
<TouchableOpacity
|
||||
style={styles.selectBox}
|
||||
onPress={() => setOpenPicker(isOpen ? null : item.key)}
|
||||
activeOpacity={0.7}
|
||||
>
|
||||
<Text style={styles.selectBoxText}>
|
||||
{getLabel(settings?.[item.key])}
|
||||
</Text>
|
||||
<MaterialCommunityIcons
|
||||
name={isOpen ? 'chevron-up' : 'chevron-down'}
|
||||
size={18}
|
||||
color={colors.text.secondary}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
|
||||
{/* 原位展开的下拉选项 */}
|
||||
{isOpen && (
|
||||
<View style={styles.dropdownMenu}>
|
||||
{VISIBILITY_OPTIONS.map((option, optIndex) => {
|
||||
const isActive = settings?.[item.key] === option.value;
|
||||
return (
|
||||
<TouchableOpacity
|
||||
key={option.value}
|
||||
style={[
|
||||
styles.dropdownOption,
|
||||
optIndex === VISIBILITY_OPTIONS.length - 1 && styles.dropdownOptionLast,
|
||||
]}
|
||||
onPress={() => {
|
||||
updateSetting(item.key, option.value);
|
||||
setOpenPicker(null);
|
||||
}}
|
||||
activeOpacity={0.7}
|
||||
>
|
||||
<Text
|
||||
variant="body"
|
||||
style={[
|
||||
styles.dropdownOptionText,
|
||||
isActive ? styles.dropdownOptionTextActive : undefined,
|
||||
]}
|
||||
>
|
||||
{option.label}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
})}
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
))}
|
||||
);
|
||||
})}
|
||||
</View>
|
||||
</View>
|
||||
</ScrollView>
|
||||
|
||||
@@ -466,9 +466,9 @@ export function CreateTradeScreen({ onClose, onSuccess, editItem }: CreateTradeS
|
||||
>
|
||||
{/* 标题区域 */}
|
||||
<View style={styles.titleSection}>
|
||||
<Text style={styles.title}>{isEdit ? '编辑商品' : '发布商品'}</Text>
|
||||
<Text style={styles.title}>{isEdit ? '编辑' + (tradeType === 'buy' ? '收购' : '商品') : '发布' + (tradeType === 'buy' ? '收购' : '商品')}</Text>
|
||||
<View style={styles.underline} />
|
||||
<Text style={styles.subtitle}>填写商品信息,让更多人看到</Text>
|
||||
<Text style={styles.subtitle}>{tradeType === 'buy' ? '填写收购需求,让卖家主动联系你' : '填写商品信息,让更多人看到'}</Text>
|
||||
</View>
|
||||
|
||||
{/* 交易类型 */}
|
||||
@@ -520,7 +520,7 @@ export function CreateTradeScreen({ onClose, onSuccess, editItem }: CreateTradeS
|
||||
style={styles.input}
|
||||
value={title}
|
||||
onChangeText={setTitle}
|
||||
placeholder="描述你的商品"
|
||||
placeholder={tradeType === 'buy' ? '描述你想收购的商品' : '描述你的商品'}
|
||||
placeholderTextColor={colors.text.hint}
|
||||
maxLength={100}
|
||||
cursorColor={colors.primary.main}
|
||||
@@ -541,7 +541,7 @@ export function CreateTradeScreen({ onClose, onSuccess, editItem }: CreateTradeS
|
||||
style={[styles.input, styles.textArea]}
|
||||
value={content}
|
||||
onChangeText={setContent}
|
||||
placeholder="详细描述商品情况..."
|
||||
placeholder={tradeType === 'buy' ? '详细描述收购需求、预算、成色要求等...' : '详细描述商品情况...'}
|
||||
placeholderTextColor={colors.text.hint}
|
||||
multiline
|
||||
maxLength={2000}
|
||||
|
||||
@@ -120,6 +120,11 @@ function createStyles(colors: AppColors) {
|
||||
fontWeight: '700',
|
||||
color: '#FF4D4F',
|
||||
},
|
||||
priceSymbol: {
|
||||
fontSize: fontSizes.md,
|
||||
fontWeight: '700',
|
||||
color: '#FF4D4F',
|
||||
},
|
||||
|
||||
titleRow: {
|
||||
flexDirection: 'row',
|
||||
@@ -330,6 +335,17 @@ function createStyles(colors: AppColors) {
|
||||
color: colors.text.primary,
|
||||
fontWeight: '600',
|
||||
},
|
||||
contentAttrs: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'flex-end',
|
||||
alignItems: 'center',
|
||||
gap: spacing.md,
|
||||
flexWrap: 'wrap',
|
||||
},
|
||||
contentAttrText: {
|
||||
fontSize: fontSizes.sm,
|
||||
color: colors.text.secondary,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -496,7 +512,7 @@ export function TradeDetailScreen({ tradeId }: TradeDetailScreenProps) {
|
||||
</View>
|
||||
{item.price != null ? (
|
||||
<Text style={styles.priceInHeader}>
|
||||
价格:¥{item.price % 1 === 0 ? item.price.toFixed(0) : item.price.toFixed(2)}
|
||||
价格:<Text style={styles.priceSymbol}>¥</Text>{item.price % 1 === 0 ? item.price.toFixed(0) : item.price.toFixed(2)}
|
||||
</Text>
|
||||
) : (
|
||||
<Text style={styles.priceInHeader}>价格:面议</Text>
|
||||
@@ -509,41 +525,12 @@ export function TradeDetailScreen({ tradeId }: TradeDetailScreenProps) {
|
||||
{/* 标题 */}
|
||||
<View style={styles.titleRow}>
|
||||
<Text style={styles.title} numberOfLines={2}>{item.title}</Text>
|
||||
{item.condition && item.trade_type === 'sell' && (
|
||||
<View style={{ flexShrink: 0, marginTop: 7, marginLeft: 'auto' }}>
|
||||
<View style={styles.conditionBadge}>
|
||||
<Text style={styles.conditionBadgeText} numberOfLines={1}>
|
||||
{TRADE_CONDITION_MAP[item.condition as keyof typeof TRADE_CONDITION_MAP] || item.condition}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
|
||||
{/* 商品属性 */}
|
||||
<View style={styles.attrGrid}>
|
||||
<View style={styles.attrItem}>
|
||||
<Text style={styles.attrLabel}>品牌</Text>
|
||||
<Text style={styles.attrValue}>{item.brand || '其他'}</Text>
|
||||
</View>
|
||||
<View style={styles.attrItem}>
|
||||
<Text style={styles.attrLabel}>成色</Text>
|
||||
<Text style={styles.attrValue}>
|
||||
{TRADE_CONDITION_MAP[item.condition as keyof typeof TRADE_CONDITION_MAP] || item.condition || '未知'}
|
||||
</Text>
|
||||
</View>
|
||||
<View style={styles.attrItem}>
|
||||
<Text style={styles.attrLabel}>功能状态</Text>
|
||||
<Text style={styles.attrValue}>{item.function_status || '正常使用'}</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* 商品描述 */}
|
||||
{item.content && (
|
||||
<Text style={styles.contentText}>{item.content}</Text>
|
||||
)}
|
||||
|
||||
<View style={styles.divider} />
|
||||
</View>
|
||||
|
||||
{/* 图片放在文字下方 */}
|
||||
@@ -555,11 +542,23 @@ export function TradeDetailScreen({ tradeId }: TradeDetailScreenProps) {
|
||||
source={{ uri: img.url || img.preview_url_large || img.preview_url || '' }}
|
||||
style={styles.image}
|
||||
/>
|
||||
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
)}
|
||||
|
||||
{/* 内容区右下角属性文字(仅出售商品显示) */}
|
||||
{item.trade_type === 'sell' && (
|
||||
<View style={{ paddingHorizontal: spacing.md, paddingBottom: spacing.md }}>
|
||||
<View style={styles.contentAttrs}>
|
||||
<Text style={styles.contentAttrText}>品牌:{item.brand || '其他'}</Text>
|
||||
<Text style={styles.contentAttrText}>
|
||||
成色:{TRADE_CONDITION_MAP[item.condition as keyof typeof TRADE_CONDITION_MAP] || item.condition || '未知'}
|
||||
</Text>
|
||||
<Text style={styles.contentAttrText}>功能状态:{item.function_status || '正常使用'}</Text>
|
||||
</View>
|
||||
</View>
|
||||
)}
|
||||
</ScrollView>
|
||||
|
||||
{/* 底部操作栏 */}
|
||||
|
||||
Reference in New Issue
Block a user