import { Ionicons } from '@expo/vector-icons';
import { useRouter } from 'expo-router';
import {
ActivityIndicator,
Alert,
Pressable,
ScrollView,
StyleSheet,
Text,
TextInput,
View,
} from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import { useEnterpriseCommunication } from '../../src/hooks';
import { colors } from '../../src/theme';
import type { EnterpriseChatItem, EnterpriseCommunicationTab, PendingTask } from '../../src/types';
/* ===================== 子组件:顶部导航 ===================== */
function Header({ companyName, onBack }: { companyName: string; onBack: () => void }) {
const router = useRouter();
return (
{companyName}
沟通
router.navigate('/talent-recommendation')}
hitSlop={6}
>
发起沟通
);
}
/* ===================== 子组件:Tab 栏 ===================== */
function TabBar({
tabs,
active,
onChange,
}: {
tabs: EnterpriseCommunicationTab[];
active: string;
onChange: (key: string) => void;
}) {
return (
{tabs.map((tab) => {
const isActive = tab.key === active;
return (
onChange(tab.key)}>
{tab.label}
({tab.count})
);
})}
);
}
/* ===================== 子组件:待处理任务 ===================== */
function PendingTasks({
tasks,
onPressTask,
}: {
tasks: PendingTask[];
onPressTask: (task: PendingTask) => void;
}) {
return (
待处理
{tasks.map((task) => {
const token =
task.color === 'action'
? colors.action
: task.color === 'compareOrange'
? colors.compareOrange
: colors.primary;
return (
onPressTask(task)}
>
{task.label}
{task.count}人
{task.hint ? {task.hint} : null}
);
})}
);
}
/* ===================== 子组件:聊天列表项 ===================== */
function ChatRow({ chat }: { chat: EnterpriseChatItem }) {
const router = useRouter();
return (
router.push('/chat/' + chat.id)}>
{chat.avatar}
{chat.name}
{chat.labels.length > 0 ? (
{chat.labels[0]}
) : null}
{chat.time}
{chat.title}
{chat.lastMessage}
{chat.unreadCount > 0 ? (
{chat.unreadCount}
) : null}
);
}
/* ===================== 子组件:搜索栏 ===================== */
function SearchBar({
value,
onChange,
onClear,
}: {
value: string;
onChange: (text: string) => void;
onClear: () => void;
}) {
return (
{value !== '' && (
)}
);
}
/* ===================== 子组件:快捷操作栏 ===================== */
/** 单个快捷操作:有 route 则跳转,无对应页面时由页面层给出「功能开发中」提示 */
type QuickActionItem = { icon: string; label: string; route?: string };
function QuickActions({
actions,
onPressAction,
}: {
actions: QuickActionItem[];
onPressAction: (action: QuickActionItem) => void;
}) {
return (
{actions.map((action) => (
onPressAction(action)}>
{action.label}
))}
);
}
/* ===================== 子组件:空状态 ===================== */
function EmptyState({
dataReference,
onDiscover,
}: {
dataReference: { label: string; value: string }[];
onDiscover: () => void;
}) {
return (
还没有沟通记录
从人才推荐中发现合适的候选人,主动发起沟通吧
去发现人才
数据参考
{dataReference.map((item, index) => (
{item.label}
{item.value}
))}
);
}
/* ===================== 页面 ===================== */
const QUICK_ACTIONS: QuickActionItem[] = [
{ icon: 'mail-outline', label: '批量发邀请', route: '/talent-recommendation' },
{ icon: 'document-text-outline', label: '批量发Offer' },
{ icon: 'calendar-outline', label: '面试评分', route: '/interview-score' },
{ icon: 'notifications-outline', label: '群发通知' },
];
export default function EnterpriseChatScreen() {
const router = useRouter();
const {
communication,
activeTab,
setActiveTab,
keyword,
setKeyword,
chats,
isEmptySearch,
loading,
error,
refresh,
} = useEnterpriseCommunication();
// 快捷操作分发:有目标路由则跳转,无对应页面则提示「功能开发中」
const handleQuickAction = (action: QuickActionItem) => {
if (action.route) {
router.push(action.route);
} else {
Alert.alert('提示', `${action.label}功能开发中,敬请期待`);
}
};
// 待办卡片:优先跳页面,其次切 Tab(接口层给的目标,页面不写死映射)
const handlePressTask = (task: PendingTask) => {
if (task.route) {
router.push(task.route as never);
return;
}
if (task.tabKey) {
setActiveTab(task.tabKey);
setKeyword('');
return;
}
Alert.alert('提示', `${task.label}功能开发中,敬请期待`);
};
if (loading) {
return (
);
}
if (error || !communication) {
return (
router.back()} />
加载失败
重试
);
}
return (
router.back()} />
{communication.hasData ? (
setKeyword('')} />
{chats.length === 0 ? (
{isEmptySearch ? `没有匹配「${keyword.trim()}」的会话` : '这个分类下暂时没有会话'}
) : (
{chats.map((chat, index) => {
const isLast = index === chats.length - 1;
return (
);
})}
)}
) : (
router.navigate('/talent-recommendation')}
/>
)}
);
}
const styles = StyleSheet.create({
container: { flex: 1, backgroundColor: colors.pageBg },
header: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
paddingHorizontal: 12,
paddingVertical: 12,
backgroundColor: colors.white,
},
headerBtn: { width: 40, alignItems: 'center' },
headerCenter: { alignItems: 'center' },
headerCompany: { fontSize: 12, color: colors.textSecondary },
headerTitle: { fontSize: 17, fontWeight: '700', color: colors.textPrimary, marginTop: 2 },
startBtn: {
flexDirection: 'row',
alignItems: 'center',
backgroundColor: colors.primaryBtn,
paddingHorizontal: 10,
paddingVertical: 6,
borderRadius: 16,
},
startBtnText: {
color: colors.white,
fontSize: 12,
fontWeight: '600',
marginLeft: 2,
},
tabBar: {
flexDirection: 'row',
backgroundColor: colors.white,
borderBottomWidth: 1,
borderBottomColor: colors.borderLight,
},
tabItem: {
flex: 1,
alignItems: 'center',
paddingTop: 8,
},
tabLabel: {
fontSize: 13,
color: colors.textSecondary,
paddingBottom: 8,
},
tabLabelActive: {
color: colors.textPrimary,
fontWeight: '600',
},
tabCount: {
fontSize: 12,
color: colors.textHint,
marginLeft: 2,
},
tabCountActive: {
color: colors.primary,
},
tabIndicator: {
height: 2,
width: 20,
borderRadius: 1,
backgroundColor: 'transparent',
marginBottom: -1,
},
tabIndicatorActive: {
backgroundColor: colors.primary,
},
body: {
padding: 16,
paddingBottom: 24,
},
center: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
gap: 12,
},
tip: { fontSize: 14, color: colors.textSecondary },
retryBtn: {
marginTop: 8,
paddingHorizontal: 24,
paddingVertical: 8,
borderRadius: 18,
backgroundColor: colors.primaryBtn,
},
retryText: { color: colors.white, fontSize: 13, fontWeight: '600' },
pendingCard: {
backgroundColor: colors.white,
borderRadius: 12,
padding: 16,
marginBottom: 12,
},
pendingTitle: {
fontSize: 15,
fontWeight: '700',
color: colors.textPrimary,
marginBottom: 12,
},
pendingGrid: {
gap: 12,
},
pendingItem: {
flexDirection: 'row',
alignItems: 'center',
backgroundColor: colors.pageBg,
borderRadius: 10,
padding: 12,
},
pendingIconWrap: {
width: 40,
height: 40,
borderRadius: 10,
alignItems: 'center',
justifyContent: 'center',
marginRight: 12,
},
pendingBody: {
flex: 1,
},
pendingLabelRow: {
flexDirection: 'row',
alignItems: 'center',
},
pendingLabel: {
fontSize: 14,
fontWeight: '600',
color: colors.textPrimary,
},
pendingBadge: {
backgroundColor: colors.actionLight,
borderRadius: 10,
paddingHorizontal: 8,
paddingVertical: 2,
marginLeft: 8,
},
pendingBadgeText: {
fontSize: 12,
fontWeight: '700',
color: colors.actionText,
},
pendingHint: {
fontSize: 12,
color: colors.textSecondary,
marginTop: 2,
},
searchBar: {
flexDirection: 'row',
alignItems: 'center',
backgroundColor: colors.white,
borderRadius: 10,
paddingHorizontal: 12,
paddingVertical: 10,
marginBottom: 12,
},
searchInput: {
flex: 1,
marginLeft: 8,
fontSize: 14,
color: colors.textPrimary,
padding: 0,
},
emptyList: {
alignItems: 'center',
gap: 10,
paddingVertical: 32,
backgroundColor: colors.white,
borderRadius: 14,
},
emptyListText: { fontSize: 13, color: colors.textSecondary },
chatListCard: {
backgroundColor: colors.white,
borderRadius: 12,
overflow: 'hidden',
marginBottom: 12,
},
chatBorder: {
borderBottomWidth: 1,
borderBottomColor: colors.borderLight,
},
chatRow: {
flexDirection: 'row',
alignItems: 'center',
padding: 14,
},
chatAvatar: {
width: 48,
height: 48,
borderRadius: 24,
backgroundColor: colors.primaryLight,
alignItems: 'center',
justifyContent: 'center',
marginRight: 12,
},
chatAvatarText: {
fontSize: 18,
fontWeight: '700',
color: colors.primary,
},
chatBody: {
flex: 1,
},
chatHeader: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
},
chatNameWrap: {
flexDirection: 'row',
alignItems: 'center',
},
chatName: {
fontSize: 15,
fontWeight: '700',
color: colors.textPrimary,
},
chatLabelTag: {
backgroundColor: colors.compareOrange + '20',
borderRadius: 8,
paddingHorizontal: 6,
paddingVertical: 2,
marginLeft: 8,
},
chatLabelTagText: {
fontSize: 11,
color: colors.compareOrange,
fontWeight: '600',
},
chatTime: {
fontSize: 12,
color: colors.textHint,
},
chatTitle: {
fontSize: 13,
color: colors.textSecondary,
marginTop: 2,
},
chatFooter: {
flexDirection: 'row',
alignItems: 'center',
marginTop: 4,
},
chatLastMessage: {
flex: 1,
fontSize: 13,
color: colors.textHint,
},
unreadBadge: {
minWidth: 18,
height: 18,
borderRadius: 9,
backgroundColor: colors.danger,
alignItems: 'center',
justifyContent: 'center',
marginLeft: 8,
},
unreadBadgeText: {
fontSize: 11,
color: colors.white,
fontWeight: '700',
},
quickActionsCard: {
flexDirection: 'row',
backgroundColor: colors.white,
borderRadius: 12,
paddingVertical: 14,
},
quickAction: {
flex: 1,
alignItems: 'center',
},
quickActionText: {
fontSize: 11,
color: colors.textSecondary,
marginTop: 4,
},
emptyCard: {
backgroundColor: colors.white,
borderRadius: 16,
padding: 28,
alignItems: 'center',
},
emptyIconWrap: {
width: 88,
height: 88,
borderRadius: 44,
backgroundColor: colors.primaryLight,
alignItems: 'center',
justifyContent: 'center',
marginBottom: 16,
},
emptyTitle: {
fontSize: 18,
fontWeight: '700',
color: colors.textPrimary,
marginBottom: 8,
},
emptySubtitle: {
fontSize: 13,
color: colors.textSecondary,
textAlign: 'center',
marginBottom: 20,
},
emptyBtn: {
backgroundColor: colors.primaryBtn,
paddingHorizontal: 32,
paddingVertical: 12,
borderRadius: 24,
marginBottom: 24,
},
emptyBtnText: {
color: colors.white,
fontSize: 15,
fontWeight: '600',
},
dataCard: {
width: '100%',
backgroundColor: colors.primaryLight,
borderRadius: 12,
padding: 16,
alignItems: 'flex-start',
},
dataTitle: {
fontSize: 14,
fontWeight: '700',
color: colors.primaryText,
marginBottom: 12,
},
dataRow: {
flexDirection: 'row',
alignItems: 'flex-start',
marginBottom: 10,
},
dataText: {
flex: 1,
fontSize: 13,
color: colors.textPrimary,
marginLeft: 10,
lineHeight: 18,
},
dataValue: {
color: colors.success,
fontWeight: '700',
},
});