实现系统设置页面:基本设置、渠道管理、分配规则、权限、自动回复、工作时间、通知

This commit is contained in:
yml2213
2026-07-14 11:14:42 +08:00
parent a1ba0475ac
commit 05c95c0136
+228 -3
View File
@@ -1,6 +1,231 @@
const Settings = () => ( import { useState } from 'react'
<div className="h-full flex items-center justify-center text-neutral-400 text-center"> import { Form, Input, Switch, Select, Button, Card, Table, Tag, message } from 'antd'
<div><div className="text-lg mb-1"></div><div className="text-sm">...</div></div> import { LinkOutlined, WechatOutlined, PhoneOutlined, MailOutlined, MobileOutlined, CopyOutlined } from '@ant-design/icons'
const channelTypes = [
{ key: 'web', icon: <LinkOutlined />, label: '网页聊天', status: true, code: 'WK_8a3f2e', script: '<script src="https://cs.example.com/widget.js" data-id="WK_8a3f2e"></script>' },
{ key: 'wechat', icon: <WechatOutlined />, label: '微信公众号', status: true, code: 'WX_c7b9d1', script: '' },
{ key: 'app', icon: <MobileOutlined />, label: 'APP 内嵌', status: false, code: '', script: '' },
{ key: 'phone', icon: <PhoneOutlined />, label: '电话客服', status: false, code: '', script: '' },
{ key: 'email', icon: <MailOutlined />, label: '邮件工单', status: true, code: 'EM_f2a8c3', script: '' },
]
const Settings = () => {
const [activeTab, setActiveTab] = useState('basic')
const [basicForm] = Form.useForm()
const [autoReplyForm] = Form.useForm()
const tabItems = [
{ key: 'basic', label: '基本设置' },
{ key: 'channels', label: '渠道管理' },
{ key: 'assignment', label: '分配规则' },
{ key: 'permission', label: '权限管理' },
{ key: 'autoreply', label: '自动回复' },
{ key: 'worktime', label: '工作时间' },
{ key: 'notify', label: '通知设置' },
]
return (
<div className="h-full flex">
{/* 左侧导航 */}
<div className="w-[200px] flex-shrink-0 bg-white border-r border-neutral-200 py-4">
<div className="px-4 mb-3">
<span className="text-xs text-neutral-400 font-medium"></span>
</div>
{tabItems.map(item => (
<div
key={item.key}
className={`px-4 py-2 text-sm cursor-pointer transition-colors ${activeTab === item.key ? 'bg-blue-50 text-blue-600 font-medium border-r-2 border-blue-500' : 'text-neutral-600 hover:bg-neutral-50'}`}
onClick={() => setActiveTab(item.key)}
>
{item.label}
</div>
))}
</div>
{/* 右侧内容 */}
<div className="flex-1 overflow-auto p-6">
{/* 基本设置 */}
{activeTab === 'basic' && (
<Card title="基本设置" className="max-w-2xl">
<Form form={basicForm} layout="vertical" initialValues={{ name: '示例公司', nickname: '客服小助手', timezone: 'Asia/Shanghai', language: 'zh-CN' }}>
<Form.Item name="name" label="租户名称" rules={[{ required: true }]}>
<Input placeholder="公司名称" />
</Form.Item>
<Form.Item name="nickname" label="客服昵称">
<Input placeholder="客服在访客端显示的昵称" />
</Form.Item>
<Form.Item name="timezone" label="时区">
<Select options={[{ value: 'Asia/Shanghai', label: '东八区 (UTC+8)' }, { value: 'Asia/Tokyo', label: '东京 (UTC+9)' }]} />
</Form.Item>
<Form.Item name="language" label="语言">
<Select options={[{ value: 'zh-CN', label: '简体中文' }]} disabled />
</Form.Item>
<Form.Item>
<Button type="primary" onClick={() => message.success('保存成功')}></Button>
</Form.Item>
</Form>
</Card>
)}
{/* 渠道管理 */}
{activeTab === 'channels' && (
<div>
<div className="flex items-center justify-between mb-4">
<h3 className="text-base font-semibold text-neutral-800"></h3>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
{channelTypes.map(ch => (
<Card key={ch.key} size="small" className="!rounded-lg" title={
<div className="flex items-center gap-2">
<span className="text-blue-500">{ch.icon}</span>
<span className="text-sm font-medium">{ch.label}</span>
<Tag color={ch.status ? 'green' : 'default'} className="ml-2">{ch.status ? '已启用' : '未启用'}</Tag>
</div>
}>
<div className="space-y-3">
{ch.code && <div className="flex items-center justify-between text-sm"><span className="text-neutral-400">ID</span><code className="text-xs bg-neutral-100 px-2 py-0.5 rounded">{ch.code}</code></div>}
{ch.script && (
<div>
<div className="text-sm text-neutral-400 mb-1"></div>
<div className="bg-neutral-50 border border-neutral-200 rounded p-2 flex items-center justify-between">
<code className="text-xs text-neutral-600 break-all">{ch.script}</code>
<CopyOutlined className="text-neutral-400 cursor-pointer hover:text-blue-500 ml-2 flex-shrink-0" onClick={() => message.success('已复制')} />
</div>
</div>
)}
<div className="flex justify-between items-center">
<span className="text-sm text-neutral-400"></span>
<Switch defaultChecked={ch.status} size="small" />
</div>
</div>
</Card>
))}
</div>
</div>
)}
{/* 分配规则 */}
{activeTab === 'assignment' && (
<Card title="客服分配规则" className="max-w-2xl">
<div className="space-y-4">
<div className="p-4 border border-neutral-200 rounded-lg">
<div className="flex items-center justify-between">
<div>
<div className="text-sm font-medium text-neutral-800"></div>
<div className="text-xs text-neutral-400 mt-1">线</div>
</div>
<Switch defaultChecked />
</div>
</div>
<div className="p-4 border border-neutral-200 rounded-lg">
<div className="flex items-center justify-between">
<div>
<div className="text-sm font-medium text-neutral-800"></div>
<div className="text-xs text-neutral-400 mt-1"></div>
</div>
<Switch />
</div>
</div>
<div className="p-4 border border-neutral-200 rounded-lg">
<div className="flex items-center justify-between">
<div>
<div className="text-sm font-medium text-neutral-800"></div>
<div className="text-xs text-neutral-400 mt-1"></div>
</div>
<Switch defaultChecked />
</div>
</div>
<Button type="primary" onClick={() => message.success('保存成功')}></Button>
</div>
</Card>
)}
{/* 权限管理 */}
{activeTab === 'permission' && (
<Card title="权限管理" className="max-w-3xl">
<Table
dataSource={[
{ key: '1', role: '租户管理员', members: 2, permissions: '全部权限', status: true },
{ key: '2', role: '客服主管', members: 3, permissions: '会话管理、数据统计、知识库管理', status: true },
{ key: '3', role: '一线客服', members: 8, permissions: '会话接待、客户管理、知识库查看', status: true },
]}
columns={[
{ title: '角色', dataIndex: 'role', key: 'role' },
{ title: '成员数', dataIndex: 'members', key: 'members' },
{ title: '权限范围', dataIndex: 'permissions', key: 'permissions' },
{ title: '状态', dataIndex: 'status', key: 'status', render: (v: boolean) => <Tag color={v ? 'green' : 'default'}>{v ? '启用' : '禁用'}</Tag> },
{ title: '操作', key: 'op', render: () => <Button type="link" size="small"></Button> },
]}
pagination={false}
size="middle"
/>
</Card>
)}
{/* 自动回复 */}
{activeTab === 'autoreply' && (
<Card title="自动回复设置" className="max-w-2xl">
<Form form={autoReplyForm} layout="vertical" initialValues={{ welcome: '您好!欢迎来到客服云,请问有什么可以帮您的?', offline: '当前无客服在线,请留下联系方式,我们会尽快回复您。' }}>
<Form.Item name="welcome" label="欢迎语">
<Input.TextArea rows={3} placeholder="访客打开聊天窗口后自动发送的欢迎语" maxLength={500} showCount />
</Form.Item>
<Form.Item name="offline" label="离线留言提示">
<Input.TextArea rows={3} placeholder="无客服在线时自动回复的提示语" maxLength={500} showCount />
</Form.Item>
<Form.Item>
<Button type="primary" onClick={() => message.success('保存成功')}></Button>
</Form.Item>
</Form>
</Card>
)}
{/* 工作时间 */}
{activeTab === 'worktime' && (
<Card title="工作时间设置" className="max-w-2xl">
<div className="space-y-4">
<div>
<div className="text-sm font-medium text-neutral-700 mb-2"></div>
{['周一', '周二', '周三', '周四', '周五', '周六', '周日'].map(day => (
<div key={day} className="flex items-center gap-4 py-2 border-b border-neutral-50 last:border-0">
<span className="text-sm text-neutral-600 w-12">{day}</span>
<Select defaultValue={day === '周六' || day === '周日' ? undefined : '09:00-18:00'} placeholder="休息" className="w-36" size="small" allowClear options={['09:00-18:00', '08:00-17:00', '10:00-19:00', '全天'].map(v => ({ value: v, label: v }))} />
</div>
))}
</div>
<div>
<div className="text-sm font-medium text-neutral-700 mb-2"></div>
<Input placeholder="当前非工作时间,我们将在工作时段尽快回复您" />
</div>
<Button type="primary" onClick={() => message.success('保存成功')}></Button>
</div>
</Card>
)}
{/* 通知设置 */}
{activeTab === 'notify' && (
<Card title="通知设置" className="max-w-2xl">
<div className="space-y-4">
{[ { label: '新会话提醒', desc: '有新访客发起会话时桌面通知' },
{ label: '消息声音', desc: '收到新消息时播放提示音' },
{ label: '离线消息通知', desc: '非工作时间收到的留言邮件通知' },
{ label: '日报推送', desc: '每日客服数据汇总邮件' },
{ label: '周报推送', desc: '每周客服数据汇总邮件' },
].map((item, i) => (
<div key={i} className="flex items-center justify-between py-2 border-b border-neutral-50 last:border-0">
<div>
<div className="text-sm text-neutral-700">{item.label}</div>
<div className="text-xs text-neutral-400">{item.desc}</div>
</div>
<Switch defaultChecked={i < 3} />
</div>
))}
</div>
</Card>
)}
</div>
</div> </div>
) )
}
export default Settings export default Settings