| | |
| | | import { allAreaList } from '@/utils/area'; |
| | | import { PlusOutlined } from '@ant-design/icons'; |
| | | import { PageContainer } from '@ant-design/pro-components'; |
| | | import { Button, Empty, message, Select, Spin } from 'antd'; |
| | | import { useEffect, useState } from 'react'; |
| | | import { Button, Cascader, Empty, message, Select, Spin, Tag } from 'antd'; |
| | | import { useEffect, useMemo, useState } from 'react'; |
| | | import { getList, update } from './service'; |
| | | import styles from './style.less'; |
| | | |
| | |
| | | .filter(Number.isFinite) |
| | | : []; |
| | | |
| | | const commaTextToArray = (value) => |
| | | value |
| | | ? String(value) |
| | | .split(',') |
| | | .map((item) => item.trim()) |
| | | .filter(Boolean) |
| | | : []; |
| | | |
| | | const arraysEqual = (a = [], b = []) => |
| | | a.length === b.length && [...a].sort().join(',') === [...b].sort().join(','); |
| | | |
| | | const buildAreaOptions = () => { |
| | | const codeMap = {}; |
| | | const options = (allAreaList || []).map((provinceItem) => { |
| | | codeMap[provinceItem.value] = provinceItem.province; |
| | | return { |
| | | label: provinceItem.province, |
| | | value: provinceItem.value, |
| | | children: (provinceItem.data || []).map((cityItem) => { |
| | | codeMap[cityItem.value] = cityItem.city; |
| | | return { |
| | | label: cityItem.city, |
| | | value: cityItem.value, |
| | | children: (cityItem.dataList || []).map((areaItem) => { |
| | | codeMap[areaItem.value] = areaItem.area; |
| | | return { label: areaItem.area, value: areaItem.value }; |
| | | }), |
| | | }; |
| | | }), |
| | | }; |
| | | }); |
| | | return { options, codeMap }; |
| | | }; |
| | | |
| | | export default function OutboundRobotSetting() { |
| | | const { options: areaOptions, codeMap } = useMemo(() => buildAreaOptions(), []); |
| | | const [loading, setLoading] = useState(false); |
| | | const [saving, setSaving] = useState(false); |
| | | const [robotList, setRobotList] = useState([]); |
| | | const [siteOptions, setSiteOptions] = useState([]); |
| | | const [draftArea, setDraftArea] = useState({}); |
| | | const [originList, setOriginList] = useState([]); |
| | | |
| | | const loadList = async () => { |
| | |
| | | const list = (res?.data?.robotSettings || []).map((item) => ({ |
| | | ...item, |
| | | siteIds: commaTextToNumberArray(item.siteIds), |
| | | dialAreaCodes: commaTextToArray(item.dialAreaCodes), |
| | | })); |
| | | setSiteOptions( |
| | | (res?.data?.sites || []).map((item) => ({ label: item.siteName, value: item.id })), |
| | |
| | | list.map((item) => ({ |
| | | id: item.id, |
| | | siteIds: [...item.siteIds], |
| | | dialAreaCodes: [...item.dialAreaCodes], |
| | | })), |
| | | ); |
| | | } else { |
| | |
| | | ); |
| | | }; |
| | | |
| | | const setAreaValue = (id, value) => { |
| | | setDraftArea((prev) => ({ ...prev, [id]: value })); |
| | | }; |
| | | |
| | | const addArea = (id) => { |
| | | const selectedPath = draftArea[id]; |
| | | const code = Array.isArray(selectedPath) ? selectedPath[selectedPath.length - 1] : selectedPath; |
| | | if (!code) return; |
| | | |
| | | setRobotList((prev) => |
| | | prev.map((item) => { |
| | | if (item.id !== id || item.dialAreaCodes.includes(code)) return item; |
| | | return { ...item, dialAreaCodes: [...item.dialAreaCodes, code] }; |
| | | }), |
| | | ); |
| | | setDraftArea((prev) => ({ ...prev, [id]: undefined })); |
| | | }; |
| | | |
| | | const removeArea = (id, code) => { |
| | | setRobotList((prev) => |
| | | prev.map((item) => |
| | | item.id === id |
| | | ? { ...item, dialAreaCodes: item.dialAreaCodes.filter((itemCode) => itemCode !== code) } |
| | | : item, |
| | | ), |
| | | ); |
| | | }; |
| | | |
| | | const save = async () => { |
| | | const changedList = robotList.filter((item) => { |
| | | const origin = originList.find((originItem) => originItem.id === item.id); |
| | | if (!origin) { |
| | | return true; |
| | | } |
| | | return !arraysEqual(origin.siteIds, item.siteIds); |
| | | return ( |
| | | !arraysEqual(origin.siteIds, item.siteIds) || |
| | | !arraysEqual(origin.dialAreaCodes, item.dialAreaCodes) |
| | | ); |
| | | }); |
| | | |
| | | if (changedList.length === 0) { |
| | |
| | | const res = await update({ |
| | | id: item.id, |
| | | siteIds: item.siteIds || [], |
| | | dialAreaCodes: item.dialAreaCodes || [], |
| | | }); |
| | | if (String(res?.code) !== '200') { |
| | | message.error(res?.msg || `机器人 ${item.robotNumber || item.id} 保存失败`); |
| | |
| | | <Spin spinning={loading}> |
| | | <div className={styles.page}> |
| | | <div className={styles.header}> |
| | | <div className={styles.title}>机器人可外呼站点设置</div> |
| | | <div className={styles.subTitle}>按订单所属站点配置机器人可外呼范围</div> |
| | | <div className={styles.title}>机器人外呼设置</div> |
| | | <div className={styles.subTitle}>按订单所属站点和拨打区域配置机器人外呼范围</div> |
| | | </div> |
| | | |
| | | {robotList.length === 0 && !loading ? <Empty description="暂无配置" /> : null} |
| | |
| | | /> |
| | | </div> |
| | | </div> |
| | | |
| | | <div className={styles.fieldRow}> |
| | | <div className={styles.label}>拨打区域</div> |
| | | <div className={styles.value}> |
| | | <div className={styles.tags}> |
| | | {item.dialAreaCodes.map((code) => ( |
| | | <Tag key={code} closable onClose={() => removeArea(item.id, code)}> |
| | | {codeMap[code] || code} |
| | | </Tag> |
| | | ))} |
| | | </div> |
| | | <div className={styles.areaRow}> |
| | | <Cascader |
| | | className={styles.cascader} |
| | | value={draftArea[item.id]} |
| | | options={areaOptions} |
| | | placeholder="请选择省 / 市 / 区" |
| | | changeOnSelect |
| | | expandTrigger="hover" |
| | | onChange={(value) => setAreaValue(item.id, value)} |
| | | /> |
| | | <Button icon={<PlusOutlined />} onClick={() => addArea(item.id)}> |
| | | 添加 |
| | | </Button> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </section> |
| | | ))} |
| | | |