482 lines
19 KiB
TypeScript
482 lines
19 KiB
TypeScript
import React, { useState, useEffect, useRef } from 'react';
|
|
import { Plus, Search, Eye, Edit, Copy, Trash2, Play, Pause, ChevronRight, Layers } from 'lucide-react';
|
|
import { useTheme } from '../contexts/ThemeContext';
|
|
import { PageType } from '../components/Sidebar';
|
|
|
|
import { requestCrudService } from '@/service/request-axios-service';
|
|
import { Pagination, Dialog, Input, Textarea, Radio, Button, MessagePlugin, Popconfirm, Loading } from 'tdesign-react';
|
|
import { useNavigate } from 'react-router-dom';
|
|
|
|
interface SceneListProps {
|
|
onViewDetail: (id: string) => void;
|
|
onNavigate: (page: PageType) => void;
|
|
}
|
|
|
|
interface sceneItem {
|
|
id?: number;
|
|
status?: number;
|
|
applicableTask?: string;
|
|
sceneName?: string;
|
|
sceneType: string;
|
|
sceneIntro: string;
|
|
sceneCode: string;
|
|
relatedAgent: string;
|
|
deleted?: number;
|
|
updateTime?: string;
|
|
taskJson?: string;
|
|
version: string;
|
|
}
|
|
|
|
const zncmUrl = import.meta.env.VITE_API_BASE_URL || '';
|
|
const sceneStatus = {
|
|
0: '执行中',
|
|
1: '成功',
|
|
2: '失败',
|
|
3: '部分成功'
|
|
}
|
|
const SceneList: React.FC<SceneListProps> = ({ onViewDetail, onNavigate }) => {
|
|
const { theme } = useTheme();
|
|
const isDark = theme === 'dark';
|
|
const [visible, setVisible] = useState(false);
|
|
const [editDataShow, setEditDataShow] = useState(false);
|
|
const [sceneName, setSceneName] = useState('');
|
|
const [scenes, setScenes] = useState<sceneItem[]>([]);
|
|
const [loading, setLoading] = useState<boolean>(false);
|
|
const [editLoading, setEditLoading] = useState<boolean>(false);
|
|
const [pageNum, setPageNum] = useState<number>(1);
|
|
const [pageSize, setPageSize] = useState<number>(10);
|
|
const [total, setTotal] = useState<number>(0);
|
|
const [formData, setFormData] = useState<sceneItem>({
|
|
applicableTask: '',
|
|
relatedAgent: '',
|
|
sceneCode: '',
|
|
sceneIntro: '',
|
|
sceneName: '',
|
|
sceneType: '',
|
|
taskJson: '',
|
|
version: '',
|
|
});
|
|
const state = useRef({
|
|
pageNum: 1,
|
|
pageSize: 10
|
|
});
|
|
const navigate = useNavigate();
|
|
|
|
const scenesDataSearch = async (pageNumParam?: number, pageSizeParam?: number, sceneNameParam?: string) => {
|
|
const parmy = {
|
|
pageNum: pageNumParam ?? state.current.pageNum,
|
|
pageSize: pageSizeParam ?? state.current.pageSize,
|
|
sceneName: sceneNameParam ?? sceneName
|
|
}
|
|
setLoading(true);
|
|
const res = await requestCrudService.get(`${zncmUrl}/scene/scenes`, parmy);
|
|
setLoading(false);
|
|
res.data.records.forEach((item) => {
|
|
if (item.taskJson) {
|
|
item.taskJson = JSON.parse(item.taskJson);
|
|
}
|
|
})
|
|
console.log('res.data.records', res.data.records)
|
|
setScenes(res.data.records || []);
|
|
setTotal(res.data.total || 0);
|
|
}
|
|
|
|
const addScene = async () => {
|
|
const res = await requestCrudService.post(`${zncmUrl}/scene/sceneAdd`, formData);
|
|
setVisible(false);
|
|
MessagePlugin.success('新增场景成功');
|
|
await scenesDataSearch();
|
|
}
|
|
|
|
const updateScene = async () => {
|
|
setEditLoading(true);
|
|
const res = await requestCrudService.post(`${zncmUrl}/scene/sceneUpdate`, {
|
|
...formData,
|
|
});
|
|
setEditLoading(false);
|
|
setEditDataShow(false);
|
|
MessagePlugin.success('更新场景成功');
|
|
await scenesDataSearch();
|
|
}
|
|
|
|
const handleDataChange = (key: keyof typeof formData, value: string | number) => {
|
|
setFormData((prev) => ({
|
|
...prev,
|
|
[key]: value,
|
|
}));
|
|
};
|
|
|
|
const showDialog = () => {
|
|
setFormData({
|
|
applicableTask: '',
|
|
relatedAgent: '',
|
|
sceneCode: '',
|
|
sceneIntro: '',
|
|
sceneName: '',
|
|
sceneType: '',
|
|
version: ''
|
|
});
|
|
setVisible(true);
|
|
};
|
|
|
|
const editShowChange = async (item: sceneItem) => {
|
|
const response = await fetch('https://ch1.b.uat.4.cn/zncmweb/templates/6b_template.json');
|
|
const data = await response.json();
|
|
console.log('response', JSON.stringify(data));
|
|
// let taskJson =
|
|
setFormData({
|
|
applicableTask: item.applicableTask,
|
|
deleted: item.deleted,
|
|
id: item.id,
|
|
relatedAgent: item.relatedAgent,
|
|
sceneCode: item.sceneCode,
|
|
sceneIntro: item.sceneIntro,
|
|
sceneName: item.sceneName,
|
|
sceneType: item.sceneType,
|
|
status: item.status,
|
|
taskJson: JSON.stringify(data),
|
|
version: item.version
|
|
});
|
|
setEditDataShow(true);
|
|
}
|
|
|
|
const { Group: RadioGroup, Button: RadioButton } = Radio;
|
|
|
|
const delDataChange = async (item: sceneItem) => {
|
|
const res = await requestCrudService.delete(`${zncmUrl}/scene/sceneDelete/${item.id}`)
|
|
console.log(res);
|
|
MessagePlugin.success('场景删除成功');
|
|
await scenesDataSearch();
|
|
}
|
|
|
|
const handleConfirm = (item: sceneItem) => {
|
|
// 确认删除逻辑
|
|
console.log('用户点击了确认');
|
|
delDataChange(item);
|
|
return true; // 返回 true 表示确认执行
|
|
};
|
|
|
|
const handleCancel = () => {
|
|
// 取消删除逻辑
|
|
console.log('用户点击了取消');
|
|
return true; // 返回 true 表示取消
|
|
};
|
|
const pageIndexChange = async (value: number) => {
|
|
state.current.pageNum = value;
|
|
setPageNum(value);
|
|
await scenesDataSearch();
|
|
}
|
|
|
|
const pageSizeChange = async (value: number) => {
|
|
state.current.pageSize = value;
|
|
setPageSize(value);
|
|
await scenesDataSearch();
|
|
}
|
|
|
|
// 回车处理
|
|
const handleKeyDown = async (e) => {
|
|
if ((e.metaKey || e.ctrlKey || e.shiftKey) && e.key === "Enter") {
|
|
e.preventDefault();
|
|
} else if (e.keyCode === 13) {
|
|
e.preventDefault();
|
|
await scenesDataSearch();
|
|
} else if (e.key === "/") {
|
|
e.preventDefault();
|
|
}
|
|
};
|
|
|
|
useEffect(() => {
|
|
scenesDataSearch();
|
|
}, [])
|
|
|
|
return (
|
|
<div className="space-y-6">
|
|
{/* 页面标题区 */}
|
|
<div className="flex items-center justify-between">
|
|
<div>
|
|
<h1 className={`text-2xl font-bold ${isDark ? 'text-white' : 'text-gray-900'} mb-2`}>业务链条管理</h1>
|
|
<p className={`${isDark ? 'text-slate-400' : 'text-gray-600'} text-sm`}>管理和配置宣传策略流程模板</p>
|
|
</div>
|
|
<Button theme="primary" onClick={showDialog}>
|
|
<Plus className="w-4 h-4 mr-2" />
|
|
<span className="text-sm font-medium">新建场景</span>
|
|
</Button>
|
|
</div>
|
|
|
|
{/* 搜索和筛选区 */}
|
|
<div className="flex items-center gap-4">
|
|
<div className="relative flex-1 max-w-md">
|
|
<Search className={`absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 ${isDark ? 'text-slate-500' : 'text-gray-400'}`} />
|
|
<input
|
|
type="text"
|
|
value={sceneName}
|
|
onChange={(e) => setSceneName(e.target.value)}
|
|
onKeyDown={(e) => handleKeyDown(e)}
|
|
placeholder="搜索场景名称..."
|
|
className={`w-full pl-10 pr-4 py-2.5 ${isDark ? 'bg-slate-800/30' : 'bg-gray-50'} border ${isDark ? 'border-blue-800/30' : 'border-gray-300'} rounded-lg ${isDark ? 'text-white' : 'text-gray-900'} placeholder-${isDark ? 'text-slate-500' : 'text-gray-500'} text-sm focus:outline-none focus:border-blue-500/50 transition-colors`}
|
|
/>
|
|
</div>
|
|
{/* <button className={`px-4 py-2.5 ${isDark ? 'bg-slate-800/50 border-blue-800/30' : 'bg-gray-100 border-gray-300'} rounded-lg ${isDark ? 'bg-slate-700/50 border-blue-800/30 text-slate-300 hover:bg-slate-600/50' : 'bg-gray-100 border-gray-300 text-gray-700 hover:bg-gray-200'} transition-colors`}>
|
|
全部类型
|
|
</button>
|
|
<button className={`px-4 py-2.5 ${isDark ? 'bg-slate-800/50 border-blue-800/30' : 'bg-gray-100 border-gray-300'} rounded-lg ${isDark ? 'bg-slate-700/50 border-blue-800/30 text-slate-300 hover:bg-slate-600/50' : 'bg-gray-100 border-gray-300 text-gray-700 hover:bg-gray-200'} transition-colors`}>
|
|
全部状态
|
|
</button> */}
|
|
</div>
|
|
|
|
{/* 场景列表 */}
|
|
<div className={`${isDark ? 'bg-slate-800/30' : 'bg-white'} backdrop-blur-xl border ${isDark ? 'border-blue-800/20' : 'border-gray-200'} rounded-xl overflow-hidden`}>
|
|
<Loading loading={loading} text="正在加载...">
|
|
<table className="w-full">
|
|
<thead>
|
|
<tr className={`border-b ${isDark ? 'border-blue-800/20' : 'border-gray-200'}`}>
|
|
<th className={`text-left px-6 py-4 text-xs font-semibold ${isDark ? 'text-slate-400' : 'text-gray-600'} uppercase tracking-wider`}>场景名称</th>
|
|
<th className={`text-left px-6 py-4 text-xs font-semibold ${isDark ? 'text-slate-400' : 'text-gray-600'} uppercase tracking-wider`}>场景类型</th>
|
|
<th className={`text-left px-6 py-4 text-xs font-semibold ${isDark ? 'text-slate-400' : 'text-gray-600'} uppercase tracking-wider`}>适用任务</th>
|
|
<th className={`text-left px-6 py-4 text-xs font-semibold ${isDark ? 'text-slate-400' : 'text-gray-600'} uppercase tracking-wider`}>版本</th>
|
|
<th className={`text-left px-6 py-4 text-xs font-semibold ${isDark ? 'text-slate-400' : 'text-gray-600'} uppercase tracking-wider`}>更新时间</th>
|
|
<th className={`text-left px-6 py-4 text-xs font-semibold ${isDark ? 'text-slate-400' : 'text-gray-600'} uppercase tracking-wider`}>状态</th>
|
|
<th className={`text-left px-6 py-4 text-xs font-semibold ${isDark ? 'text-slate-400' : 'text-gray-600'} uppercase tracking-wider`}>操作</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{scenes.map((scene) => (
|
|
<tr key={scene.id} className={`border-b ${isDark ? 'border-blue-800/20' : 'border-gray-200'} hover:bg-slate-700/20 transition-colors`}>
|
|
<td className="px-6 py-4">
|
|
<div className="flex items-center gap-3">
|
|
<div className="w-10 h-10 rounded-lg bg-blue-500/20 flex items-center justify-center">
|
|
<Layers className="w-5 h-5 text-blue-400" />
|
|
</div>
|
|
<span className={`${isDark ? 'text-white' : 'text-gray-900'} font-medium`}>{scene.sceneName}</span>
|
|
</div>
|
|
</td>
|
|
<td className="px-6 py-4">
|
|
<span className={`${isDark ? 'text-slate-400' : 'text-gray-600'} text-sm`}>{scene.sceneType}</span>
|
|
</td>
|
|
<td className="px-6 py-4">
|
|
<span className={`${isDark ? 'text-slate-400' : 'text-gray-600'} text-sm`}>{scene.sceneIntro}</span>
|
|
</td>
|
|
<td className="px-6 py-4">
|
|
<span className={`${isDark ? 'text-slate-400' : 'text-gray-600'} text-sm`}>{scene.version}</span>
|
|
</td>
|
|
<td className="px-6 py-4">
|
|
<span className={`${isDark ? 'text-slate-400' : 'text-gray-600'} text-sm`}>{scene.updateTime}</span>
|
|
</td>
|
|
<td className="px-6 py-4">
|
|
<span
|
|
className={`inline-flex items-center gap-1.5 px-2.5 py-1 rounded-full text-xs font-medium ${scene.status === 0
|
|
? 'bg-green-500/20 text-green-400'
|
|
: scene.status === 1
|
|
? 'bg-yellow-500/20 text-yellow-400'
|
|
: `${isDark ? 'bg-slate-500/20 text-slate-400' : 'bg-gray-500/20 text-gray-500'}`
|
|
}`}
|
|
>
|
|
<span
|
|
className={`w-1.5 h-1.5 rounded-full ${scene.status === 0
|
|
? 'bg-green-400'
|
|
: scene.status === 1
|
|
? 'bg-yellow-400'
|
|
: isDark ? 'bg-slate-400' : 'bg-gray-400'
|
|
}`}
|
|
/>
|
|
{sceneStatus[scene.status]}
|
|
</span>
|
|
</td>
|
|
<td className="px-6 py-4">
|
|
<div className="flex items-center gap-2">
|
|
<button
|
|
onClick={() => {
|
|
navigate(`/scene-detail/${scene.id}`);
|
|
}}
|
|
className="p-2 hover:bg-slate-700/50 rounded-lg transition-colors group"
|
|
title="查看"
|
|
>
|
|
<Eye className={`w-4 h-4 ${isDark ? 'text-slate-500' : 'text-gray-500'} group-hover:${isDark ? 'text-white' : 'text-gray-900'}`} />
|
|
</button>
|
|
{/* <button className="p-2 hover:bg-slate-700/50 rounded-lg transition-colors group" title="编辑" onClick={() => editShowChange(scene)}>
|
|
<Edit className={`w-4 h-4 ${isDark ? 'text-slate-500' : 'text-gray-500'} group-hover:${isDark ? 'text-white' : 'text-gray-900'}`} />
|
|
</button> */}
|
|
{/* <button className="p-2 hover:bg-slate-700/50 rounded-lg transition-colors group" title="复制">
|
|
<Copy className={`w-4 h-4 ${isDark ? 'text-slate-500' : 'text-gray-500'} group-hover:${isDark ? 'text-white' : 'text-gray-900'}`} />
|
|
</button> */}
|
|
{/* <button
|
|
onClick={() => onNavigate('task-scene')}
|
|
className="p-2 hover:bg-blue-500/20 rounded-lg transition-colors group"
|
|
title="启动"
|
|
>
|
|
<Play className={`w-4 h-4 ${isDark ? 'text-slate-500' : 'text-gray-500'} group-hover:text-blue-400`} />
|
|
</button> */}
|
|
{/* <Popconfirm
|
|
content="确认删除吗"
|
|
onConfirm={() => handleConfirm(scene)}
|
|
onCancel={handleCancel}
|
|
>
|
|
<button className="p-2 hover:bg-red-500/20 rounded-lg transition-colors group" title="删除">
|
|
<Trash2 className={`w-4 h-4 ${isDark ? 'text-slate-500' : 'text-gray-500'} group-hover:text-red-400`} />
|
|
</button>
|
|
</Popconfirm> */}
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
))}
|
|
</tbody>
|
|
</table>
|
|
</Loading>
|
|
</div>
|
|
|
|
{/* 分页 */}
|
|
<div className="p-4 text-center">
|
|
<Pagination
|
|
total={total}
|
|
current={pageNum}
|
|
pageSize={pageSize}
|
|
onCurrentChange={(page) => pageIndexChange(Number(page))}
|
|
onPageSizeChange={(size) => pageSizeChange(Number(size))}
|
|
/>
|
|
</div>
|
|
|
|
{/* 新增对话框 */}
|
|
<Dialog
|
|
header="新增场景"
|
|
visible={visible}
|
|
onConfirm={addScene}
|
|
onCancel={() => setVisible(false)}
|
|
onClose={() => setVisible(false)}
|
|
closeOnOverlayClick={true}
|
|
>
|
|
<div className="mb-4">
|
|
<Input
|
|
label="场景名称"
|
|
value={formData.sceneName}
|
|
onChange={(val) => handleDataChange('sceneName', val)}
|
|
placeholder="请输入场景名称"
|
|
className="w-full"
|
|
/>
|
|
</div>
|
|
<div className="mb-4">
|
|
<Input
|
|
label="场景类型"
|
|
value={formData.sceneType}
|
|
onChange={(val) => handleDataChange('sceneType', val)}
|
|
placeholder="请输入场景类型"
|
|
className="w-full"
|
|
/>
|
|
</div>
|
|
<div className="mb-4">
|
|
<Input
|
|
label="适用任务"
|
|
value={formData.applicableTask}
|
|
onChange={(val) => handleDataChange('applicableTask', val)}
|
|
placeholder="请输入适用任务"
|
|
className="w-full"
|
|
/>
|
|
</div>
|
|
<div className="mb-4">
|
|
<Input
|
|
label="场景代码"
|
|
value={formData.sceneCode}
|
|
onChange={(val) => handleDataChange('sceneCode', val)}
|
|
placeholder="请输入场景代码"
|
|
className="w-full"
|
|
/>
|
|
</div>
|
|
<div className="mb-4">
|
|
<Input
|
|
label="版本"
|
|
value={formData.version}
|
|
onChange={(val) => handleDataChange('version', val)}
|
|
placeholder="请输入版本"
|
|
className="w-full"
|
|
/>
|
|
</div>
|
|
<div>
|
|
<Textarea
|
|
value={formData.sceneIntro}
|
|
onChange={(val) => handleDataChange('sceneIntro', val)}
|
|
placeholder="请输入场景简介"
|
|
className="w-full"
|
|
/>
|
|
</div>
|
|
</Dialog>
|
|
|
|
{/* 编辑对话框 */}
|
|
<Dialog
|
|
header="编辑场景"
|
|
visible={editDataShow}
|
|
onConfirm={updateScene}
|
|
confirmBtn={{
|
|
content: '确定',
|
|
theme: 'primary',
|
|
loading: editLoading,
|
|
}}
|
|
onCancel={() => setEditDataShow(false)}
|
|
onClose={() => setEditDataShow(false)}
|
|
closeOnOverlayClick={true}
|
|
>
|
|
<div className="mb-4">
|
|
<Input
|
|
label="场景名称"
|
|
value={formData.sceneName}
|
|
onChange={(val) => handleDataChange('sceneName', val)}
|
|
placeholder="请输入场景名称"
|
|
className="w-full"
|
|
/>
|
|
</div>
|
|
<div className="mb-4">
|
|
<Input
|
|
label="场景类型"
|
|
value={formData.sceneType}
|
|
onChange={(val) => handleDataChange('sceneType', val)}
|
|
placeholder="请输入场景类型"
|
|
className="w-full"
|
|
/>
|
|
</div>
|
|
<div className="mb-4">
|
|
<Input
|
|
label="适用任务"
|
|
value={formData.applicableTask}
|
|
onChange={(val) => handleDataChange('applicableTask', val)}
|
|
placeholder="请输入适用任务"
|
|
className="w-full"
|
|
/>
|
|
</div>
|
|
<div className="mb-4">
|
|
<Input
|
|
label="场景代码"
|
|
value={formData.sceneCode}
|
|
onChange={(val) => handleDataChange('sceneCode', val)}
|
|
placeholder="请输入场景代码"
|
|
className="w-full"
|
|
/>
|
|
</div>
|
|
<div className="mb-4">
|
|
<Input
|
|
label="版本"
|
|
value={formData.version}
|
|
onChange={(val) => handleDataChange('version', val)}
|
|
placeholder="请输入版本"
|
|
className="w-full"
|
|
/>
|
|
</div>
|
|
<div className="mb-4">
|
|
<Textarea
|
|
value={formData.sceneIntro}
|
|
onChange={(val) => handleDataChange('sceneIntro', val)}
|
|
placeholder="请输入场景简介"
|
|
className="w-full"
|
|
/>
|
|
</div>
|
|
{/* <div>
|
|
<Textarea
|
|
value={formData.taskJson}
|
|
onChange={(val) => handleDataChange('taskJson', val)}
|
|
placeholder="请输入场景JSON"
|
|
className="w-full"
|
|
/>
|
|
</div> */}
|
|
</Dialog>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default SceneList; |