|
|
@@ -0,0 +1,218 @@
|
|
|
+import { Card, Descriptions, Button, Space, Tag, Timeline, Form, Input, Select, Modal, message, Divider } from 'antd'
|
|
|
+import { ArrowLeftOutlined, EditOutlined, PlusOutlined } from '@ant-design/icons'
|
|
|
+import { useEffect, useState } from 'react'
|
|
|
+import { useParams, useNavigate } from 'react-router-dom'
|
|
|
+import request from '../../utils/request'
|
|
|
+import { CUSTOMER_STATUS, FOLLOWUP_TYPES } from '../../utils/constants'
|
|
|
+import dayjs from 'dayjs'
|
|
|
+
|
|
|
+const { TextArea } = Input
|
|
|
+const { Option } = Select
|
|
|
+
|
|
|
+const CustomerDetail = () => {
|
|
|
+ const { id } = useParams()
|
|
|
+ const navigate = useNavigate()
|
|
|
+ const [data, setData] = useState(null)
|
|
|
+ const [loading, setLoading] = useState(true)
|
|
|
+ const [followupVisible, setFollowupVisible] = useState(false)
|
|
|
+ const [statusVisible, setStatusVisible] = useState(false)
|
|
|
+ const [form] = Form.useForm()
|
|
|
+
|
|
|
+ useEffect(() => {
|
|
|
+ loadData()
|
|
|
+ }, [id])
|
|
|
+
|
|
|
+ const loadData = async () => {
|
|
|
+ try {
|
|
|
+ const res = await request.get(`/customers/${id}`)
|
|
|
+ setData(res.data)
|
|
|
+ } catch (error) {
|
|
|
+ console.error('加载失败:', error)
|
|
|
+ } finally {
|
|
|
+ setLoading(false)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ const handleAddFollowup = async (values) => {
|
|
|
+ try {
|
|
|
+ await request.post('/customers/followup', {
|
|
|
+ customer_id: id,
|
|
|
+ ...values
|
|
|
+ })
|
|
|
+ message.success('跟进记录添加成功')
|
|
|
+ setFollowupVisible(false)
|
|
|
+ form.resetFields()
|
|
|
+ loadData()
|
|
|
+ } catch (error) {
|
|
|
+ console.error('添加失败:', error)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ const handleUpdateStatus = async (values) => {
|
|
|
+ try {
|
|
|
+ await request.patch(`/customers/${id}/status`, values)
|
|
|
+ message.success('状态更新成功')
|
|
|
+ setStatusVisible(false)
|
|
|
+ loadData()
|
|
|
+ } catch (error) {
|
|
|
+ console.error('更新失败:', error)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (loading || !data) return null
|
|
|
+
|
|
|
+ const { customer, followups, attachments } = data
|
|
|
+
|
|
|
+ return (
|
|
|
+ <div>
|
|
|
+ <Card
|
|
|
+ title={
|
|
|
+ <Space>
|
|
|
+ <Button icon={<ArrowLeftOutlined />} onClick={() => navigate('/customers')} />
|
|
|
+ <span>客户详情</span>
|
|
|
+ </Space>
|
|
|
+ }
|
|
|
+ extra={
|
|
|
+ <Space>
|
|
|
+ <Button icon={<PlusOutlined />} onClick={() => setFollowupVisible(true)}>
|
|
|
+ 添加跟进
|
|
|
+ </Button>
|
|
|
+ <Button icon={<EditOutlined />} onClick={() => setStatusVisible(true)}>
|
|
|
+ 更新状态
|
|
|
+ </Button>
|
|
|
+ </Space>
|
|
|
+ }
|
|
|
+ >
|
|
|
+ <Descriptions bordered column={2}>
|
|
|
+ <Descriptions.Item label="客户名称">{customer.customer_name}</Descriptions.Item>
|
|
|
+ <Descriptions.Item label="状态">
|
|
|
+ <Tag color={CUSTOMER_STATUS[customer.status]?.color}>
|
|
|
+ {CUSTOMER_STATUS[customer.status]?.text}
|
|
|
+ </Tag>
|
|
|
+ </Descriptions.Item>
|
|
|
+ <Descriptions.Item label="行业">{customer.industry}</Descriptions.Item>
|
|
|
+ <Descriptions.Item label="地区">{customer.region}</Descriptions.Item>
|
|
|
+ <Descriptions.Item label="联系人">{customer.contact_person}</Descriptions.Item>
|
|
|
+ <Descriptions.Item label="联系电话">{customer.contact_phone}</Descriptions.Item>
|
|
|
+ <Descriptions.Item label="负责人">{customer.owner_name}</Descriptions.Item>
|
|
|
+ <Descriptions.Item label="来源渠道">{customer.source}</Descriptions.Item>
|
|
|
+ <Descriptions.Item label="报备时间">
|
|
|
+ {dayjs(customer.report_time).format('YYYY-MM-DD HH:mm')}
|
|
|
+ </Descriptions.Item>
|
|
|
+ <Descriptions.Item label="保护期到期">
|
|
|
+ <span style={{ color: customer.is_expired ? '#ff4d4f' : undefined }}>
|
|
|
+ {dayjs(customer.protected_end_date).format('YYYY-MM-DD HH:mm')}
|
|
|
+ </span>
|
|
|
+ </Descriptions.Item>
|
|
|
+ {customer.last_followup && (
|
|
|
+ <Descriptions.Item label="最后跟进">
|
|
|
+ {dayjs(customer.last_followup).format('YYYY-MM-DD HH:mm')}
|
|
|
+ </Descriptions.Item>
|
|
|
+ )}
|
|
|
+ <Descriptions.Item label="需求概况" span={2}>
|
|
|
+ {customer.demand_description}
|
|
|
+ </Descriptions.Item>
|
|
|
+ </Descriptions>
|
|
|
+ </Card>
|
|
|
+
|
|
|
+ <Card title="跟进记录" style={{ marginTop: 16 }}>
|
|
|
+ {followups.length === 0 ? (
|
|
|
+ <div style={{ textAlign: 'center', padding: '40px 0', color: '#999' }}>
|
|
|
+ 暂无跟进记录
|
|
|
+ </div>
|
|
|
+ ) : (
|
|
|
+ <Timeline>
|
|
|
+ {followups.map(item => (
|
|
|
+ <Timeline.Item key={item.id}>
|
|
|
+ <div>
|
|
|
+ <div style={{ marginBottom: 8 }}>
|
|
|
+ <Tag>{FOLLOWUP_TYPES[item.followup_type]}</Tag>
|
|
|
+ <span style={{ marginLeft: 8, color: '#999' }}>
|
|
|
+ {item.user_name} · {dayjs(item.created_at).format('YYYY-MM-DD HH:mm')}
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
+ <div style={{ marginBottom: 4 }}>
|
|
|
+ <strong>跟进内容:</strong>{item.content}
|
|
|
+ </div>
|
|
|
+ {item.next_plan && (
|
|
|
+ <div style={{ color: '#666' }}>
|
|
|
+ <strong>下一步计划:</strong>{item.next_plan}
|
|
|
+ </div>
|
|
|
+ )}
|
|
|
+ </div>
|
|
|
+ </Timeline.Item>
|
|
|
+ ))}
|
|
|
+ </Timeline>
|
|
|
+ )}
|
|
|
+ </Card>
|
|
|
+
|
|
|
+ {/* 添加跟进记录弹窗 */}
|
|
|
+ <Modal
|
|
|
+ title="添加跟进记录"
|
|
|
+ open={followupVisible}
|
|
|
+ onCancel={() => setFollowupVisible(false)}
|
|
|
+ footer={null}
|
|
|
+ >
|
|
|
+ <Form form={form} onFinish={handleAddFollowup} layout="vertical">
|
|
|
+ <Form.Item
|
|
|
+ label="跟进方式"
|
|
|
+ name="followup_type"
|
|
|
+ rules={[{ required: true, message: '请选择跟进方式' }]}
|
|
|
+ >
|
|
|
+ <Select>
|
|
|
+ {Object.entries(FOLLOWUP_TYPES).map(([key, value]) => (
|
|
|
+ <Option key={key} value={key}>{value}</Option>
|
|
|
+ ))}
|
|
|
+ </Select>
|
|
|
+ </Form.Item>
|
|
|
+ <Form.Item
|
|
|
+ label="跟进内容"
|
|
|
+ name="content"
|
|
|
+ rules={[{ required: true, message: '请输入跟进内容' }]}
|
|
|
+ >
|
|
|
+ <TextArea rows={4} />
|
|
|
+ </Form.Item>
|
|
|
+ <Form.Item label="下一步计划" name="next_plan">
|
|
|
+ <TextArea rows={2} />
|
|
|
+ </Form.Item>
|
|
|
+ <Form.Item>
|
|
|
+ <Button type="primary" htmlType="submit">提交</Button>
|
|
|
+ <Button style={{ marginLeft: 8 }} onClick={() => setFollowupVisible(false)}>
|
|
|
+ 取消
|
|
|
+ </Button>
|
|
|
+ </Form.Item>
|
|
|
+ </Form>
|
|
|
+ </Modal>
|
|
|
+
|
|
|
+ {/* 更新状态弹窗 */}
|
|
|
+ <Modal
|
|
|
+ title="更新客户状态"
|
|
|
+ open={statusVisible}
|
|
|
+ onCancel={() => setStatusVisible(false)}
|
|
|
+ footer={null}
|
|
|
+ >
|
|
|
+ <Form onFinish={handleUpdateStatus} layout="vertical">
|
|
|
+ <Form.Item
|
|
|
+ label="状态"
|
|
|
+ name="status"
|
|
|
+ rules={[{ required: true, message: '请选择状态' }]}
|
|
|
+ >
|
|
|
+ <Select>
|
|
|
+ <Option value="following">跟进中</Option>
|
|
|
+ <Option value="won">已成交</Option>
|
|
|
+ <Option value="lost">已丢单</Option>
|
|
|
+ </Select>
|
|
|
+ </Form.Item>
|
|
|
+ <Form.Item>
|
|
|
+ <Button type="primary" htmlType="submit">提交</Button>
|
|
|
+ <Button style={{ marginLeft: 8 }} onClick={() => setStatusVisible(false)}>
|
|
|
+ 取消
|
|
|
+ </Button>
|
|
|
+ </Form.Item>
|
|
|
+ </Form>
|
|
|
+ </Modal>
|
|
|
+ </div>
|
|
|
+ )
|
|
|
+}
|
|
|
+
|
|
|
+export default CustomerDetail
|