Files
kefu_cloud/web/src/pages/agent/settings/AssignmentSection.tsx
T
yml2213 6304f97bc3 refactor(web): 拆分 Dashboard/Settings/VisitorChat 超大组件为子模块
- Dashboard 2591→1944 行,抽出 dashboard/ 纯函数、客户资料字段与 5 个状态自持弹窗
- Settings 1602→478 行,抽出 settings/ 16 个分区模块(设置卡片、弹窗与工具函数)
- VisitorChat 1343→1045 行,抽出 visitor/ 头部、消息区、输入区与评价浮层
- 均为纯代码搬移:行为、路由入口与导出不变;tsc -b 与 vite build 通过
2026-09-05 14:40:14 +08:00

114 lines
5.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { Button, Form, Input, Select, Spin } from 'antd'
import { TeamOutlined } from '@ant-design/icons'
import type { FormInstance } from 'antd'
import type { SaveSettingsFn } from './types'
interface AssignmentSectionProps {
assignForm: FormInstance
loading: boolean
isAdmin: boolean
saving: boolean
onSave: SaveSettingsFn
}
/** 客服分配规则:自动分配策略与并发上限 */
const AssignmentSection = ({ assignForm, loading, isAdmin, saving, onSave }: AssignmentSectionProps) => {
return (
<div className="space-y-5">
{loading ? (
<div className="py-16 text-center"><Spin /></div>
) : (
<>
<div className="bg-blue-50/50 rounded-2xl border border-blue-100 p-5">
<div className="flex items-start gap-3.5">
<div className="w-10 h-10 rounded-xl bg-blue-100 text-blue-600 flex items-center justify-center shrink-0 text-lg">
<TeamOutlined />
</div>
<div className="min-w-0">
<div className="text-sm font-bold text-slate-900">自动分配规则</div>
<div className="text-xs text-slate-600 mt-1 leading-relaxed">
工作时间内,访客进线会按下列策略自动分配给<strong className="font-semibold text-blue-700">在线一线客服</strong>;
非工作时间或无可接坐席时进入排队或离线留言。人工领取与内部转接不受此策略限制。
</div>
</div>
</div>
</div>
<div className="bg-white rounded-2xl border border-slate-200/80 shadow-xs p-6 md:p-8">
<Form
form={assignForm}
layout="vertical"
disabled={!isAdmin}
onFinish={async (values: {
assign_strategy: 'least_load' | 'round_robin'
max_active_per_agent: number
}) => {
await onSave({
assign_strategy: values.assign_strategy,
max_active_per_agent: Number(values.max_active_per_agent) || 0,
}, '分配规则已保存')
}}
>
<Form.Item
name="assign_strategy"
label={<span className="text-xs font-semibold text-slate-700">分配策略</span>}
rules={[{ required: true, message: '请选择策略' }]}
extra={<span className="text-[11px] text-slate-400">负载最低:优先分配给进行中会话最少的坐席;轮询:在可接坐席中均匀依次分发。</span>}
>
<Select
className="!rounded-lg"
options={[
{ value: 'least_load', label: '负载最低策略(推荐)' },
{ value: 'round_robin', label: '轮询轮转分配策略' },
]}
/>
</Form.Item>
<Form.Item
name="max_active_per_agent"
label={<span className="text-xs font-semibold text-slate-700">每位坐席最大并发会话数</span>}
rules={[
{ required: true, message: '请填写上限' },
{
validator: async (_, v) => {
const n = Number(v)
if (!Number.isFinite(n) || n < 0 || n > 200) {
throw new Error('请填写 0–200 的整数,0 表示不限制')
}
},
},
]}
extra={<span className="text-[11px] text-slate-400">达到上限后不再自动接入新访客,全部坐席满载时访客将进入等待队列。0 表示不限上限。</span>}
>
<Input type="number" min={0} max={200} placeholder="0 表示不限制" className="!rounded-lg" />
</Form.Item>
<div className="pt-4 border-t border-slate-100 flex items-center justify-between">
{isAdmin ? (
<Button
type="primary"
htmlType="submit"
loading={saving}
className="!h-9 !px-6 !rounded-xl !text-xs !font-semibold !bg-gradient-to-r !from-blue-600 !to-blue-500 shadow-sm shadow-blue-500/20"
>
保存分配规则
</Button>
) : (
<p className="text-xs text-slate-400 m-0">仅租户管理员可修改分配规则</p>
)}
</div>
</Form>
</div>
<div className="bg-slate-50/80 rounded-2xl border border-slate-200/80 p-4 text-xs text-slate-500 leading-relaxed space-y-1">
<div className="font-bold text-slate-700 mb-1">使用说明</div>
<p className="m-0">· 自动分发候选范围:角色为客服、当前状态为「在线」的账号(忙碌/离线状态不参与自动分发)。</p>
<p className="m-0">· 专属坐席组与渠道绑定策略将在后续版本逐步上线。</p>
</div>
</>
)}
</div>
)
}
export default AssignmentSection