Files
yunhaifinance/frontend/src/components/ContactManager.tsx
T

210 lines
7.4 KiB
TypeScript
Raw Normal View History

import React, { useState, useEffect } from 'react'
import { Table, Button, Modal, Form, Input, Switch, message, Space, Tag, Popconfirm } from 'antd'
import { PlusOutlined, EditOutlined, DeleteOutlined, PhoneOutlined, UserOutlined } from '@ant-design/icons'
import type { ColumnsType } from 'antd/es/table'
import { useTranslation } from 'react-i18next'
interface Contact {
id: number
name: string
name_zh?: string
position?: string
department?: string
is_primary: boolean
phone?: string
mobile?: string
wechat?: string
whatsapp?: string
line_id?: string
notes?: string
}
interface ContactManagerProps {
companyType: 'customer' | 'supplier' | 'subcontractor'
companyId: number
companyName: string
onContactsUpdated?: () => void
}
const ContactManager: React.FC<ContactManagerProps> = ({
companyType,
companyId,
companyName,
onContactsUpdated
}) => {
const { t } = useTranslation()
const [contacts, setContacts] = useState<Contact[]>([])
const [loading, setLoading] = useState(false)
const [modalVisible, setModalVisible] = useState(false)
const [editingContact, setEditingContact] = useState<Contact | null>(null)
const [form] = Form.useForm()
const fetchContacts = async () => {
setLoading(true)
try {
const response = await fetch(`/api/${companyType}s/${companyId}/contacts`)
const data = await response.json()
setContacts(data.contacts || [])
} catch (error) {
console.error('获取联系人失败:', error)
} finally {
setLoading(false)
}
}
useEffect(() => {
if (companyId) {
fetchContacts()
}
}, [companyId, companyType])
const columns: ColumnsType<Contact> = [
{
title: t('contact.name'),
dataIndex: 'name',
key: 'name',
render: (text, record) => (
<div>
<div style={{ fontWeight: 'bold' }}>{text}</div>
{record.position && <div style={{ fontSize: '12px', color: '#666' }}>{record.position}</div>}
</div>
)
},
{
title: t('contact.contactInfo'),
key: 'contact',
render: (_, record) => (
<Space direction="vertical" size={2}>
{record.mobile && <div><PhoneOutlined style={{ marginRight: 4 }} />{record.mobile}</div>}
{record.phone && <div style={{ fontSize: '12px', color: '#666' }}>电话: {record.phone}</div>}
{record.wechat && <div style={{ fontSize: '12px', color: '#666' }}>微信: {record.wechat}</div>}
</Space>
)
},
{
title: t('contact.status'),
dataIndex: 'is_primary',
key: 'is_primary',
width: 100,
render: (isPrimary) => (
<Tag color={isPrimary ? 'green' : 'blue'}>
{isPrimary ? t('contact.primary') : t('contact.secondary')}
</Tag>
)
},
{
title: t('common.actions'),
key: 'actions',
width: 120,
render: (_, record) => (
<Space>
<Button type="text" icon={<EditOutlined />} onClick={() => handleEdit(record)} size="small" />
<Popconfirm title={t('common.confirmDelete')} onConfirm={() => handleDelete(record.id)} okText={t('common.yes')} cancelText={t('common.no')}>
<Button type="text" danger icon={<DeleteOutlined />} size="small" />
</Popconfirm>
</Space>
)
}
]
const handleSubmit = async (values: any) => {
try {
const url = editingContact ? `/api/${companyType}s/${companyId}/contacts/${editingContact.id}` : `/api/${companyType}s/${companyId}/contacts`
const method = editingContact ? 'PUT' : 'POST'
const response = await fetch(url, {
method,
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ ...values, company_type: companyType, company_id: companyId })
})
if (response.ok) {
message.success(editingContact ? t('common.updateSuccess') : t('common.createSuccess'))
setModalVisible(false)
form.resetFields()
setEditingContact(null)
fetchContacts()
onContactsUpdated?.()
}
} catch (error) {
message.error(t('common.operationFailed'))
}
}
const handleEdit = (contact: Contact) => {
setEditingContact(contact)
form.setFieldsValue(contact)
setModalVisible(true)
}
const handleDelete = async (contactId: number) => {
try {
await fetch(`/api/${companyType}s/${companyId}/contacts/${contactId}`, { method: 'DELETE' })
message.success(t('common.deleteSuccess'))
fetchContacts()
onContactsUpdated?.()
} catch (error) {
message.error(t('common.deleteFailed'))
}
}
return (
<div>
<div style={{ marginBottom: 16, display: 'flex', justifyContent: 'space-between' }}>
<div>
<h3>{t('contact.management')}</h3>
<p style={{ color: '#666' }}>{companyName} - {t(`company.${companyType}`)}</p>
</div>
<Button type="primary" icon={<PlusOutlined />} onClick={() => { setEditingContact(null); form.resetFields(); setModalVisible(true) }}>
{t('contact.addContact')}
</Button>
</div>
<Table columns={columns} dataSource={contacts} rowKey="id" loading={loading} pagination={{ pageSize: 10 }} size="middle" />
<Modal
title={editingContact ? t('contact.editContact') : t('contact.addContact')}
open={modalVisible}
onCancel={() => { setModalVisible(false); form.resetFields(); setEditingContact(null) }}
onOk={() => form.submit()}
width={600}
destroyOnClose
>
<Form form={form} layout="vertical" onFinish={handleSubmit} initialValues={{ is_primary: false }}>
<Form.Item name="name" label={t('contact.name')} rules={[{ required: true }]}>
<Input placeholder={t('contact.namePlaceholder')} />
</Form.Item>
<Form.Item name="position" label={t('contact.position')}>
<Input placeholder={t('contact.positionPlaceholder')} />
</Form.Item>
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 16 }}>
<Form.Item name="phone" label={t('contact.phone')}>
<Input placeholder={t('contact.phonePlaceholder')} />
</Form.Item>
<Form.Item name="mobile" label={t('contact.mobile')}>
<Input placeholder={t('contact.mobilePlaceholder')} />
</Form.Item>
</div>
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 16 }}>
<Form.Item name="wechat" label={t('contact.wechat')}>
<Input placeholder={t('contact.wechatPlaceholder')} />
</Form.Item>
<Form.Item name="line_id" label={t('contact.lineId')}>
<Input placeholder={t('contact.lineIdPlaceholder')} />
</Form.Item>
</div>
<Form.Item name="whatsapp" label="WhatsApp">
<Input placeholder="输入WhatsApp号码" />
</Form.Item>
<Form.Item name="is_primary" label={t('contact.primaryContact')} valuePropName="checked">
<Switch />
</Form.Item>
<Form.Item name="notes" label={t('contact.notes')}>
<Input.TextArea rows={3} placeholder={t('contact.notesPlaceholder')} />
</Form.Item>
</Form>
</Modal>
</div>
)
}
export default ContactManager