样式修改
This commit is contained in:
1 parent
23b9d401b7
commit
7548620683
433 files changed
+61042
-27
No files matched your search
@@ -6,6 +6,8 @@
|
||||
border-color:rgba(43, 143, 254, 0.6);
|
||||
border-radius:4px;
|
||||
backdrop-filter:blur(2px);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.item-nav{
|
||||
@@ -14,11 +16,12 @@
|
||||
background:rgba(0, 12, 28, 0.15);
|
||||
border:1px solid;
|
||||
border-color:rgba(85, 166, 254, 0.2);
|
||||
line-height:8px;
|
||||
line-height:28px;
|
||||
font-family:Noto Sans S Chinese;
|
||||
font-weight:500;
|
||||
color:rgba(255, 255, 255, 0.8);
|
||||
font-size:20px;
|
||||
padding-bottom:18px;
|
||||
text-align:center;
|
||||
margin: 10px;
|
||||
}
|
||||
@@ -132,9 +132,24 @@ const menuItems: MenuItem[] = [
|
||||
label: "COH中任务",
|
||||
icon: Folder,
|
||||
children: [
|
||||
{ id: "coh-task-3qfx", label: "3QFX", icon: ListTodo },
|
||||
{ id: "coh-task-fx", label: "任务FX", icon: ListTodo },
|
||||
{ id: "coh-task-xdfx", label: "XDFX", icon: ListTodo },
|
||||
{
|
||||
id: "coh-task-3qfx",
|
||||
label: "3QFX",
|
||||
icon: ListTodo,
|
||||
path: "/page/strategy/task-management/plan-task-sentiment",
|
||||
},
|
||||
{
|
||||
id: "coh-task-fx",
|
||||
label: "任务FX",
|
||||
icon: ListTodo,
|
||||
path: "/page/strategy/task-management/plan-task/plan-task-fx",
|
||||
},
|
||||
{
|
||||
id: "coh-task-xdfx",
|
||||
label: "XDFX",
|
||||
icon: ListTodo,
|
||||
path: "/page/strategy/task-management/plan-task/plan-task-action",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
@@ -130,8 +130,18 @@ const menuItems: MenuItem[] = [
|
||||
label: "XK中任务",
|
||||
icon: Folder,
|
||||
children: [
|
||||
{ id: "xk-task-jc", label: "任务JC", icon: ListTodo },
|
||||
{ id: "xk-task-dispatch", label: "任务调度", icon: ListTodo },
|
||||
{
|
||||
id: "xk-task-jc",
|
||||
label: "任务JC",
|
||||
icon: ListTodo,
|
||||
path: "/page/strategy/task-management/xk-task-jc",
|
||||
},
|
||||
{
|
||||
id: "xk-task-dispatch",
|
||||
label: "任务调度",
|
||||
icon: ListTodo,
|
||||
path: "/page/strategy/task-management/xk-task-dispatch",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -139,9 +149,24 @@ const menuItems: MenuItem[] = [
|
||||
label: "COH中任务",
|
||||
icon: Folder,
|
||||
children: [
|
||||
{ id: "coh-task-3qfx", label: "3QFX", icon: ListTodo },
|
||||
{ id: "coh-task-fx", label: "任务FX", icon: ListTodo },
|
||||
{ id: "coh-task-xdfx", label: "XDFX", icon: ListTodo },
|
||||
{
|
||||
id: "coh-task-3qfx",
|
||||
label: "3QFX",
|
||||
icon: ListTodo,
|
||||
path: "/page/strategy/task-management/plan-task-sentiment",
|
||||
},
|
||||
{
|
||||
id: "coh-task-fx",
|
||||
label: "任务FX",
|
||||
icon: ListTodo,
|
||||
path: "/page/strategy/task-management/plan-task/plan-task-fx",
|
||||
},
|
||||
{
|
||||
id: "coh-task-xdfx",
|
||||
label: "XDFX",
|
||||
icon: ListTodo,
|
||||
path: "/page/strategy/task-management/plan-task/plan-task-action",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -149,9 +174,24 @@ const menuItems: MenuItem[] = [
|
||||
label: "PG中任务",
|
||||
icon: Folder,
|
||||
children: [
|
||||
{ id: "pg-task-pg", label: "任务PG", icon: ListTodo },
|
||||
{ id: "pg-task-xdpg", label: "XDPG", icon: ListTodo },
|
||||
{ id: "pg-task-info-pg", label: "信息PG", icon: ListTodo },
|
||||
{
|
||||
id: "pg-task-pg",
|
||||
label: "任务PG",
|
||||
icon: ListTodo,
|
||||
path: "/page/strategy/task-management/effect-task-pg",
|
||||
},
|
||||
{
|
||||
id: "pg-task-xdpg",
|
||||
label: "XDPG",
|
||||
icon: ListTodo,
|
||||
path: "/page/strategy/task-management/effect-action-pg",
|
||||
},
|
||||
{
|
||||
id: "pg-task-info-pg",
|
||||
label: "信息PG",
|
||||
icon: ListTodo,
|
||||
path: "/page/strategy/task-management/effect-info-pg",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -159,9 +199,24 @@ const menuItems: MenuItem[] = [
|
||||
label: "TY中任务",
|
||||
icon: Folder,
|
||||
children: [
|
||||
{ id: "ty-task-xdty", label: "XDTY任务", icon: ListTodo },
|
||||
{ id: "ty-task-plan", label: "方案TY任务", icon: ListTodo },
|
||||
{ id: "ty-task-xxty", label: "XXTY任务", icon: ListTodo },
|
||||
{
|
||||
id: "ty-task-xdty",
|
||||
label: "XDTY任务",
|
||||
icon: ListTodo,
|
||||
path: "/page/strategy/task-management/mnty-task-mechanism",
|
||||
},
|
||||
{
|
||||
id: "ty-task-plan",
|
||||
label: "方案TY任务",
|
||||
icon: ListTodo,
|
||||
path: "/page/strategy/task-management/mnty-task-plan",
|
||||
},
|
||||
{
|
||||
id: "ty-task-xxty",
|
||||
label: "XXTY任务",
|
||||
icon: ListTodo,
|
||||
path: "/page/strategy/task-management/mnty-task-zsty",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
|
||||
@@ -9,11 +9,26 @@ export interface ChatMessage {
|
||||
agentName?: string;
|
||||
}
|
||||
|
||||
export interface toolsUser {
|
||||
email: string;
|
||||
id: string;
|
||||
is_active: boolean;
|
||||
is_superuser: boolean;
|
||||
is_verified: boolean;
|
||||
pages_limit: number;
|
||||
pages_used: string;
|
||||
yUserId: string;
|
||||
}
|
||||
|
||||
interface ChatContextType {
|
||||
messages: ChatMessage[];
|
||||
addMessage: (role: 'user' | 'assistant', content: string, type?: 'general' | 'agent' | 'compare', agentName?: string) => void;
|
||||
clearMessages: () => void;
|
||||
deleteMessage: (id: string) => void;
|
||||
startConfig: unknown;
|
||||
setStartConfig: (val: unknown) => void;
|
||||
toolsUser: toolsUser | null;
|
||||
setToolsUser: (val: toolsUser) => void;
|
||||
}
|
||||
|
||||
const ChatContext = createContext<ChatContextType | undefined>(undefined);
|
||||
@@ -28,6 +43,9 @@ export function ChatProvider({ children }: { children: ReactNode }) {
|
||||
}
|
||||
});
|
||||
|
||||
const [startConfig, setStartConfig] = useState<unknown>(null);
|
||||
const [toolsUser, setToolsUser] = useState<toolsUser | null>(null);
|
||||
|
||||
const saveToStorage = useCallback((newMessages: ChatMessage[]) => {
|
||||
try {
|
||||
localStorage.setItem('qa-chat-history', JSON.stringify(newMessages));
|
||||
@@ -66,7 +84,18 @@ export function ChatProvider({ children }: { children: ReactNode }) {
|
||||
}, [saveToStorage]);
|
||||
|
||||
return (
|
||||
<ChatContext.Provider value={{ messages, addMessage, clearMessages, deleteMessage }}>
|
||||
<ChatContext.Provider
|
||||
value={{
|
||||
messages,
|
||||
addMessage,
|
||||
clearMessages,
|
||||
deleteMessage,
|
||||
startConfig,
|
||||
setStartConfig,
|
||||
toolsUser,
|
||||
setToolsUser,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</ChatContext.Provider>
|
||||
);
|
||||
|
||||
@@ -48,16 +48,16 @@ const themes: Record<ThemeType, ThemeConfig> = {
|
||||
textSecondary: 'slate-400',
|
||||
primary: 'blue-500',
|
||||
primaryHover: 'blue-400',
|
||||
border: 'slate-700',
|
||||
accent: 'blue-600',
|
||||
border: 'blue-800',
|
||||
accent: 'cyan-400',
|
||||
success: 'emerald-500',
|
||||
warning: 'amber-500',
|
||||
error: 'rose-500',
|
||||
card: 'slate-800',
|
||||
cardHover: 'slate-700',
|
||||
input: 'slate-800',
|
||||
inputBorder: 'slate-600',
|
||||
overlay: 'slate-900/80',
|
||||
inputBorder: 'blue-800',
|
||||
overlay: 'slate-900/50',
|
||||
},
|
||||
},
|
||||
light: {
|
||||
|
||||
@@ -30,6 +30,13 @@ import LightAppNoteListPage from "./LightAppNoteListPage";
|
||||
import LightAppNoteQAPage from "./LightAppNoteQAPage";
|
||||
import TaskXkFrom from "@/strategy-components/components/task-components/TaskXkFrom";
|
||||
import TaskManagement from "@/strategy-components/components/task-components/TaskManagement";
|
||||
import TaskPgFrom from "@/strategy-components/components/task-components/TaskPgFrom";
|
||||
import FanganTyPage from "@/strategy-components/components/task-components/FanganTy";
|
||||
import XingdongTyPage from "@/strategy-components/components/task-components/XingdongTy";
|
||||
import XXTyPage from "@/strategy-components/components/task-components/XXTy";
|
||||
import SentimentAnalyzes from "@/strategy-components/components/task-components/SentimentAnalyzes";
|
||||
import TaskAnalyzes from "@/strategy-components/components/task-components/TaskAnalyzes";
|
||||
import { ActionAnalyzes } from "@/strategy-components/components/task-components/ActionAnalyzes";
|
||||
|
||||
export default function StrategyRoutes() {
|
||||
return (
|
||||
@@ -60,6 +67,93 @@ export default function StrategyRoutes() {
|
||||
/>
|
||||
}
|
||||
/>
|
||||
{/* pg中任务 */}
|
||||
<Route
|
||||
path="effect-task-pg"
|
||||
element={
|
||||
<TaskPgFrom
|
||||
gopath="task"
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="effect-action-pg"
|
||||
element={
|
||||
<TaskPgFrom
|
||||
gopath="xd"
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="effect-info-pg"
|
||||
element={
|
||||
<TaskPgFrom
|
||||
gopath="info"
|
||||
/>
|
||||
}
|
||||
/>
|
||||
{/* COH中任务 */}
|
||||
<Route
|
||||
path="plan-task-sentiment"
|
||||
element={
|
||||
<SentimentAnalyzes
|
||||
title="3QFX"
|
||||
category="规划任务"
|
||||
subCategory="规划任务"
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="plan-task/plan-task-fx"
|
||||
element={
|
||||
<TaskAnalyzes
|
||||
title="任务FX"
|
||||
category="规划任务"
|
||||
subCategory="规划任务"
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="plan-task/plan-task-action"
|
||||
element={
|
||||
<ActionAnalyzes
|
||||
title="xdfx"
|
||||
category="规划任务"
|
||||
subCategory="规划任务"
|
||||
/>
|
||||
}
|
||||
/>
|
||||
{/* MNTY任务 */}
|
||||
<Route
|
||||
path="mnty-task-mechanism"
|
||||
element={
|
||||
<XingdongTyPage
|
||||
title="XDTY任务"
|
||||
category="MNTY任务"
|
||||
subCategory="MNTY任务"
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="mnty-task-plan"
|
||||
element={
|
||||
<FanganTyPage
|
||||
title="方案TY任务"
|
||||
category="MNTY任务"
|
||||
subCategory="MNTY任务"
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="mnty-task-zsty"
|
||||
element={
|
||||
<XXTyPage
|
||||
title="XXTY任务"
|
||||
category="MNTY任务"
|
||||
subCategory="MNTY任务"
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</Route>
|
||||
{/* 轻应用 */}
|
||||
<Route path="light-app/new-note" element={<LightAppNewNotePage />} />
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
import service from "@/utils/request";
|
||||
export const requestCrudService = {
|
||||
get: async (endpoint, params = {}) => {
|
||||
try {
|
||||
const response = await service.get(endpoint, { params });
|
||||
return response;
|
||||
} catch (error) {
|
||||
throw new Error(`GET request failed: ${error.message}`);
|
||||
}
|
||||
},
|
||||
|
||||
post: async (endpoint, data = {}) => {
|
||||
try {
|
||||
const response = await service.post(endpoint, data);
|
||||
return response;
|
||||
} catch (error) {
|
||||
throw new Error(`POST request failed: ${error.message}`);
|
||||
}
|
||||
},
|
||||
|
||||
put: async (endpoint, data = {}) => {
|
||||
try {
|
||||
const response = await service.put(endpoint, data);
|
||||
return response;
|
||||
} catch (error) {
|
||||
throw new Error(`PUT request failed: ${error.message}`);
|
||||
}
|
||||
},
|
||||
|
||||
update: async (endpoint, data = {}) => {
|
||||
try {
|
||||
const response = await service.patch(endpoint, data);
|
||||
return response;
|
||||
} catch (error) {
|
||||
throw new Error(`patch request failed: ${error.message}`);
|
||||
}
|
||||
},
|
||||
|
||||
delete: async (endpoint) => {
|
||||
try {
|
||||
const response = await service.delete(endpoint);
|
||||
return response;
|
||||
} catch (error) {
|
||||
throw new Error(`DELETE request failed: ${error.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
import { environment } from '@/environments/environment';
|
||||
import { requestCrudService } from '@/service/request-axios-service';
|
||||
|
||||
export interface GetOneParam {
|
||||
fields?: string[]; // 指定返回的字段 id,name
|
||||
join?: string[]; // 指定关联表,如 ['user||id,name', 'relation||id']
|
||||
cache?: 0 | 1; // 0直接DB,1缓存,默认1缓存
|
||||
}
|
||||
export interface GetAllParam {
|
||||
fields?: string[] | string; // 指定返回的字段: 多个字段如 'id', 'name'
|
||||
filter?: string[]; // 指定过滤条件,如 ['name||$eq||batman', 'id||$in||1,2,3', 'field||$isnull', 'relation.id||$eq||1']
|
||||
or?: string[]; // 同filter
|
||||
join?: string[]; // 指定关联表,如 ['user||id,name', 'relation||id']
|
||||
cache?: 0 | 1; // 0直接DB,1缓存,默认1缓存
|
||||
page?: number; // 页码(会被转换成offset)
|
||||
offset?: number; // 偏移量
|
||||
limit?: number; // 每页数量
|
||||
sort?: string[]; // 排序,如 '['name,ASC', 'id,DESC]'
|
||||
s?: string;
|
||||
joinTaskAction?: boolean; //是否连行动表查询
|
||||
joinTaskActionBoci?: boolean; //策略分析列表使用
|
||||
}
|
||||
|
||||
export interface GetAllResponse {
|
||||
data: any[];
|
||||
page: number;
|
||||
pageCount: number;
|
||||
count: number;
|
||||
total: number;
|
||||
errMsg?: string;
|
||||
}
|
||||
let deepCopy = (str: any) => {
|
||||
return JSON.parse(JSON.stringify(str));
|
||||
}
|
||||
export class RequestCrudService {
|
||||
|
||||
/**
|
||||
* 获取单个对象
|
||||
* @param url 地址
|
||||
* @param id ID
|
||||
* @param params 参数
|
||||
* @returns 单个对象
|
||||
*/
|
||||
async getOne(url: string, id: number, params?: GetOneParam): Promise<any> {
|
||||
const getUrl = `${url}/${id}`;
|
||||
const res = await requestCrudService.get(getUrl, params);
|
||||
if (res) return res;
|
||||
// this.notification.create('error', getUrl, `${res.data}`);
|
||||
return {};
|
||||
}
|
||||
/**
|
||||
* 获取多个对象
|
||||
* @param url 地址
|
||||
* @param params 参数
|
||||
* @returns 多个对象数组
|
||||
*/
|
||||
async getAll(url: string, params: GetAllParam | null = null): Promise<GetAllResponse> {
|
||||
const formattedParams = deepCopy(params || {});
|
||||
// 分页默认值
|
||||
if (!params || !params.limit) formattedParams.limit = 999;
|
||||
if (!params || !params.page) formattedParams.page = 1;
|
||||
// 计算offset
|
||||
if (params && formattedParams.page) {
|
||||
formattedParams.offset = (formattedParams.page - 1) * formattedParams.limit!;
|
||||
delete formattedParams.page;
|
||||
}
|
||||
const res = await requestCrudService.get(url, formattedParams);
|
||||
if (res.status === 200) return res.data;
|
||||
// this.notification.create('error', url, `${res.data}`);
|
||||
return {
|
||||
count: 0,
|
||||
data: [],
|
||||
page: 1,
|
||||
pageCount: 1,
|
||||
total: 0
|
||||
};
|
||||
}
|
||||
/**
|
||||
* 创建单个对象
|
||||
* @param url 地址
|
||||
* @param data 单个对象
|
||||
* @returns 被创建的单个对象
|
||||
*/
|
||||
async createOne(url: string, data: any) {
|
||||
const res = await requestCrudService.post(url, data);
|
||||
if (res.status === 201) return res.data;
|
||||
// this.notification.create('error', url, `${res.data}`);
|
||||
return {};
|
||||
}
|
||||
/**
|
||||
* 创建多个对象
|
||||
* @param url 地址
|
||||
* @param data 多个对象
|
||||
* @returns 被创建的多个对象
|
||||
*/
|
||||
async createMulti(url: string, data: any[]) {
|
||||
const res = await requestCrudService.post(url, {
|
||||
bulk: data
|
||||
});
|
||||
if (res.status === 201) return res.data;
|
||||
// this.notification.create('error', url, `${res.data}`);
|
||||
return [];
|
||||
}
|
||||
/**
|
||||
* 更新单个对象
|
||||
* @param url 地址
|
||||
* @param id ID
|
||||
* @param data 单个对象部分键值对
|
||||
* @returns 被修改的单个对象
|
||||
*/
|
||||
async updateOne(url: string, id: number, data: any) {
|
||||
const res = await requestCrudService.update(`${url}/${id}`, data);
|
||||
if (res.status === 200) return res.data;
|
||||
// this.notification.create('error', url, `${res.data}`);
|
||||
return {};
|
||||
}
|
||||
/**
|
||||
* 删除单个对象
|
||||
* @param url 地址
|
||||
* @param id ID
|
||||
* @returns 操作结果
|
||||
*/
|
||||
async deleteOne(url: string, id: number) {
|
||||
const res = await requestCrudService.delete(`${url}/${id}`);
|
||||
if (res.status === 200) return res.data;
|
||||
// this.notification.create('error', url, `${res.data}`);
|
||||
return 'error';
|
||||
}
|
||||
// get 请求
|
||||
async simpleGet(url: string, params?: any): Promise<any> {
|
||||
const res = await requestCrudService.get(url, params);
|
||||
if (res.status === 200) return res.data;
|
||||
return res.data;
|
||||
}
|
||||
// post 请求
|
||||
async simplePost(url: string, data: any) {
|
||||
const res = await requestCrudService.post(url, data);
|
||||
if (res.status === 200) return res.data;
|
||||
return res.data;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
import toolsService from "@/utils/tools-request";
|
||||
const service = toolsService;
|
||||
export const toolsCrudService = {
|
||||
get: async (endpoint, params = {}) => {
|
||||
try {
|
||||
const response = await service.get(endpoint, { params });
|
||||
return response;
|
||||
} catch (error) {
|
||||
throw new Error(`GET request failed: ${error.message}`);
|
||||
}
|
||||
},
|
||||
|
||||
post: async (endpoint, data = {}) => {
|
||||
try {
|
||||
const response = await service.post(endpoint, data);
|
||||
return response;
|
||||
} catch (error) {
|
||||
throw new Error(`POST request failed: ${error.message}`);
|
||||
}
|
||||
},
|
||||
|
||||
put: async (endpoint, data = {}) => {
|
||||
try {
|
||||
const response = await service.put(endpoint, data);
|
||||
return response;
|
||||
} catch (error) {
|
||||
throw new Error(`PUT request failed: ${error.message}`);
|
||||
}
|
||||
},
|
||||
patch: async (endpoint, data = {}) => {
|
||||
try {
|
||||
const response = await service.patch(endpoint, data);
|
||||
return response;
|
||||
} catch (error) {
|
||||
throw new Error(`PATCH request failed: ${error.message}`);
|
||||
}
|
||||
},
|
||||
|
||||
update: async (endpoint, data = {}) => {
|
||||
try {
|
||||
const response = await service.patch(endpoint, data);
|
||||
return response;
|
||||
} catch (error) {
|
||||
throw new Error(`patch request failed: ${error.message}`);
|
||||
}
|
||||
},
|
||||
|
||||
delete: async (endpoint) => {
|
||||
try {
|
||||
const response = await service.delete(endpoint);
|
||||
return response;
|
||||
} catch (error) {
|
||||
throw new Error(`DELETE request failed: ${error.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,207 @@
|
||||
import { useTheme } from '@/contexts/ThemeContext';
|
||||
import { actionColumnItem, ActionListItem, TaskListItem, taskStatusList } from './TaskManagement';
|
||||
import { Bot, ChevronRight, Download, Edit, Eye, FileText, Layers, MoreHorizontal, Play, Search, User, Users, Workflow } from 'lucide-react';
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import { format } from 'date-fns';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { Col, Empty, Loading, Pagination, Progress, Row } from 'tdesign-react';
|
||||
import { requestCrudService } from '@/service/request-axios-service';
|
||||
import { ActionColumn } from './ActionColumn';
|
||||
|
||||
interface ActionAnalyzesProps {
|
||||
TaskType?: string;
|
||||
title: string;
|
||||
category: string;
|
||||
subCategory: string;
|
||||
}
|
||||
|
||||
const service = requestCrudService;
|
||||
const zncmUrl = import.meta.env.VITE_API_BASE_URL || '';
|
||||
export const ActionAnalyzes: React.FC<ActionAnalyzesProps> = ({ title, category, subCategory}) => {
|
||||
const { theme } = useTheme();
|
||||
const isDark = theme === 'dark';
|
||||
// 主题颜色变量
|
||||
const cardBg = isDark ? 'bg-slate-800/30' : 'bg-white';
|
||||
const cardBorder = isDark ? 'border-blue-800/30' : 'border-red-100';
|
||||
const textPrimary = isDark ? 'text-white' : 'text-gray-900';
|
||||
const textSecondary = isDark ? 'text-slate-400' : 'text-gray-600';
|
||||
const textMuted = isDark ? 'text-slate-500' : 'text-gray-400';
|
||||
const inputBg = isDark ? 'bg-slate-900/50' : 'bg-white';
|
||||
const navigate = useNavigate();
|
||||
const [searchQuery, setSearchQuery] = useState<string>('');
|
||||
const [taskList, setTaskList] = useState<TaskListItem[]>([]); // 任务列表
|
||||
const [taskId, setTaskId] = useState<number>(0); // 任务
|
||||
const [actionColumnList, setActionColumnList] = useState<actionColumnItem[]>([]);
|
||||
const [taskLoading, setTaskLoading] = useState<boolean>(false);
|
||||
const [actionLoading, setActionLoading] = useState<boolean>(false);
|
||||
// 当前页码,默认为第 1 页
|
||||
const [pageNum, setPageNum] = useState<number>(1);
|
||||
// 每页显示数量,默认为 10 条
|
||||
const [pageSize, setPageSize] = useState<number>(10); // 每页显示 10 条
|
||||
const [total, setTotal] = useState<number>(0); // 查询任务总数
|
||||
const state = useRef({
|
||||
pageIndex: 1,
|
||||
pageSize: 10
|
||||
})
|
||||
// 回车处理
|
||||
const handleKeyDown = async (e) => {
|
||||
if ((e.metaKey || e.ctrlKey || e.shiftKey) && e.key === "Enter") {
|
||||
e.preventDefault();
|
||||
} else if (e.keyCode === 13) {
|
||||
e.preventDefault();
|
||||
await taskListSearch()
|
||||
} else if (e.key === "/") {
|
||||
e.preventDefault();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 页码变化时的回调函数
|
||||
* 当用户切换页码时触发,更新页码并重新获取数据
|
||||
* @param value - 新的页码值
|
||||
*/
|
||||
const pageIndexChange = async (value: number) => {
|
||||
// 更新 ref 中存储的页码值
|
||||
// 更新页码状态,触发组件重新渲染
|
||||
setPageNum(value);
|
||||
state.current.pageIndex = value;
|
||||
await taskListSearch();
|
||||
}
|
||||
|
||||
// 任务查询
|
||||
const taskListSearch = async () => {
|
||||
const param = {
|
||||
keyword: '',
|
||||
pageNum: state.current.pageIndex,
|
||||
pageSize: state.current.pageSize
|
||||
}
|
||||
setTaskLoading(true);
|
||||
const res = await service.post(`${zncmUrl}/action/cop-action-tasks`, param);
|
||||
setTaskLoading(false);
|
||||
console.log('任务搜索', res);
|
||||
const result = res.data;
|
||||
result.forEach((item: TaskListItem) => {
|
||||
item.progress = item.endCount / item.sumAll || 0; // 获取已完成和未完成的占比
|
||||
item.taskStatusData = taskStatusList[item.taskStatus] // 根据任务状态码获取相关信息
|
||||
})
|
||||
setTaskList(result || []); // 设置任务列表页
|
||||
const totalRes = await service.post(`${zncmUrl}/action/cop-action-tasks-count`, param); // 查询任务总数
|
||||
console.log('totalRes', totalRes)
|
||||
setTotal(totalRes.data || 0); // 设置总数
|
||||
if (result.length > 0) {
|
||||
setTaskId(result[0].id);
|
||||
await actionListSearch(result[0].id);
|
||||
}
|
||||
}
|
||||
|
||||
// 行动查询
|
||||
const actionListSearch = async (taskId: number) => {
|
||||
const param = {
|
||||
taskId: taskId,
|
||||
}
|
||||
setActionLoading(true);
|
||||
const res = await service.get(`${zncmUrl}/action/cop-task-actions`, param);
|
||||
console.log('行动查询', res);
|
||||
setActionLoading(false);
|
||||
const result = res.data;
|
||||
setActionColumnList([
|
||||
{ columnName: '待分析', icon: '待CEH.svg', actionList: result.collect || [] },
|
||||
{ columnName: 'CEH中', icon: '进行中.svg', actionList: result.collect1 || [] },
|
||||
{ columnName: 'CEH完成', icon: '完成.svg', actionList: result.collect2 || [] },
|
||||
{ columnName: '已终止', icon: '已终止.svg', actionList: result.endDate || [] },
|
||||
]);
|
||||
}
|
||||
|
||||
const taskDataChange = async (taskId: number) => {
|
||||
setTaskId(taskId);
|
||||
await actionListSearch(taskId);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
taskListSearch();
|
||||
}, [])
|
||||
return (
|
||||
<div className="h-full flex flex-col">
|
||||
<Row className="h-full flex flex-col">
|
||||
<Col span={2} className="h-full">
|
||||
<div className={`h-[calc(100vh-100px)] p-[10px] ${isDark ? 'bg-[rgba(3,38,80,0.6)]' : 'bg-white'} shadow-[2px_0px_6px_rgba(0,0,0,0.12)]`} >
|
||||
{/* <div className="flex justify-between">
|
||||
<span>筛选</span>
|
||||
<span className="flex items-center">
|
||||
<span>下发时间排序</span>
|
||||
</span>
|
||||
</div> */}
|
||||
<div className="relative flex-1 max-w-md">
|
||||
<Search className={`absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 ${textMuted}`} />
|
||||
<input
|
||||
type="text"
|
||||
placeholder="搜索任务名称"
|
||||
value={searchQuery}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
onKeyDown={(e) => handleKeyDown(e)}
|
||||
className={`w-full pl-10 pr-4 py-2 border rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-red-500/20 transition-colors ${inputBg} ${cardBorder} ${textPrimary} placeholder:${textMuted}`}
|
||||
/>
|
||||
</div>
|
||||
{/* 任务列表 */}
|
||||
<Loading loading={taskLoading}>
|
||||
<div className="h-[calc(100vh-200px)] overflow-y-auto pr-[10px]">
|
||||
{
|
||||
taskList.map((item: TaskListItem, index: number) => (
|
||||
<div key={index} className={`${isDark ? 'bg-[#0062d91a] border-[#55a6fe1a] shadow-[0_3px_4px_#8b8b8b0f] dark-theme' : 'bg-white border-[#f5f5f5] shadow-[2px_0px_6px_rgba(0,0,0,0.08)] task-item-active'} p-[10px] mt-[10px] mb-[10px] rounded-[4px] border ${taskId === item.id && 'task-active'} cursor-pointer`} onClick={() => taskDataChange(item.id)}>
|
||||
<div className="text-[var(--text-color)] text-[17px] mb-[5px] font-[500]">{item.taskName}</div>
|
||||
<div className="text-[#7f7f7f] mb-[5px]">{format(new Date(item.taskStartDate), 'yyyy-MM-dd HH:mm:ss')}</div>
|
||||
<div className="flex items-center mb-[8px]">
|
||||
<img src={`/icon/${item?.taskStatusData?.icon}`} alt="" />
|
||||
<span className="ml-[5px] leading-[16px]">{item?.taskStatusData?.label}</span>
|
||||
</div>
|
||||
<div className="pb-[10px] pt-[10px]">
|
||||
<div className="border-b border-[#e8e8e8]"></div>
|
||||
</div>
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="h-[24px] item-active pl-[10px] pr-[10px] task-tab-color leading-[24px] rounded-[50px] bg-[var(--bg-color)]">
|
||||
完成{item.endCount}/{item.sumAll}
|
||||
</div>
|
||||
<div className="pl-[20px] flex-1">
|
||||
<Progress percentage={item.progress} label={false} color={item?.taskStatusData?.color} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</Loading>
|
||||
{/* 分页 */}
|
||||
<div>
|
||||
<Pagination
|
||||
total={total}
|
||||
size="small"
|
||||
theme="simple"
|
||||
current={pageNum}
|
||||
pageSize={pageSize}
|
||||
showPageSize={false}
|
||||
onCurrentChange={(page) => pageIndexChange(Number(page))}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Col>
|
||||
|
||||
<Col span={10} className="h-full">
|
||||
<div className="h-full flex flex-col p-[0_10px]">
|
||||
<Loading loading={actionLoading}>
|
||||
<div className="flex grid grid-cols-4 gap-[10px] pl-[10px]">
|
||||
{
|
||||
actionColumnList.map((item: actionColumnItem, index: number) => (
|
||||
<ActionColumn key={index} icon={item.icon} columnIndex={index} columnName={item.columnName} actionCount={item.actionList.length} actionList={item.actionList} title={title} category={category} subCategory={subCategory} />
|
||||
))
|
||||
}
|
||||
{
|
||||
actionColumnList.length === 0 && <Empty/>
|
||||
}
|
||||
</div>
|
||||
</Loading>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
import { useTheme } from '@/contexts/ThemeContext';
|
||||
import { ActionListItem, TaskListItem, taskStatusList } from './TaskManagement';
|
||||
import { Bot, ChevronRight, Download, Edit, Eye, FileText, Layers, MoreHorizontal, Play, Search, User, Users, Workflow } from 'lucide-react';
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import { format } from 'date-fns';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { Col, Empty, Pagination, Progress, Row } from 'tdesign-react';
|
||||
import { requestCrudService } from '@/service/request-axios-service';
|
||||
|
||||
interface ActionColumnProps {
|
||||
TaskType?: string;
|
||||
title: string;
|
||||
category: string;
|
||||
subCategory: string;
|
||||
icon: string;
|
||||
columnIndex: number;
|
||||
columnName: string;
|
||||
actionCount: number;
|
||||
actionList: ActionListItem[];
|
||||
}
|
||||
|
||||
const service = requestCrudService;
|
||||
const zncmUrl = import.meta.env.VITE_API_BASE_URL || '';
|
||||
export const ActionColumn: React.FC<ActionColumnProps> = ({ title, category, subCategory, icon, columnName, columnIndex, actionCount, actionList }) => {
|
||||
const { theme } = useTheme();
|
||||
const isDark = theme === 'dark';
|
||||
// 主题颜色变量
|
||||
const cardBg = isDark ? 'bg-slate-800/30' : 'bg-white';
|
||||
const cardBorder = isDark ? 'border-blue-800/30' : 'border-red-100';
|
||||
const textPrimary = isDark ? 'text-white' : 'text-gray-900';
|
||||
const textSecondary = isDark ? 'text-slate-400' : 'text-gray-600';
|
||||
const textMuted = isDark ? 'text-slate-500' : 'text-gray-400';
|
||||
const inputBg = isDark ? 'bg-slate-900/50' : 'bg-white';
|
||||
const navigate = useNavigate();
|
||||
const [searchQuery, setSearchQuery] = useState<string>('');
|
||||
// 当前页码,默认为第 1 页
|
||||
const [pageNum, setPageNum] = useState<number>(1);
|
||||
// 每页显示数量,默认为 10 条
|
||||
const [pageSize, setPageSize] = useState<number>(10); // 每页显示 10 条
|
||||
const [total, setTotal] = useState<number>(0); // 查询任务总数
|
||||
const state = useRef({
|
||||
pageIndex: 1,
|
||||
pageSize: 10
|
||||
})
|
||||
/**
|
||||
* 页码变化时的回调函数
|
||||
* 当用户切换页码时触发,更新页码并重新获取数据
|
||||
* @param value - 新的页码值
|
||||
*/
|
||||
const pageIndexChange = async (value: number) => {
|
||||
// 更新 ref 中存储的页码值
|
||||
// 更新页码状态,触发组件重新渲染
|
||||
setPageNum(value);
|
||||
state.current.pageIndex = value;
|
||||
}
|
||||
|
||||
const actionRouterChange = (item: ActionListItem) => {
|
||||
if (item.actionStatus !== 0) {
|
||||
window.open(`https://ch1.b.uat.4.cn/web/#/action/item-aiceh/step-analysis-three?id=${item.id}`);
|
||||
} else {
|
||||
window.open(`https://ch1.b.uat.4.cn/web/#/action/item-aiceh/step-action-topic?id=${item.id}&isPassApply=false`);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={`h-[calc(100vh-100px)] p-[10px] flex-1 ${isDark? 'dark-action-bg' : `bg-white action-bg${columnIndex} shadow-[2px_0px_6px_rgba(0,0,0,0.12)]`} rounded-[4px]`}>
|
||||
<div className="mb-[10px]">
|
||||
<div className="flex items-center mb-[20px]">
|
||||
<img src={`/icon/${icon}`} className="w-[24px] mr-[10px]" alt="" />
|
||||
<span className="text-[#222222] text-[18px] font-bold mr-[20px]">{columnName}</span>
|
||||
<span className="text-[#222222] text-[18px] font-bold mr-[20px]">{actionCount}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="h-[calc(100vh-200px)] overflow-y-auto pr-[10px]">
|
||||
{
|
||||
actionList.map((item: ActionListItem, index: number) => (
|
||||
<div key={index} className={`${isDark? 'dark-action-item' : 'bg-white border border-[#f5f5f5] shadow-[0_1px_16px_#0000000f] action-item'} rounded-md px-4 py-2.5 mb-[10px] hover:border-[var(--card-border)] cursor-pointer`} onClick={() => actionRouterChange(item)}>
|
||||
<div className="line-clamp-4 overflow-hidden mb-[10px]">
|
||||
{
|
||||
item.actionName
|
||||
}
|
||||
</div>
|
||||
<div className="flex items-center mb-[10px]">
|
||||
<span className={`${isDark? 'bg-[#0062d94d] text-[#aaceff]' :'bg-[#f5f5f5]'} leading-[16px] p-[5px_10px] rounded-[2px]`}>
|
||||
{item.level === 1 ? '主要行动' : '次要行动'}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<span className={`${isDark? 'bg-[#0062d94d] text-[#aaceff]' :'bg-[#f5f5f5]'} leading-[16px] p-[5px_10px] rounded-[2px]`}>
|
||||
{item.deptName}
|
||||
</span>
|
||||
<span className="border border-[var(--color-main)] text-[var(--color-main)] cursor-pointer leading-[16px] ml-[10px] p-[5px_10px] rounded-[2px]">
|
||||
{
|
||||
item.actionStatus === 0 ? <span>XD签收</span> : <span>开始CEH</span>
|
||||
}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
))
|
||||
}
|
||||
{
|
||||
actionList.length === 0 && <Empty image={
|
||||
<img
|
||||
src="/icon/empty-list.png"
|
||||
alt="empty"
|
||||
className="w-[135px]"
|
||||
/>
|
||||
} />
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import "./css/iframe.css";
|
||||
|
||||
import { useTheme } from "@/contexts/ThemeContext";
|
||||
|
||||
interface FanganTyPageProps {
|
||||
title: string;
|
||||
category: string;
|
||||
subCategory: string;
|
||||
}
|
||||
|
||||
const TYURL = import.meta.env.VITE_AIPI_B2ACTOR_URL || "";
|
||||
|
||||
const FanganTyPage: React.FC<FanganTyPageProps> = () => {
|
||||
const { theme } = useTheme();
|
||||
const isDark = theme === "dark";
|
||||
const [iframeUrl, setIframeUrl] = useState("");
|
||||
useEffect(() => {
|
||||
initIframeUrl();
|
||||
}, []);
|
||||
const initIframeUrl = () => {
|
||||
const url = `${TYURL}/fromB/toPage?defaultPage=iframeFaty`;
|
||||
console.log({ iframeUrl: url });
|
||||
setIframeUrl(url);
|
||||
};
|
||||
return (
|
||||
<div className="iframe-container">
|
||||
<iframe src={iframeUrl} className="full-size-iframe"></iframe>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default FanganTyPage;
|
||||
@@ -0,0 +1,90 @@
|
||||
import { useTheme } from '@/contexts/ThemeContext';
|
||||
import { TaskListItem } from './TaskManagement';
|
||||
import { ChevronRight, FileText } from 'lucide-react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { TaskCard } from './TaskCard';
|
||||
import { Pagination } from 'tdesign-react';
|
||||
|
||||
interface KanbanColumnProps {
|
||||
title: string;
|
||||
count: number;
|
||||
color: string;
|
||||
tasks: TaskListItem[];
|
||||
taskType: string;
|
||||
TaskDataType?: string; // 区分来源类型
|
||||
pageIndexSearch: (taskType: string, pageIndex: number) => void;
|
||||
handleStartAnalysis: (task: TaskListItem) => void
|
||||
}
|
||||
|
||||
export const KanbanColumn: React.FC<KanbanColumnProps> = ({ title, count, color, tasks,taskType, TaskDataType = 'task' , pageIndexSearch, handleStartAnalysis }) => {
|
||||
const { theme } = useTheme();
|
||||
const isDark = theme === 'dark';
|
||||
const textPrimary = isDark ? 'text-white' : 'text-gray-900';
|
||||
const textMuted = isDark ? 'text-slate-500' : 'text-gray-400';
|
||||
// 当前页码,默认为第 1 页
|
||||
const [pageNum, setPageNum] = useState<number>(1);
|
||||
// 每页显示数量,默认为 10 条
|
||||
const [pageSize, setPageSize] = useState<number>(10); // 每页显示 10 条
|
||||
/**
|
||||
* 页码变化时的回调函数
|
||||
* 当用户切换页码时触发,更新页码并重新获取数据
|
||||
* @param value - 新的页码值
|
||||
*/
|
||||
const pageIndexChange = async (value: number) => {
|
||||
// 更新 ref 中存储的页码值
|
||||
// 更新页码状态,触发组件重新渲染
|
||||
console.log('taskType', taskType)
|
||||
setPageNum(value);
|
||||
await pageIndexSearch(taskType, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 每页显示数量变化时的回调函数
|
||||
* 当用户切换每页显示条数时触发,更新每页数量并重新获取数据
|
||||
* @param value - 新的每页显示数量
|
||||
*/
|
||||
const pageSizeChange = async (value: number) => {
|
||||
// 更新 ref 中存储的每页显示数量
|
||||
// 更新每页显示数量状态,触发组件重新渲染
|
||||
setPageSize(value);
|
||||
}
|
||||
return (
|
||||
<div className="flex flex-col min-h-0 flex-1">
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className={`w-2 h-2 rounded-full ${color}`} />
|
||||
<h2 className={`font-semibold text-sm ${textPrimary}`}>{title}</h2>
|
||||
<span className={`px-2 py-0.5 rounded-full text-xs ${isDark ? 'bg-slate-700/50 text-slate-400' : 'bg-red-50 text-red-500'
|
||||
}`}>{count}</span>
|
||||
</div>
|
||||
{/* <button className={`p-1 rounded transition-colors ${isDark ? 'hover:bg-slate-700/50' : 'hover:bg-red-50'
|
||||
}`}>
|
||||
<ChevronRight className={`w-4 h-4 ${textMuted}`} />
|
||||
</button> */}
|
||||
<div>
|
||||
<Pagination
|
||||
total={count}
|
||||
size="small"
|
||||
theme="simple"
|
||||
current={pageNum}
|
||||
pageSize={pageSize}
|
||||
maxPageBtn={3}
|
||||
showPageSize={false}
|
||||
totalContent={false}
|
||||
onCurrentChange={(page) => pageIndexChange(Number(page))}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex-1 overflow-y-auto space-y-3 pr-1 scrollbar-thin">
|
||||
{tasks.length === 0 ? (
|
||||
<div className={`flex flex-col items-center justify-center py-8 ${textMuted}`}>
|
||||
<FileText className="w-8 h-8 mb-2 opacity-50" />
|
||||
<p className="text-sm">暂无任务</p>
|
||||
</div>
|
||||
) : (
|
||||
tasks.map(task => <TaskCard key={task.id} task={task} TaskType={TaskDataType} handleStartAnalysis={handleStartAnalysis}/>)
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
+391
@@ -0,0 +1,391 @@
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import {
|
||||
Plus,
|
||||
Search,
|
||||
Eye,
|
||||
Play,
|
||||
Pause,
|
||||
Clock,
|
||||
User,
|
||||
ChevronRight,
|
||||
Filter,
|
||||
FileText,
|
||||
AlertTriangle,
|
||||
CheckCircle,
|
||||
XCircle,
|
||||
MoreHorizontal,
|
||||
Edit,
|
||||
Download,
|
||||
Bot,
|
||||
Users,
|
||||
Workflow,
|
||||
X,
|
||||
ArrowRight,
|
||||
Target,
|
||||
Layers,
|
||||
} from 'lucide-react';
|
||||
import { useTheme } from '@/contexts/ThemeContext';
|
||||
// 导入请求服务,用于与后端 API 交互
|
||||
import { requestCrudService } from '@/service/request-axios-service';
|
||||
import { KanbanColumn } from './KanbanColumn';
|
||||
import { TaskListItem } from './TaskManagement';
|
||||
import { DateRangePicker, DateRangePickerProps, DateRangeValue, Loading } from 'tdesign-react';
|
||||
import dayjs from 'dayjs';
|
||||
import { useChatContext } from '@/contexts/ChatContext';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
interface TaskListPageProps {
|
||||
title: string;
|
||||
category: string;
|
||||
subCategory: string;
|
||||
}
|
||||
|
||||
interface stateItem {
|
||||
startTime: string,
|
||||
endTime: string,
|
||||
task: TaskListItem,
|
||||
}
|
||||
|
||||
type TaskType = 'single' | 'consultation' | 'scene' | 'multi-agent' | 'task-search';
|
||||
type TaskStatus = 'draft' | 'pending' | 'running' | 'paused' | 'blocked' | 'completed';
|
||||
type ColumnType = 'unanalyzed' | 'analyzing' | 'completed';
|
||||
|
||||
const service = requestCrudService;
|
||||
const zncmUrl = import.meta.env.VITE_API_BASE_URL || '';
|
||||
|
||||
const SentimentAnalyzes: React.FC<TaskListPageProps> = ({ title, category, subCategory}) => {
|
||||
const { theme } = useTheme();
|
||||
const isDark = theme === 'dark';
|
||||
const router = useNavigate();
|
||||
// 主题颜色变量
|
||||
const cardBg = isDark ? 'bg-slate-800/30' : 'bg-white';
|
||||
const cardBorder = isDark ? 'border-blue-800/30' : 'border-red-100';
|
||||
const textPrimary = isDark ? 'text-white' : 'text-gray-900';
|
||||
const textSecondary = isDark ? 'text-slate-400' : 'text-gray-600';
|
||||
const textMuted = isDark ? 'text-slate-500' : 'text-gray-400';
|
||||
const inputBg = isDark ? 'bg-slate-900/50' : 'bg-white';
|
||||
const { setStartConfig } = useChatContext();
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
const [modeFilter, setModeFilter] = useState<string>('all');
|
||||
const [showAnalysisModal, setShowAnalysisModal] = useState(false);
|
||||
const [selectedTaskForAnalysis, setSelectedTaskForAnalysis] = useState<TaskListItem | null>(null);
|
||||
const [unanalyzedTotal, setUnanalyzedTotal] = useState<number>(0); // 未分析
|
||||
const [runningTotal, setRunningTotal] = useState<number>(0); // 分析中
|
||||
const [completedTotal, setCompletedTotal] = useState<number>(0); // 已完成
|
||||
const [unanalyzedList, setUnanalyzedList] = useState<TaskListItem[]>([]); // 未分析
|
||||
const [runningList, setRunningList] = useState<TaskListItem[]>([]); // 分析中
|
||||
const [completedList, setCompletedList] = useState<TaskListItem[]>([]); // 已完成
|
||||
const [unanalyzedLoading, setUnanalyzedLoading] = useState<boolean>(false); // 未分析
|
||||
const [runningLoading, setRunningLoading] = useState<boolean>(false); // 分析中
|
||||
const [completedLoading, setCompletedLoading] = useState<boolean>(false); // 已完成
|
||||
const [presets] = useState<DateRangePickerProps['presets']>({
|
||||
最近7天: [dayjs().subtract(6, 'day').toDate(), dayjs().toDate()],
|
||||
最近3天: [dayjs().subtract(2, 'day').toDate(), dayjs().toDate()],
|
||||
今天: [dayjs().toDate(), dayjs().toDate()],
|
||||
});
|
||||
const [range, setRange] = useState<DateRangeValue>([]);
|
||||
const state = useRef<stateItem>({
|
||||
startTime: '',
|
||||
endTime: '',
|
||||
task: {} as TaskListItem,
|
||||
})
|
||||
|
||||
// 时间日期选择器查询
|
||||
const setRangeChange = async (val) => {
|
||||
setRange(val);
|
||||
console.log('时间日期', val);
|
||||
state.current.startTime = '';
|
||||
state.current.endTime = '';
|
||||
if (val.length > 0) {
|
||||
state.current.startTime = val[0];
|
||||
state.current.endTime = val[1];
|
||||
}
|
||||
|
||||
await taskDataChange();
|
||||
}
|
||||
|
||||
// 处理开始分析按钮点击
|
||||
const handleStartAnalysis = (task: TaskListItem) => {
|
||||
setSelectedTaskForAnalysis(task);
|
||||
state.current.task = task;
|
||||
setShowAnalysisModal(true);
|
||||
};
|
||||
|
||||
// 处理分析模式选择
|
||||
const handleSelectAnalysisMode = (mode: TaskType) => {
|
||||
setShowAnalysisModal(false);
|
||||
console.log('mode', mode);
|
||||
if (mode === 'consultation') {
|
||||
console.log('state.current.task', state.current.task)
|
||||
const sceneItem = JSON.parse(state.current.task.scene);
|
||||
if (sceneItem.taskJson) {
|
||||
sceneItem.taskJson = JSON.parse(sceneItem.taskJson);
|
||||
setStartConfig({
|
||||
...sceneItem.taskJson,
|
||||
llm_preset_id: 'BAILING',
|
||||
consistency_mode: 'soft',
|
||||
phase_config: { diverge_rounds: 3 },
|
||||
task_context: state.current.task.overview
|
||||
});
|
||||
router('/qa-center/mope-chat')
|
||||
}
|
||||
console.log('sceneItem', sceneItem);
|
||||
} else if (mode === 'task-search') {
|
||||
window.open(`https://ch1.b.uat.4.cn/web/#/task/item-aicoh/step-info-search?id=${state.current.task.id}`)
|
||||
}
|
||||
};
|
||||
|
||||
// 处理卡片点击
|
||||
const taskListSearch = async (taskType: string, pageNum: number = 1, pageSize: number = 10) => {
|
||||
let taskStatus: number[] = []
|
||||
if (taskType === 'unanalyzed') { // 未分析
|
||||
taskStatus = [0, 10];
|
||||
setUnanalyzedLoading(true);
|
||||
} else if (taskType === 'running') { // 分析中
|
||||
taskStatus = [20];
|
||||
setRunningLoading(true);
|
||||
} else if (taskType === 'completed') { // 已完成
|
||||
taskStatus = [25, 30, 40, 60, 70, 80, 90, 100, 110];
|
||||
setCompletedLoading(true);
|
||||
}
|
||||
const param = {
|
||||
content: searchQuery,
|
||||
pageNum: pageNum,
|
||||
pageSize: pageSize,
|
||||
taskStatus: taskStatus,
|
||||
startTime: state.current.startTime,
|
||||
endTime: state.current.endTime,
|
||||
taskDirection: ''
|
||||
}
|
||||
const res = await service.post(`${zncmUrl}/three/cop-three-tasks`, param);
|
||||
console.log('任务列表', res);
|
||||
const result = res.data;
|
||||
result.records.forEach((item: TaskListItem) => {
|
||||
item.column = taskType;
|
||||
if (!item.taskLevel) item.taskLevel = 1;
|
||||
})
|
||||
if (taskType === 'unanalyzed') {
|
||||
setUnanalyzedLoading(false);
|
||||
setUnanalyzedTotal(result.total);
|
||||
setUnanalyzedList(result.records);
|
||||
} else if (taskType === 'running') {
|
||||
setRunningLoading(false);
|
||||
setRunningTotal(result.total);
|
||||
setRunningList(result.records);
|
||||
} else if (taskType === 'completed') {
|
||||
setCompletedLoading(false);
|
||||
setCompletedTotal(result.total);
|
||||
setCompletedList(result.records);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 页码变化时的回调函数
|
||||
* 当用户切换页码时触发,更新页码并重新获取数据
|
||||
* @param value - 新的页码值
|
||||
*/
|
||||
const pageIndexChange = async (type: string, pageIndex: number) => {
|
||||
// 更新 ref 中存储的页码值
|
||||
// 更新页码状态,触发组件重新渲染
|
||||
await taskListSearch(type, pageIndex);
|
||||
}
|
||||
|
||||
const taskDataChange = async (pageNum: number = 1, pageSize: number = 10) => {
|
||||
taskListSearch('unanalyzed', pageNum, pageSize);
|
||||
taskListSearch('running', pageNum, pageSize);
|
||||
taskListSearch('completed', pageNum, pageSize);
|
||||
}
|
||||
|
||||
// 回车处理
|
||||
const handleKeyDown = async (e) => {
|
||||
if ((e.metaKey || e.ctrlKey || e.shiftKey) && e.key === "Enter") {
|
||||
e.preventDefault();
|
||||
} else if (e.keyCode === 13) {
|
||||
e.preventDefault();
|
||||
await taskDataChange();
|
||||
} else if (e.key === "/") {
|
||||
e.preventDefault();
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
taskDataChange();
|
||||
}, []);
|
||||
|
||||
// 渲染看板列
|
||||
|
||||
return (
|
||||
<div className="h-full flex flex-col">
|
||||
{/* 顶部工具栏 */}
|
||||
<div className={`rounded-xl p-4 flex-shrink-0 mb-4 ${cardBg} border ${cardBorder}`}>
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<div>
|
||||
<div className="flex items-center gap-2 mb-1">
|
||||
<span className={`px-2 py-0.5 rounded text-xs ${isDark ? 'bg-blue-500/20 text-blue-400' : 'bg-red-100 text-red-600'}`}>
|
||||
{category}
|
||||
</span>
|
||||
<span className={`text-xs ${textMuted}`}>/</span>
|
||||
<span className={`px-2 py-0.5 rounded text-xs ${isDark ? 'bg-cyan-500/20 text-cyan-400' : 'bg-red-100 text-red-600'}`}>
|
||||
{subCategory}
|
||||
</span>
|
||||
</div>
|
||||
<h1 className={`text-xl font-bold ${textPrimary}`}>任务中心</h1>
|
||||
</div>
|
||||
{/* <button
|
||||
onClick={() => onNavigate('task-create')}
|
||||
className={`flex items-center gap-2 px-4 py-2 text-white rounded-lg text-sm font-medium transition-colors ${isDark
|
||||
? 'bg-blue-600 hover:bg-blue-500'
|
||||
: 'bg-red-600 hover:bg-red-500 shadow-md'
|
||||
}`}
|
||||
>
|
||||
<Plus className="w-4 h-4" />
|
||||
新建任务
|
||||
</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 ${textMuted}`} />
|
||||
<input
|
||||
type="text"
|
||||
placeholder="搜索任务名称"
|
||||
value={searchQuery}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
onKeyDown={(e) => handleKeyDown(e)}
|
||||
className={`w-full pl-10 pr-4 py-2 border rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-red-500/20 transition-colors ${inputBg} ${cardBorder} ${textPrimary} placeholder:${textMuted}`}
|
||||
/>
|
||||
</div>
|
||||
{/* <select
|
||||
value={modeFilter}
|
||||
onChange={(e) => setModeFilter(e.target.value)}
|
||||
className={`px-4 py-2 border rounded-lg text-sm focus:outline-none cursor-pointer ${inputBg} ${cardBorder} ${textSecondary}`}
|
||||
>
|
||||
<option value="all">全部模式</option>
|
||||
<option value="single">单智能体</option>
|
||||
<option value="multi-agent">多智能体协同</option>
|
||||
<option value="consultation">多智能体会商</option>
|
||||
<option value="scene">场景执行</option>
|
||||
</select> */}
|
||||
{/* <button className={`flex items-center gap-2 px-4 py-2 border rounded-lg text-sm transition-colors ${inputBg} ${cardBorder} ${textSecondary}`}>
|
||||
<Clock className="w-4 h-4" />
|
||||
时间范围
|
||||
</button> */}
|
||||
<DateRangePicker value={range} presets={presets} onChange={(val) => setRangeChange(val)} enableTimePicker={true} clearable />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 统计概览栏 */}
|
||||
<div className="grid grid-cols-4 gap-4 mb-4 flex-shrink-0">
|
||||
<button
|
||||
onClick={() => { setModeFilter('all'); }}
|
||||
className={`p-4 rounded-xl border transition-all ${cardBg} ${cardBorder} hover:border-red-400`}
|
||||
>
|
||||
<p className={`text-2xl font-bold ${textPrimary}`}>{unanalyzedTotal + runningTotal + completedTotal}</p>
|
||||
<p className={`text-sm ${textSecondary}`}>全部任务</p>
|
||||
</button>
|
||||
<button
|
||||
className={`p-4 rounded-xl border transition-all ${cardBg} ${cardBorder} hover:border-red-400`}
|
||||
>
|
||||
<p className={`text-2xl font-bold ${textPrimary}`}>{unanalyzedTotal}</p>
|
||||
<p className={`text-sm ${textSecondary}`}>未分析</p>
|
||||
</button>
|
||||
<button
|
||||
className={`p-4 rounded-xl border transition-all ${cardBg} ${cardBorder} hover:border-red-400`}
|
||||
>
|
||||
<p className={`text-2xl font-bold ${isDark ? 'text-blue-400' : 'text-red-600'}`}>{runningTotal}</p>
|
||||
<p className={`text-sm ${textSecondary}`}>分析中</p>
|
||||
</button>
|
||||
<button
|
||||
className={`p-4 rounded-xl border transition-all ${cardBg} ${cardBorder} hover:border-red-400`}
|
||||
>
|
||||
<p className={`text-2xl font-bold ${isDark ? 'text-green-400' : 'text-green-600'}`}>{completedTotal}</p>
|
||||
<p className={`text-sm ${textSecondary}`}>已完成</p>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* 看板区 */}
|
||||
<div className="flex-1 grid grid-cols-3 gap-4 min-h-0 overflow-hidden">
|
||||
<Loading loading={unanalyzedLoading}>
|
||||
<KanbanColumn title="未分析" taskType={"unanalyzed"} TaskDataType={'sentiment'} count={unanalyzedTotal} color={isDark ? 'bg-yellow-400' : 'bg-yellow-400'} tasks={unanalyzedList} pageIndexSearch={pageIndexChange} handleStartAnalysis={handleStartAnalysis} />
|
||||
</Loading>
|
||||
<Loading loading={runningLoading}>
|
||||
<KanbanColumn title="分析中" taskType="running" TaskDataType={'sentiment'} count={runningTotal} color={isDark ? 'bg-blue-400' : 'bg-red-400'} tasks={runningList} pageIndexSearch={pageIndexChange} handleStartAnalysis={handleStartAnalysis} />
|
||||
</Loading>
|
||||
<Loading loading={completedLoading}>
|
||||
<KanbanColumn title="已完成" taskType="completed" TaskDataType={'sentiment'} count={completedTotal} color={isDark ? 'bg-green-400' : 'bg-green-400'} tasks={completedList} pageIndexSearch={pageIndexChange} handleStartAnalysis={handleStartAnalysis} />
|
||||
</Loading>
|
||||
</div>
|
||||
|
||||
{/* 选择分析模式弹窗 */}
|
||||
{showAnalysisModal && selectedTaskForAnalysis && (
|
||||
<div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50" onClick={() => setShowAnalysisModal(false)}>
|
||||
<div
|
||||
className={`w-full max-w-lg rounded-2xl p-6 ${cardBg} border ${cardBorder} shadow-xl`}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<div className="flex items-center justify-between mb-6">
|
||||
<div>
|
||||
<h2 className={`text-xl font-bold ${textPrimary}`}>选择分析模式</h2>
|
||||
<p className={`text-sm ${textMuted} mt-1`}>为任务「{selectedTaskForAnalysis.taskName}」选择分析方式</p>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => setShowAnalysisModal(false)}
|
||||
className={`p-2 rounded-lg hover:bg-slate-700/50 ${textSecondary}`}
|
||||
>
|
||||
<X className="w-5 h-5" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="space-y-3">
|
||||
|
||||
<button
|
||||
onClick={() => handleSelectAnalysisMode('task-search')}
|
||||
className={`w-full p-4 rounded-xl border transition-all duration-200 text-left hover:shadow-md ${isDark
|
||||
? 'bg-slate-700/50 border-blue-800/30 hover:border-green-500/50'
|
||||
: 'bg-white border-red-100 hover:border-red-300 hover:shadow-md'
|
||||
}`}
|
||||
>
|
||||
<div className="flex items-center gap-4">
|
||||
<div className={`w-12 h-12 rounded-xl flex items-center justify-center ${isDark ? 'bg-green-500/20' : 'bg-green-100'
|
||||
}`}>
|
||||
<Layers className={`w-6 h-6 ${isDark ? 'text-green-400' : 'text-green-600'}`} />
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<h3 className={`font-semibold ${textPrimary}`}>查看任务详情</h3>
|
||||
<p className={`text-sm ${textSecondary}`}>进入b1系统查看任务详情</p>
|
||||
</div>
|
||||
<ArrowRight className={`w-5 h-5 ${textMuted}`} />
|
||||
</div>
|
||||
</button>
|
||||
|
||||
|
||||
{/* 多智能体会商 */}
|
||||
<button
|
||||
onClick={() => handleSelectAnalysisMode('consultation')}
|
||||
className={`w-full p-4 rounded-xl border transition-all duration-200 text-left hover:shadow-md ${isDark
|
||||
? 'bg-slate-700/50 border-blue-800/30 hover:border-orange-500/50'
|
||||
: 'bg-white border-red-100 hover:border-red-300 hover:shadow-md'
|
||||
}`}
|
||||
>
|
||||
<div className="flex items-center gap-4">
|
||||
<div className={`w-12 h-12 rounded-xl flex items-center justify-center ${isDark ? 'bg-orange-500/20' : 'bg-red-100'
|
||||
}`}>
|
||||
<Users className={`w-6 h-6 ${isDark ? 'text-orange-400' : 'text-red-500'}`} />
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<h3 className={`font-semibold ${textPrimary}`}>多智能体会商</h3>
|
||||
<p className={`text-sm ${textSecondary}`}>多个AI智能体从不同视角开展讨论并形成共识</p>
|
||||
</div>
|
||||
<ArrowRight className={`w-5 h-5 ${textMuted}`} />
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SentimentAnalyzes;
|
||||
@@ -0,0 +1,648 @@
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import {
|
||||
Plus,
|
||||
Search,
|
||||
Eye,
|
||||
Play,
|
||||
Pause,
|
||||
Clock,
|
||||
User,
|
||||
ChevronRight,
|
||||
Filter,
|
||||
FileText,
|
||||
AlertTriangle,
|
||||
CheckCircle,
|
||||
XCircle,
|
||||
MoreHorizontal,
|
||||
Edit,
|
||||
Download,
|
||||
Bot,
|
||||
Users,
|
||||
Workflow,
|
||||
X,
|
||||
ArrowRight,
|
||||
Target,
|
||||
Layers,
|
||||
} from 'lucide-react';
|
||||
import { useTheme } from '@/contexts/ThemeContext';
|
||||
// 导入请求服务,用于与后端 API 交互
|
||||
import { requestCrudService } from '@/service/request-axios-service';
|
||||
import { KanbanColumn } from './KanbanColumn';
|
||||
import { TaskListItem } from './TaskManagement';
|
||||
import { DateRangePicker, DateRangePickerProps, DateRangeValue, Loading } from 'tdesign-react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import dayjs from 'dayjs';
|
||||
import { useChatContext } from '@/contexts/ChatContext';
|
||||
|
||||
interface TaskListPageProps {
|
||||
title: string;
|
||||
category: string;
|
||||
subCategory: string;
|
||||
}
|
||||
|
||||
type TaskType = 'single' | 'consultation' | 'scene' | 'multi-agent' | 'task-search';
|
||||
type TaskStatus = 'draft' | 'pending' | 'running' | 'paused' | 'blocked' | 'completed';
|
||||
type ColumnType = 'unanalyzed' | 'analyzing' | 'completed';
|
||||
|
||||
interface Task {
|
||||
id: string;
|
||||
name: string;
|
||||
type: TaskType;
|
||||
status: TaskStatus;
|
||||
agent?: string;
|
||||
scene?: string;
|
||||
participants?: string[];
|
||||
currentPhase?: string;
|
||||
currentRound?: number;
|
||||
summary?: string;
|
||||
creator: string;
|
||||
createTime: string;
|
||||
updateTime: string;
|
||||
column: ColumnType;
|
||||
}
|
||||
|
||||
interface stateItem {
|
||||
startTime: string,
|
||||
endTime: string,
|
||||
task: TaskListItem,
|
||||
}
|
||||
|
||||
const service = requestCrudService;
|
||||
const zncmUrl = import.meta.env.VITE_API_BASE_URL || '';
|
||||
|
||||
const TaskAnalyzes: React.FC<TaskListPageProps> = ({ title, category, subCategory}) => {
|
||||
const { theme } = useTheme();
|
||||
const { setStartConfig } = useChatContext();
|
||||
const router = useNavigate();
|
||||
const isDark = theme === 'dark';
|
||||
|
||||
// 主题颜色变量
|
||||
const cardBg = isDark ? 'bg-slate-800/30' : 'bg-white';
|
||||
const cardBorder = isDark ? 'border-blue-800/30' : 'border-red-100';
|
||||
const textPrimary = isDark ? 'text-white' : 'text-gray-900';
|
||||
const textSecondary = isDark ? 'text-slate-400' : 'text-gray-600';
|
||||
const textMuted = isDark ? 'text-slate-500' : 'text-gray-400';
|
||||
const inputBg = isDark ? 'bg-slate-900/50' : 'bg-white';
|
||||
|
||||
// 生成模拟数据
|
||||
const [tasks] = useState<Task[]>([
|
||||
// 未分析列
|
||||
{
|
||||
id: '2026-0320-001',
|
||||
name: '端午节品牌传播方案',
|
||||
type: 'scene',
|
||||
scene: '宣传策略生成',
|
||||
status: 'draft',
|
||||
column: 'unanalyzed',
|
||||
summary: '针对端午节品牌活动制定完整传播策略方案',
|
||||
creator: '李策划',
|
||||
createTime: '2026-03-20 10:00',
|
||||
updateTime: '2026-03-20 10:00',
|
||||
},
|
||||
{
|
||||
id: '2026-0320-002',
|
||||
name: '新品上市用户调研',
|
||||
type: 'single',
|
||||
agent: 'AudienceAgent',
|
||||
status: 'pending',
|
||||
column: 'unanalyzed',
|
||||
summary: '对新品目标用户进行画像分析',
|
||||
creator: '王运营',
|
||||
createTime: '2026-03-20 09:30',
|
||||
updateTime: '2026-03-20 09:30',
|
||||
},
|
||||
// 分析中列
|
||||
{
|
||||
id: '2026-0319-001',
|
||||
name: '五四青年节宣传活动策略',
|
||||
type: 'scene',
|
||||
scene: '宣传策略生成',
|
||||
status: 'running',
|
||||
currentPhase: '行为分析',
|
||||
column: 'analyzing',
|
||||
summary: '正在按场景模板执行目标、受众、行为、叙事等分析...',
|
||||
creator: '张策略',
|
||||
createTime: '2026-03-19 10:30',
|
||||
updateTime: '2026-03-20 09:45',
|
||||
},
|
||||
{
|
||||
id: '2026-0319-002',
|
||||
name: '青年传播方向会商分析',
|
||||
type: 'consultation',
|
||||
participants: ['GoalAgent', 'AudienceAgent', 'NarrativeAgent'],
|
||||
status: 'running',
|
||||
currentRound: 2,
|
||||
column: 'analyzing',
|
||||
summary: '围绕宣传方向、叙事方式和受众切入点开展多视角会商...',
|
||||
creator: '王策划',
|
||||
createTime: '2026-03-19 14:00',
|
||||
updateTime: '2026-03-20 09:00',
|
||||
},
|
||||
{
|
||||
id: '2026-0318-001',
|
||||
name: '年度品牌升级方案推演',
|
||||
type: 'scene',
|
||||
scene: '宣传方案推演',
|
||||
status: 'paused',
|
||||
currentPhase: '叙事分析',
|
||||
column: 'analyzing',
|
||||
summary: '任务已暂停,等待人工介入确认叙事方向',
|
||||
creator: '王策划',
|
||||
createTime: '2026-03-18 14:00',
|
||||
updateTime: '2026-03-20 08:30',
|
||||
},
|
||||
// 已完成列
|
||||
{
|
||||
id: '2026-0318-002',
|
||||
name: '核心受众画像分析',
|
||||
type: 'single',
|
||||
agent: 'AudienceAgent',
|
||||
status: 'completed',
|
||||
column: 'completed',
|
||||
summary: '已完成目标受众识别、关切提炼和阻力分析',
|
||||
creator: '赵运营',
|
||||
createTime: '2026-03-18 09:00',
|
||||
updateTime: '2026-03-18 16:30',
|
||||
},
|
||||
{
|
||||
id: '2026-0319-003',
|
||||
name: '五四青年节宣传策略',
|
||||
type: 'scene',
|
||||
scene: '宣传策略生成',
|
||||
status: 'completed',
|
||||
column: 'completed',
|
||||
summary: '已完成宣传策略方案生成',
|
||||
creator: '张策略',
|
||||
createTime: '2026-03-19 10:00',
|
||||
updateTime: '2026-03-19 18:00',
|
||||
},
|
||||
{
|
||||
id: '2026-0319-004',
|
||||
name: '青年品牌传播路径会商',
|
||||
type: 'consultation',
|
||||
participants: ['GoalAgent', 'NarrativeAgent', 'RiskAgent'],
|
||||
status: 'completed',
|
||||
column: 'completed',
|
||||
summary: '已完成多智能体会商,形成结论与分歧报告',
|
||||
creator: '王策划',
|
||||
createTime: '2026-03-19 14:30',
|
||||
updateTime: '2026-03-19 17:00',
|
||||
},
|
||||
]);
|
||||
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
const [modeFilter, setModeFilter] = useState<string>('all');
|
||||
const [showAnalysisModal, setShowAnalysisModal] = useState(false);
|
||||
const [selectedTaskForAnalysis, setSelectedTaskForAnalysis] = useState<TaskListItem | null>(null);
|
||||
const [unanalyzedTotal, setUnanalyzedTotal] = useState<number>(0); // 未分析
|
||||
const [runningTotal, setRunningTotal] = useState<number>(0); // 分析中
|
||||
const [completedTotal, setCompletedTotal] = useState<number>(0); // 已完成
|
||||
const [unanalyzedList, setUnanalyzedList] = useState<TaskListItem[]>([]); // 未分析
|
||||
const [runningList, setRunningList] = useState<TaskListItem[]>([]); // 分析中
|
||||
const [completedList, setCompletedList] = useState<TaskListItem[]>([]); // 已完成
|
||||
const [unanalyzedLoading, setUnanalyzedLoading] = useState<boolean>(false); // 未分析
|
||||
const [runningLoading, setRunningLoading] = useState<boolean>(false); // 分析中
|
||||
const [completedLoading, setCompletedLoading] = useState<boolean>(false); // 已完成
|
||||
const [presets] = useState<DateRangePickerProps['presets']>({
|
||||
最近7天: [dayjs().subtract(6, 'day').toDate(), dayjs().toDate()],
|
||||
最近3天: [dayjs().subtract(2, 'day').toDate(), dayjs().toDate()],
|
||||
今天: [dayjs().toDate(), dayjs().toDate()],
|
||||
});
|
||||
const [range, setRange] = useState<DateRangeValue>([]);
|
||||
const state = useRef<stateItem>({
|
||||
startTime: '',
|
||||
endTime: '',
|
||||
task: {} as TaskListItem,
|
||||
})
|
||||
|
||||
// 时间日期选择器查询
|
||||
const setRangeChange = async (val) => {
|
||||
setRange(val);
|
||||
console.log('时间日期', val);
|
||||
state.current.startTime = '';
|
||||
state.current.endTime = '';
|
||||
if (val.length > 0) {
|
||||
state.current.startTime = val[0];
|
||||
state.current.endTime = val[1];
|
||||
}
|
||||
|
||||
await taskDataChange();
|
||||
}
|
||||
|
||||
// 获取任务类型配置
|
||||
const getTypeConfig = (type: TaskType) => {
|
||||
if (isDark) {
|
||||
switch (type) {
|
||||
case 'single':
|
||||
return { label: '单智能体', color: 'bg-cyan-500/20 text-cyan-400', icon: Bot };
|
||||
case 'scene':
|
||||
return { label: '场景执行', color: 'bg-purple-500/20 text-purple-400', icon: Workflow };
|
||||
case 'consultation':
|
||||
return { label: '多智能体会商', color: 'bg-orange-500/20 text-orange-400', icon: Users };
|
||||
case 'multi-agent':
|
||||
return { label: '多智能体协同', color: 'bg-green-500/20 text-green-400', icon: Layers };
|
||||
}
|
||||
} else {
|
||||
switch (type) {
|
||||
case 'single':
|
||||
return { label: '单智能体', color: 'bg-blue-100 text-blue-600', icon: Bot };
|
||||
case 'scene':
|
||||
return { label: '场景执行', color: 'bg-purple-100 text-purple-600', icon: Workflow };
|
||||
case 'consultation':
|
||||
return { label: '多智能体会商', color: 'bg-orange-100 text-orange-600', icon: Users };
|
||||
case 'multi-agent':
|
||||
return { label: '多智能体协同', color: 'bg-green-100 text-green-600', icon: Layers };
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 获取状态配置
|
||||
const getStatusConfig = (status: TaskStatus, column: ColumnType) => {
|
||||
if (column === 'unanalyzed') {
|
||||
switch (status) {
|
||||
case 'draft':
|
||||
return { label: '草稿', color: isDark ? 'bg-slate-500/20 text-slate-400' : 'bg-gray-100 text-gray-600' };
|
||||
case 'pending':
|
||||
return { label: '待启动', color: isDark ? 'bg-slate-500/20 text-slate-400' : 'bg-gray-100 text-gray-600' };
|
||||
default:
|
||||
return { label: '待配置', color: isDark ? 'bg-yellow-500/20 text-yellow-400' : 'bg-yellow-100 text-yellow-600' };
|
||||
}
|
||||
} else if (column === 'analyzing') {
|
||||
switch (status) {
|
||||
case 'running':
|
||||
return { label: '运行中', color: isDark ? 'bg-blue-500/20 text-blue-400' : 'bg-red-100 text-red-600' };
|
||||
case 'paused':
|
||||
return { label: '已暂停', color: isDark ? 'bg-yellow-500/20 text-yellow-400' : 'bg-yellow-100 text-yellow-600' };
|
||||
case 'blocked':
|
||||
return { label: '阻塞中', color: isDark ? 'bg-red-500/20 text-red-400' : 'bg-orange-100 text-orange-600' };
|
||||
default:
|
||||
return { label: '待处理', color: isDark ? 'bg-purple-500/20 text-purple-400' : 'bg-purple-100 text-purple-600' };
|
||||
}
|
||||
} else {
|
||||
return { label: '已完成', color: isDark ? 'bg-green-500/20 text-green-400' : 'bg-green-100 text-green-600' };
|
||||
}
|
||||
};
|
||||
|
||||
// 筛选任务
|
||||
const filteredTasks = tasks.filter(task => {
|
||||
const matchesSearch = task.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||
task.id.toLowerCase().includes(searchQuery.toLowerCase());
|
||||
const matchesMode = modeFilter === 'all' || task.type === modeFilter;
|
||||
return matchesSearch && matchesMode;
|
||||
});
|
||||
|
||||
const unanalyzedTasks = filteredTasks.filter(t => t.column === 'unanalyzed');
|
||||
const analyzingTasks = filteredTasks.filter(t => t.column === 'analyzing');
|
||||
const completedTasks = filteredTasks.filter(t => t.column === 'completed');
|
||||
|
||||
// 统计数据
|
||||
const stats = {
|
||||
total: tasks.length,
|
||||
unanalyzed: unanalyzedTasks.length,
|
||||
analyzing: analyzingTasks.length,
|
||||
completed: completedTasks.length,
|
||||
};
|
||||
|
||||
// 处理开始分析按钮点击
|
||||
const handleStartAnalysis = (task: TaskListItem) => {
|
||||
setSelectedTaskForAnalysis(task);
|
||||
state.current.task = task;
|
||||
setShowAnalysisModal(true);
|
||||
};
|
||||
|
||||
// 处理分析模式选择
|
||||
const handleSelectAnalysisMode = (mode: TaskType) => {
|
||||
setShowAnalysisModal(false);
|
||||
console.log('mode', mode);
|
||||
if (mode === 'consultation') {
|
||||
console.log('state.current.task', state.current.task)
|
||||
const sceneItem = JSON.parse(state.current.task.scene);
|
||||
if (sceneItem.taskJson) {
|
||||
sceneItem.taskJson = JSON.parse(sceneItem.taskJson);
|
||||
setStartConfig({
|
||||
...sceneItem.taskJson,
|
||||
llm_preset_id: 'BAILING',
|
||||
consistency_mode: 'soft',
|
||||
phase_config: {diverge_rounds: 3},
|
||||
task_context: state.current.task.overview
|
||||
});
|
||||
router('/qa-center/mope-chat')
|
||||
}
|
||||
console.log('sceneItem', sceneItem);
|
||||
} else if (mode === 'task-search') {
|
||||
window.open(`https://ch1.b.uat.4.cn/web/#/task/item-aicoh/step-info-search?id=${state.current.task.id}`)
|
||||
}
|
||||
};
|
||||
|
||||
const taskListSearch = async (taskType: string, pageNum: number = 1, pageSize: number = 10) => {
|
||||
let taskStatus: number[] = []
|
||||
if (taskType === 'unanalyzed') { // 未分析
|
||||
taskStatus = [0, 10];
|
||||
setUnanalyzedLoading(true);
|
||||
} else if (taskType === 'running') { // 分析中
|
||||
taskStatus = [20];
|
||||
setRunningLoading(true);
|
||||
} else if (taskType === 'completed') { // 已完成
|
||||
taskStatus = [25, 30, 40, 60, 70, 80, 90, 100, 110];
|
||||
setCompletedLoading(true);
|
||||
}
|
||||
const param = {
|
||||
content: searchQuery,
|
||||
pageNum: pageNum,
|
||||
pageSize: pageSize,
|
||||
taskStatus: taskStatus,
|
||||
startTime: state.current.startTime,
|
||||
endTime: state.current.endTime,
|
||||
taskDirection: ''
|
||||
}
|
||||
const res = await service.post(`${zncmUrl}/task/cop-tasks`, param);
|
||||
console.log('任务列表', res);
|
||||
const result = res.data;
|
||||
result.records.forEach((item: TaskListItem) => {
|
||||
item.column = taskType;
|
||||
if (!item.taskLevel) item.taskLevel = 1;
|
||||
})
|
||||
if (taskType === 'unanalyzed') {
|
||||
setUnanalyzedLoading(false);
|
||||
setUnanalyzedTotal(result.total);
|
||||
setUnanalyzedList(result.records);
|
||||
} else if (taskType === 'running') {
|
||||
setRunningLoading(false);
|
||||
setRunningTotal(result.total);
|
||||
setRunningList(result.records);
|
||||
} else if (taskType === 'completed') {
|
||||
setCompletedLoading(false);
|
||||
setCompletedTotal(result.total);
|
||||
setCompletedList(result.records);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 页码变化时的回调函数
|
||||
* 当用户切换页码时触发,更新页码并重新获取数据
|
||||
* @param value - 新的页码值
|
||||
*/
|
||||
const pageIndexChange = async (type: string, pageIndex: number) => {
|
||||
// 更新 ref 中存储的页码值
|
||||
// 更新页码状态,触发组件重新渲染
|
||||
await taskListSearch(type, pageIndex);
|
||||
}
|
||||
|
||||
const taskDataChange = async (pageNum: number = 1, pageSize: number = 10) => {
|
||||
taskListSearch('unanalyzed', pageNum, pageSize);
|
||||
taskListSearch('running', pageNum, pageSize);
|
||||
taskListSearch('completed', pageNum, pageSize);
|
||||
}
|
||||
|
||||
// 回车处理
|
||||
const handleKeyDown = async (e) => {
|
||||
if ((e.metaKey || e.ctrlKey || e.shiftKey) && e.key === "Enter") {
|
||||
e.preventDefault();
|
||||
} else if (e.keyCode === 13) {
|
||||
e.preventDefault();
|
||||
await taskDataChange();
|
||||
} else if (e.key === "/") {
|
||||
e.preventDefault();
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
taskDataChange();
|
||||
}, []);
|
||||
|
||||
// 渲染看板列
|
||||
|
||||
return (
|
||||
<div className="h-full flex flex-col">
|
||||
{/* 顶部工具栏 */}
|
||||
<div className={`rounded-xl p-4 flex-shrink-0 mb-4 ${cardBg} border ${cardBorder}`}>
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<div>
|
||||
<div className="flex items-center gap-2 mb-1">
|
||||
<span className={`px-2 py-0.5 rounded text-xs ${isDark ? 'bg-blue-500/20 text-blue-400' : 'bg-red-100 text-red-600'}`}>
|
||||
{category}
|
||||
</span>
|
||||
<span className={`text-xs ${textMuted}`}>/</span>
|
||||
<span className={`px-2 py-0.5 rounded text-xs ${isDark ? 'bg-cyan-500/20 text-cyan-400' : 'bg-red-100 text-red-600'}`}>
|
||||
{subCategory}
|
||||
</span>
|
||||
</div>
|
||||
<h1 className={`text-xl font-bold ${textPrimary}`}>任务中心</h1>
|
||||
</div>
|
||||
<button
|
||||
className={`flex items-center gap-2 px-4 py-2 text-white rounded-lg text-sm font-medium transition-colors ${isDark
|
||||
? 'bg-blue-600 hover:bg-blue-500'
|
||||
: 'bg-red-600 hover:bg-red-500 shadow-md'
|
||||
}`}
|
||||
>
|
||||
<Plus className="w-4 h-4" />
|
||||
新建任务
|
||||
</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 ${textMuted}`} />
|
||||
<input
|
||||
type="text"
|
||||
placeholder="搜索任务名称"
|
||||
value={searchQuery}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
onKeyDown={(e) => handleKeyDown(e)}
|
||||
className={`w-full pl-10 pr-4 py-2 border rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-red-500/20 transition-colors ${inputBg} ${cardBorder} ${textPrimary} placeholder:${textMuted}`}
|
||||
/>
|
||||
</div>
|
||||
{/* <select
|
||||
value={modeFilter}
|
||||
onChange={(e) => setModeFilter(e.target.value)}
|
||||
className={`px-4 py-2 border rounded-lg text-sm focus:outline-none cursor-pointer ${inputBg} ${cardBorder} ${textSecondary}`}
|
||||
>
|
||||
<option value="all">全部模式</option>
|
||||
<option value="single">单智能体</option>
|
||||
<option value="multi-agent">多智能体协同</option>
|
||||
<option value="consultation">多智能体会商</option>
|
||||
<option value="scene">场景执行</option>
|
||||
</select> */}
|
||||
{/* <button className={`flex items-center gap-2 px-4 py-2 border rounded-lg text-sm transition-colors ${inputBg} ${cardBorder} ${textSecondary}`}>
|
||||
<Clock className="w-4 h-4" />
|
||||
时间范围
|
||||
</button> */}
|
||||
<DateRangePicker value={range} presets={presets} onChange={(val) => setRangeChange(val)} enableTimePicker={true} clearable />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 统计概览栏 */}
|
||||
<div className="grid grid-cols-4 gap-4 mb-4 flex-shrink-0">
|
||||
<button
|
||||
onClick={() => { setModeFilter('all'); }}
|
||||
className={`p-4 rounded-xl border transition-all ${cardBg} ${cardBorder} hover:border-red-400`}
|
||||
>
|
||||
<p className={`text-2xl font-bold ${textPrimary}`}>{unanalyzedTotal + runningTotal + completedTotal}</p>
|
||||
<p className={`text-sm ${textSecondary}`}>全部任务</p>
|
||||
</button>
|
||||
<button
|
||||
className={`p-4 rounded-xl border transition-all ${cardBg} ${cardBorder} hover:border-red-400`}
|
||||
>
|
||||
<p className={`text-2xl font-bold ${textPrimary}`}>{unanalyzedTotal}</p>
|
||||
<p className={`text-sm ${textSecondary}`}>未分析</p>
|
||||
</button>
|
||||
<button
|
||||
className={`p-4 rounded-xl border transition-all ${cardBg} ${cardBorder} hover:border-red-400`}
|
||||
>
|
||||
<p className={`text-2xl font-bold ${isDark ? 'text-blue-400' : 'text-red-600'}`}>{runningTotal}</p>
|
||||
<p className={`text-sm ${textSecondary}`}>分析中</p>
|
||||
</button>
|
||||
<button
|
||||
className={`p-4 rounded-xl border transition-all ${cardBg} ${cardBorder} hover:border-red-400`}
|
||||
>
|
||||
<p className={`text-2xl font-bold ${isDark ? 'text-green-400' : 'text-green-600'}`}>{completedTotal}</p>
|
||||
<p className={`text-sm ${textSecondary}`}>已完成</p>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* 看板区 */}
|
||||
<div className="flex-1 grid grid-cols-3 gap-4 min-h-0 overflow-hidden">
|
||||
<Loading loading={unanalyzedLoading}>
|
||||
<KanbanColumn title="未分析" taskType={"unanalyzed"} count={unanalyzedTotal} color={isDark ? 'bg-yellow-400' : 'bg-yellow-400'} tasks={unanalyzedList} pageIndexSearch={pageIndexChange} handleStartAnalysis={handleStartAnalysis}/>
|
||||
</Loading>
|
||||
<Loading loading={runningLoading}>
|
||||
<KanbanColumn title="分析中" taskType="running" count={runningTotal} color={isDark ? 'bg-blue-400' : 'bg-red-400'} tasks={runningList} pageIndexSearch={pageIndexChange} handleStartAnalysis={handleStartAnalysis}/>
|
||||
</Loading>
|
||||
<Loading loading={completedLoading}>
|
||||
<KanbanColumn title="已完成" taskType="completed" count={completedTotal} color={isDark ? 'bg-green-400' : 'bg-green-400'} tasks={completedList} pageIndexSearch={pageIndexChange} handleStartAnalysis={handleStartAnalysis}/>
|
||||
</Loading>
|
||||
</div>
|
||||
|
||||
{/* 选择分析模式弹窗 */}
|
||||
{showAnalysisModal && selectedTaskForAnalysis && (
|
||||
<div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50" onClick={() => setShowAnalysisModal(false)}>
|
||||
<div
|
||||
className={`w-full max-w-lg rounded-2xl p-6 ${cardBg} border ${cardBorder} shadow-xl`}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<div className="flex items-center justify-between mb-6">
|
||||
<div>
|
||||
<h2 className={`text-xl font-bold ${textPrimary}`}>选择分析模式</h2>
|
||||
<p className={`text-sm ${textMuted} mt-1`}>为任务「{selectedTaskForAnalysis.taskName}」选择分析方式</p>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => setShowAnalysisModal(false)}
|
||||
className={`p-2 rounded-lg hover:bg-slate-700/50 ${textSecondary}`}
|
||||
>
|
||||
<X className="w-5 h-5" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="space-y-3">
|
||||
{/* 单智能体分析 */}
|
||||
{/* <button
|
||||
onClick={() => handleSelectAnalysisMode('single')}
|
||||
className={`w-full p-4 rounded-xl border transition-all duration-200 text-left hover:shadow-md ${isDark
|
||||
? 'bg-slate-700/50 border-blue-800/30 hover:border-cyan-500/50'
|
||||
: 'bg-white border-red-100 hover:border-red-300 hover:shadow-md'
|
||||
}`}
|
||||
>
|
||||
<div className="flex items-center gap-4">
|
||||
<div className={`w-12 h-12 rounded-xl flex items-center justify-center ${isDark ? 'bg-cyan-500/20' : 'bg-red-100'
|
||||
}`}>
|
||||
<Bot className={`w-6 h-6 ${isDark ? 'text-cyan-400' : 'text-red-500'}`} />
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<h3 className={`font-semibold ${textPrimary}`}>单智能体分析</h3>
|
||||
<p className={`text-sm ${textSecondary}`}>选择一个AI智能体完成单一维度的专项分析</p>
|
||||
</div>
|
||||
<ArrowRight className={`w-5 h-5 ${textMuted}`} />
|
||||
</div>
|
||||
</button> */}
|
||||
|
||||
{/* 多智能体协同分析 */}
|
||||
{/* <button
|
||||
onClick={() => handleSelectAnalysisMode('multi-agent')}
|
||||
className={`w-full p-4 rounded-xl border transition-all duration-200 text-left hover:shadow-md ${isDark
|
||||
? 'bg-slate-700/50 border-blue-800/30 hover:border-green-500/50'
|
||||
: 'bg-white border-red-100 hover:border-red-300 hover:shadow-md'
|
||||
}`}
|
||||
>
|
||||
<div className="flex items-center gap-4">
|
||||
<div className={`w-12 h-12 rounded-xl flex items-center justify-center ${isDark ? 'bg-green-500/20' : 'bg-green-100'
|
||||
}`}>
|
||||
<Layers className={`w-6 h-6 ${isDark ? 'text-green-400' : 'text-green-600'}`} />
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<h3 className={`font-semibold ${textPrimary}`}>多智能体协同分析</h3>
|
||||
<p className={`text-sm ${textSecondary}`}>多个AI智能体并行分析,输出各自的判断和结论</p>
|
||||
</div>
|
||||
<ArrowRight className={`w-5 h-5 ${textMuted}`} />
|
||||
</div>
|
||||
</button> */}
|
||||
<button
|
||||
onClick={() => handleSelectAnalysisMode('task-search')}
|
||||
className={`w-full p-4 rounded-xl border transition-all duration-200 text-left hover:shadow-md ${isDark
|
||||
? 'bg-slate-700/50 border-blue-800/30 hover:border-green-500/50'
|
||||
: 'bg-white border-red-100 hover:border-red-300 hover:shadow-md'
|
||||
}`}
|
||||
>
|
||||
<div className="flex items-center gap-4">
|
||||
<div className={`w-12 h-12 rounded-xl flex items-center justify-center ${isDark ? 'bg-green-500/20' : 'bg-green-100'
|
||||
}`}>
|
||||
<Layers className={`w-6 h-6 ${isDark ? 'text-green-400' : 'text-green-600'}`} />
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<h3 className={`font-semibold ${textPrimary}`}>查看任务详情</h3>
|
||||
<p className={`text-sm ${textSecondary}`}>进入b1系统查看任务详情</p>
|
||||
</div>
|
||||
<ArrowRight className={`w-5 h-5 ${textMuted}`} />
|
||||
</div>
|
||||
</button>
|
||||
|
||||
{/* 多智能体会商 */}
|
||||
<button
|
||||
onClick={() => handleSelectAnalysisMode('consultation')}
|
||||
className={`w-full p-4 rounded-xl border transition-all duration-200 text-left hover:shadow-md ${isDark
|
||||
? 'bg-slate-700/50 border-blue-800/30 hover:border-orange-500/50'
|
||||
: 'bg-white border-red-100 hover:border-red-300 hover:shadow-md'
|
||||
}`}
|
||||
>
|
||||
<div className="flex items-center gap-4">
|
||||
<div className={`w-12 h-12 rounded-xl flex items-center justify-center ${isDark ? 'bg-orange-500/20' : 'bg-red-100'
|
||||
}`}>
|
||||
<Users className={`w-6 h-6 ${isDark ? 'text-orange-400' : 'text-red-500'}`} />
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<h3 className={`font-semibold ${textPrimary}`}>多智能体会商</h3>
|
||||
<p className={`text-sm ${textSecondary}`}>多个AI智能体从不同视角开展讨论并形成共识</p>
|
||||
</div>
|
||||
<ArrowRight className={`w-5 h-5 ${textMuted}`} />
|
||||
</div>
|
||||
</button>
|
||||
|
||||
{/* 场景执行 */}
|
||||
{/* <button
|
||||
onClick={() => handleSelectAnalysisMode('scene')}
|
||||
className={`w-full p-4 rounded-xl border transition-all duration-200 text-left hover:shadow-md ${isDark
|
||||
? 'bg-slate-700/50 border-blue-800/30 hover:border-purple-500/50'
|
||||
: 'bg-white border-red-100 hover:border-red-300 hover:shadow-md'
|
||||
}`}
|
||||
>
|
||||
<div className="flex items-center gap-4">
|
||||
<div className={`w-12 h-12 rounded-xl flex items-center justify-center ${isDark ? 'bg-purple-500/20' : 'bg-red-100'
|
||||
}`}>
|
||||
<Workflow className={`w-6 h-6 ${isDark ? 'text-purple-400' : 'text-red-500'}`} />
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<h3 className={`font-semibold ${textPrimary}`}>场景执行</h3>
|
||||
<p className={`text-sm ${textSecondary}`}>按照预设场景模板自动执行多阶段策略分析</p>
|
||||
</div>
|
||||
<ArrowRight className={`w-5 h-5 ${textMuted}`} />
|
||||
</div>
|
||||
</button> */}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default TaskAnalyzes;
|
||||
@@ -0,0 +1,303 @@
|
||||
import { useTheme } from '@/contexts/ThemeContext';
|
||||
import { TaskListItem } from './TaskManagement';
|
||||
import { Bot, ChevronRight, Download, Edit, Eye, FileText, Layers, MoreHorizontal, Play, User, Users, Workflow } from 'lucide-react';
|
||||
import React from 'react';
|
||||
import { format } from 'date-fns';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { useChatContext } from '@/contexts/ChatContext';
|
||||
|
||||
interface TaskCardProps {
|
||||
TaskType?: string;
|
||||
task: TaskListItem;
|
||||
handleStartAnalysis: (task: TaskListItem) => void;
|
||||
}
|
||||
|
||||
export const TaskCard: React.FC<TaskCardProps> = ({ task, TaskType = 'task', handleStartAnalysis }) => {
|
||||
const { theme } = useTheme();
|
||||
const { setStartConfig } = useChatContext();
|
||||
const isDark = theme === 'dark';
|
||||
const textPrimary = isDark ? 'text-white' : 'text-gray-900';
|
||||
const textMuted = isDark ? 'text-slate-500' : 'text-gray-400';
|
||||
const textSecondary = isDark ? 'text-slate-400' : 'text-gray-600';
|
||||
const navigate = useNavigate();
|
||||
|
||||
const handleStartChange = (task: TaskListItem) => {
|
||||
// if (TaskType === 'task') {
|
||||
// handleStartAnalysis(task);
|
||||
// } else {
|
||||
// sceneDataChange(task);
|
||||
// }
|
||||
handleStartAnalysis(task);
|
||||
}
|
||||
|
||||
const sceneDataChange = (task: TaskListItem) => {
|
||||
if (!task.scene) return;
|
||||
const sceneItem = JSON.parse(task.scene);
|
||||
if (sceneItem.taskJson) {
|
||||
sceneItem.taskJson = JSON.parse(sceneItem.taskJson);
|
||||
setStartConfig({
|
||||
...sceneItem.taskJson,
|
||||
llm_preset_id: 'BAILING',
|
||||
consistency_mode: 'soft',
|
||||
phase_config: { diverge_rounds: 3 },
|
||||
task_context: task.overview
|
||||
});
|
||||
navigate('/qa-center/mope-chat')
|
||||
}
|
||||
console.log('sceneItem', sceneItem);
|
||||
}
|
||||
// 处理卡片点击
|
||||
const handleTaskClick = (task: TaskListItem) => {
|
||||
if (TaskType === 'sentiment') {
|
||||
console.log('点击了', task)
|
||||
sceneDataChange(task);
|
||||
// window.open(`http://ch1.b.uat.4.cn/taskcop/#/situation-overview/action/sentiment?taskId=${task.id}`)
|
||||
} else {
|
||||
if (task.column === 'completed') {
|
||||
// if (task.type === 'consultation') {
|
||||
// onNavigate('task-consult-result');
|
||||
// } else {
|
||||
// onNavigate('strategy-result');
|
||||
// }
|
||||
navigate('/strategy-result')
|
||||
} else if (task.column === 'running') {
|
||||
// navigate('/task-management/task-consult')
|
||||
sceneDataChange(task);
|
||||
// window.open(`http://ch1.b.uat.4.cn/mopeweb/#/mope?taskOverview=${task.overview}`)
|
||||
// if (task.type === 'scene') {
|
||||
// onNavigate('task-scene');
|
||||
// } else if (task.type === 'consultation') {
|
||||
// onNavigate('task-consult');
|
||||
// } else {
|
||||
// onNavigate('task-single');
|
||||
// }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 获取任务类型配置
|
||||
const getTypeConfig = (type: string) => {
|
||||
if (isDark) {
|
||||
switch (type) {
|
||||
case 'single':
|
||||
return { label: '单智能体', color: 'bg-cyan-500/20 text-cyan-400', icon: Bot };
|
||||
case 'scene':
|
||||
return { label: '场景执行', color: 'bg-purple-500/20 text-purple-400', icon: Workflow };
|
||||
case 'consultation':
|
||||
return { label: '多智能体会商', color: 'bg-orange-500/20 text-orange-400', icon: Users };
|
||||
case 'multi-agent':
|
||||
return { label: '多智能体协同', color: 'bg-green-500/20 text-green-400', icon: Layers };
|
||||
}
|
||||
} else {
|
||||
switch (type) {
|
||||
case 'single':
|
||||
return { label: '单智能体', color: 'bg-blue-100 text-blue-600', icon: Bot };
|
||||
case 'scene':
|
||||
return { label: '场景执行', color: 'bg-purple-100 text-purple-600', icon: Workflow };
|
||||
case 'consultation':
|
||||
return { label: '多智能体会商', color: 'bg-orange-100 text-orange-600', icon: Users };
|
||||
case 'multi-agent':
|
||||
return { label: '多智能体协同', color: 'bg-green-100 text-green-600', icon: Layers };
|
||||
}
|
||||
}
|
||||
};
|
||||
const typeConfig = getTypeConfig('single');
|
||||
// 获取任务等级
|
||||
const getLevel = (type: number) => {
|
||||
const fallback = { label: '一般', color: isDark ? 'bg-cyan-500/20 text-cyan-400' : 'bg-blue-100 text-blue-600', icon: Bot };
|
||||
if (isDark) {
|
||||
switch (type) {
|
||||
case 1:
|
||||
return { label: '一般', color: 'bg-cyan-500/20 text-cyan-400', icon: Bot };
|
||||
case 2:
|
||||
return { label: '紧急', color: 'bg-purple-500/20 text-purple-400', icon: Workflow };
|
||||
case 3:
|
||||
return { label: '加急', color: 'bg-orange-500/20 text-orange-400', icon: Users };
|
||||
default:
|
||||
return fallback;
|
||||
}
|
||||
} else {
|
||||
switch (type) {
|
||||
case 1:
|
||||
return { label: '一般', color: 'bg-blue-100 text-blue-600', icon: Bot };
|
||||
case 2:
|
||||
return { label: '紧急', color: 'bg-purple-100 text-purple-600', icon: Workflow };
|
||||
case 3:
|
||||
return { label: '加急', color: 'bg-orange-100 text-orange-600', icon: Users };
|
||||
default:
|
||||
return fallback;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const levelConfig = getLevel(task.taskLevel);
|
||||
const LevelIcon = levelConfig.icon;
|
||||
// 获取状态配置
|
||||
const getStatusConfig = (column: string) => {
|
||||
if (column === 'unanalyzed') {
|
||||
return { label: '待分析', color: isDark ? 'bg-yellow-500/20 text-yellow-400' : 'bg-yellow-100 text-yellow-600' };
|
||||
} else if (column === 'running') {
|
||||
return { label: '运行中', color: isDark ? 'bg-blue-500/20 text-blue-400' : 'bg-red-100 text-red-600' };
|
||||
} else {
|
||||
return { label: '已完成', color: isDark ? 'bg-green-500/20 text-green-400' : 'bg-green-100 text-green-600' };
|
||||
}
|
||||
};
|
||||
const statusConfig = getStatusConfig(task.column);
|
||||
return (
|
||||
<div
|
||||
className={`rounded-xl p-4 hover:shadow-md transition-all duration-200 cursor-pointer group theme-card border ${isDark
|
||||
? 'bg-slate-800/40 backdrop-blur-xl border-blue-800/30 hover:border-blue-500/50'
|
||||
: 'bg-white border-red-100 hover:shadow-md shadow-red-100'
|
||||
}`}
|
||||
onClick={() => handleTaskClick(task)}
|
||||
>
|
||||
{/* 卡片头部:模式标签 + 状态标签 */}
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<span className={`inline-flex items-center gap-1.5 px-2 py-0.5 rounded text-xs font-medium ${levelConfig.color}`}>
|
||||
{LevelIcon && <LevelIcon className="w-3 h-3" />}
|
||||
{levelConfig.label}
|
||||
</span>
|
||||
<span></span>
|
||||
<span className={`inline-flex items-center px-2 py-0.5 rounded text-xs font-medium ${statusConfig.color}`}>
|
||||
{statusConfig.label}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* 任务名称 */}
|
||||
<h3 className={`font-semibold text-sm mb-1 line-clamp-2 group-hover:text-red-500 transition-colors ${textPrimary}`}>
|
||||
{task.taskName}
|
||||
</h3>
|
||||
|
||||
{/* 任务编号 */}
|
||||
<p className={`text-xs mb-2 ${textMuted}`}>TASK-{task.id}</p>
|
||||
|
||||
{/* 任务摘要 */}
|
||||
<p className={`text-xs mb-3 line-clamp-2 ${textSecondary}`}>{task.overview}</p>
|
||||
|
||||
{/* 补充信息区 */}
|
||||
{/* <div className="space-y-1 mb-3">
|
||||
{task.type === 'single' && task.agent && (
|
||||
<div className="flex items-center gap-2 text-xs">
|
||||
<Bot className={`w-3 h-3 ${isDark ? 'text-cyan-400' : 'text-red-500'}`} />
|
||||
<span className={textSecondary}>执行智能体:</span>
|
||||
<span className={isDark ? 'text-cyan-400' : 'text-red-600'}>{task.agent}</span>
|
||||
</div>
|
||||
)}
|
||||
{task.type === 'scene' && task.scene && (
|
||||
<div className="flex items-center gap-2 text-xs">
|
||||
<Workflow className={`w-3 h-3 ${isDark ? 'text-purple-400' : 'text-red-500'}`} />
|
||||
<span className={textSecondary}>关联场景:</span>
|
||||
<span className={isDark ? 'text-purple-400' : 'text-red-600'}>{task.scene}</span>
|
||||
</div>
|
||||
)}
|
||||
{task.type === 'consultation' && (
|
||||
<>
|
||||
<div className="flex items-center gap-2 text-xs">
|
||||
<Users className={`w-3 h-3 ${isDark ? 'text-orange-400' : 'text-red-500'}`} />
|
||||
<span className={textSecondary}>参与智能体:</span>
|
||||
<span className={isDark ? 'text-orange-400' : 'text-red-600'}>{task.participants?.length || 0}个</span>
|
||||
</div>
|
||||
{task.currentRound && (
|
||||
<div className="flex items-center gap-2 text-xs">
|
||||
<Clock className={`w-3 h-3 ${textMuted}`} />
|
||||
<span className={textSecondary}>当前轮次:</span>
|
||||
<span className={textPrimary}>第{task.currentRound}轮</span>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{task.column === 'analyzing' && task.currentPhase && (
|
||||
<div className="flex items-center gap-2 text-xs">
|
||||
<Target className={`w-3 h-3 ${textMuted}`} />
|
||||
<span className={textSecondary}>当前阶段:</span>
|
||||
<span className={textPrimary}>{task.currentPhase}</span>
|
||||
</div>
|
||||
)}
|
||||
</div> */}
|
||||
|
||||
{/* 时间信息 */}
|
||||
<div className={`flex items-center justify-between text-xs pt-3 border-t ${isDark ? 'border-blue-800/30 text-slate-500' : 'border-red-100 text-gray-400'
|
||||
}`}>
|
||||
<div className="flex items-center gap-1.5">
|
||||
<User className="w-3 h-3" />
|
||||
<span>{task.taskDirection}</span>
|
||||
</div>
|
||||
<span>{format(new Date(task.updateTime), 'yyyy-MM-dd HH:mm:ss')}</span>
|
||||
</div>
|
||||
|
||||
{/* 操作按钮区 */}
|
||||
<div className={`flex items-center gap-2 mt-3 pt-3 border-t ${isDark ? 'border-blue-800/30' : 'border-red-100'
|
||||
}`}>
|
||||
{task.column === 'unanalyzed' && (
|
||||
<>
|
||||
<button
|
||||
onClick={(e) => { e.stopPropagation(); handleStartChange(task); }}
|
||||
className={`flex-1 flex items-center justify-center gap-1.5 px-3 py-1.5 text-white rounded-lg text-xs font-medium transition-colors ${isDark
|
||||
? 'bg-blue-600 hover:bg-blue-500'
|
||||
: 'bg-red-600 hover:bg-red-500 shadow-md'
|
||||
}`}
|
||||
>
|
||||
<Play className="w-3 h-3" />
|
||||
开始分析
|
||||
</button>
|
||||
{/* <button
|
||||
onClick={(e) => { e.stopPropagation(); onNavigate('task-create'); }}
|
||||
className={`p-1.5 rounded-lg transition-colors ${isDark ? 'hover:bg-slate-700/50' : 'hover:bg-red-50'
|
||||
}`}
|
||||
title="编辑"
|
||||
>
|
||||
<Edit className={`w-4 h-4 ${isDark ? 'text-slate-400' : 'text-red-400'}`} />
|
||||
</button> */}
|
||||
</>
|
||||
)}
|
||||
{task.column === 'running' && (
|
||||
<>
|
||||
<button
|
||||
onClick={(e) => { e.stopPropagation(); handleStartChange(task); }}
|
||||
className={`flex-1 flex items-center justify-center gap-1.5 px-3 py-1.5 text-white rounded-lg text-xs font-medium transition-colors ${isDark
|
||||
? 'bg-blue-600 hover:bg-blue-500'
|
||||
: 'bg-red-600 hover:bg-red-500 shadow-md'
|
||||
}`}
|
||||
>
|
||||
<Play className="w-3 h-3" />
|
||||
开始分析
|
||||
</button>
|
||||
{/* <button
|
||||
onClick={(e) => { e.stopPropagation(); handleStartChange(task); }}
|
||||
className={`px-2 py-1.5 rounded-lg text-xs transition-colors ${isDark
|
||||
? 'hover:bg-slate-700/50 border border-blue-800/30 text-slate-300'
|
||||
: 'hover:bg-red-50 border border-red-200 text-red-600'
|
||||
}`}
|
||||
>
|
||||
查看进展
|
||||
</button> */}
|
||||
</>
|
||||
)}
|
||||
{task.column === 'completed' && (
|
||||
<>
|
||||
<button
|
||||
onClick={(e) => { e.stopPropagation(); handleStartChange(task); }}
|
||||
className={`flex-1 flex items-center justify-center gap-1.5 px-3 py-1.5 text-white rounded-lg text-xs font-medium transition-colors ${isDark
|
||||
? 'bg-green-600 hover:bg-green-500'
|
||||
: 'bg-green-600 hover:bg-green-500 shadow-md'
|
||||
}`}
|
||||
>
|
||||
<Eye className="w-3 h-3" />
|
||||
开始分析
|
||||
</button>
|
||||
{/* <button className={`px-2 py-1.5 rounded-lg text-xs transition-colors ${isDark
|
||||
? 'hover:bg-slate-700/50 border border-blue-800/30 text-slate-300'
|
||||
: 'hover:bg-red-50 border border-red-200 text-red-600'
|
||||
}`}>
|
||||
<Download className="w-4 h-4" />
|
||||
</button> */}
|
||||
</>
|
||||
)}
|
||||
{/* <button className={`p-1.5 rounded-lg transition-colors ${isDark ? 'hover:bg-slate-700/50' : 'hover:bg-red-50'
|
||||
}`} title="更多">
|
||||
<MoreHorizontal className={`w-4 h-4 ${isDark ? 'text-slate-400' : 'text-red-400'}`} />
|
||||
</button> */}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Outlet } from 'react-router-dom';
|
||||
import './css/strategy-data.css';
|
||||
export interface SceneInfo {
|
||||
applicableTask: string;
|
||||
createTime: string;
|
||||
@@ -159,7 +160,7 @@ export const taskStatusList = {
|
||||
};
|
||||
export default function TaskManagement() {
|
||||
return (
|
||||
<div className="h-full flex flex-col">
|
||||
<div className="strategy-data h-full flex flex-col">
|
||||
<Outlet />
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
import { Outlet, useNavigate } from 'react-router-dom';
|
||||
import React, { useState, useEffect, useRef } from 'react';
|
||||
import { useSearchParams } from 'react-router-dom';
|
||||
|
||||
const userInfo = localStorage.getItem('userInfo');
|
||||
const pgLinkUrl = import.meta.env.VITE_AIPI_PGLINKTO_URL;
|
||||
interface TaskPgFromProps {
|
||||
gopath: string
|
||||
}
|
||||
const TaskPgFrom: React.FC<TaskPgFromProps> = ({gopath}) => {
|
||||
const [pgUrl, setPgUrl] = useState(pgLinkUrl);
|
||||
useEffect(() => {
|
||||
if(userInfo) {
|
||||
const userData = JSON.parse(userInfo);
|
||||
setPgUrl(`${pgUrl}?access_token=${userData.accessToken}&goPath=${gopath}`);
|
||||
}
|
||||
}, [gopath]);
|
||||
return (
|
||||
<div className="h-full flex flex-col">
|
||||
<iframe className="h-[calc(100vh-92px)]" src={pgUrl}></iframe>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
export default TaskPgFrom
|
||||
@@ -0,0 +1,33 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import "./css/iframe.css";
|
||||
|
||||
import { useTheme } from "@/contexts/ThemeContext";
|
||||
|
||||
interface XXTyPageProps {
|
||||
title: string;
|
||||
category: string;
|
||||
subCategory: string;
|
||||
}
|
||||
|
||||
const TYURL = import.meta.env.VITE_AIPI_B2ACTOR_URL || "";
|
||||
|
||||
const XXTyPage: React.FC<XXTyPageProps> = () => {
|
||||
const { theme } = useTheme();
|
||||
const isDark = theme === "dark";
|
||||
const [iframeUrl, setIframeUrl] = useState("");
|
||||
useEffect(() => {
|
||||
initIframeUrl();
|
||||
}, []);
|
||||
const initIframeUrl = () => {
|
||||
const url = `${TYURL}/fromB/toPage?defaultPage=iframeSysystem`;
|
||||
console.log({ iframeUrl: url });
|
||||
setIframeUrl(url);
|
||||
};
|
||||
return (
|
||||
<div className="iframe-container">
|
||||
<iframe src={iframeUrl} className="full-size-iframe"></iframe>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default XXTyPage;
|
||||
@@ -0,0 +1,33 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import "./css/iframe.css";
|
||||
|
||||
import { useTheme } from "@/contexts/ThemeContext";
|
||||
|
||||
interface XingdongTyPageProps {
|
||||
title: string;
|
||||
category: string;
|
||||
subCategory: string;
|
||||
}
|
||||
|
||||
const TYURL = import.meta.env.VITE_AIPI_B2ACTOR_URL || "";
|
||||
|
||||
const XingdongTyPage: React.FC<XingdongTyPageProps> = () => {
|
||||
const { theme } = useTheme();
|
||||
const isDark = theme === "dark";
|
||||
const [iframeUrl, setIframeUrl] = useState("");
|
||||
useEffect(() => {
|
||||
initIframeUrl();
|
||||
}, []);
|
||||
const initIframeUrl = () => {
|
||||
const url = `${TYURL}/fromB/toPage?defaultPage=iframeXdty`;
|
||||
console.log({ iframeUrl: url });
|
||||
setIframeUrl(url);
|
||||
};
|
||||
return (
|
||||
<div className="iframe-container">
|
||||
<iframe src={iframeUrl} className="full-size-iframe"></iframe>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default XingdongTyPage;
|
||||
@@ -0,0 +1,10 @@
|
||||
.iframe-container {
|
||||
width: 100%;
|
||||
height: calc(100vh - 100px); /* 可根据需求调整 */
|
||||
}
|
||||
|
||||
.full-size-iframe {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: none;
|
||||
}
|
||||
+184
@@ -0,0 +1,184 @@
|
||||
/**
|
||||
* Strategy task components styles migrated from web-code/index.css
|
||||
* Used by TaskAnalyzes, SentimentAnalyzes, ActionAnalyzes and related children.
|
||||
*/
|
||||
|
||||
/* ── CSS variables (light) ─────────────────────────────────────────────── */
|
||||
.strategy-data {
|
||||
--color-main: #cf1322;
|
||||
--td-brand-color: #ce2d39;
|
||||
--bg-color: #fef6f6;
|
||||
--card-border: #fa9196;
|
||||
--text-color: #222222;
|
||||
|
||||
--card-bg: 0 0% 100%;
|
||||
--card-hover: 0 86% 97%;
|
||||
--border-color: 0 76% 89%;
|
||||
--input-bg: 0 0% 100%;
|
||||
--input-border: 0 76% 89%;
|
||||
}
|
||||
|
||||
/* ── CSS variables (dark) ────────────────────────────────────────────── */
|
||||
.dark .strategy-data {
|
||||
--color-main: #0062d9;
|
||||
--td-brand-color: #ce2d39;
|
||||
--bg-color: #0062d966;
|
||||
--card-border: #fa9196;
|
||||
--text-color: #ffffff;
|
||||
|
||||
--card-bg: 217.2 32.6% 17.5%;
|
||||
--card-hover: 215 20.2% 25%;
|
||||
--border-color: 217.2 32.6% 17.5%;
|
||||
--input-bg: 217.2 32.6% 17.5%;
|
||||
--input-border: 217.2 32.6% 17.5%;
|
||||
}
|
||||
|
||||
/* ── Theme card (TaskCard, KanbanColumn) ─────────────────────────────── */
|
||||
.strategy-data .theme-card {
|
||||
background-color: hsl(var(--card-bg));
|
||||
}
|
||||
|
||||
.strategy-data .theme-card-hover:hover {
|
||||
background-color: hsl(var(--card-hover));
|
||||
}
|
||||
|
||||
.strategy-data .theme-transition {
|
||||
transition:
|
||||
background-color 0.3s ease,
|
||||
color 0.3s ease,
|
||||
border-color 0.3s ease,
|
||||
box-shadow 0.3s ease;
|
||||
}
|
||||
|
||||
/* ── Scrollbar (KanbanColumn) ────────────────────────────────────────── */
|
||||
.strategy-data .scrollbar-thin {
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: #ddd #f1f1f1;
|
||||
}
|
||||
|
||||
.strategy-data .scrollbar-thin::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
}
|
||||
|
||||
.strategy-data .scrollbar-thin::-webkit-scrollbar-thumb {
|
||||
background: #ddd;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.strategy-data .scrollbar-thin::-webkit-scrollbar-thumb:hover {
|
||||
background: #555;
|
||||
}
|
||||
|
||||
.strategy-data .scrollbar-thin::-webkit-scrollbar-track {
|
||||
background: #f1f1f1;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.dark .strategy-data .scrollbar-thin {
|
||||
scrollbar-color: rgba(148, 163, 184, 0.45) transparent;
|
||||
}
|
||||
|
||||
.dark .strategy-data .scrollbar-thin::-webkit-scrollbar-thumb {
|
||||
background: rgba(148, 163, 184, 0.45);
|
||||
}
|
||||
|
||||
.dark .strategy-data .scrollbar-thin::-webkit-scrollbar-thumb:hover {
|
||||
background: rgba(148, 163, 184, 0.65);
|
||||
}
|
||||
|
||||
.dark .strategy-data .scrollbar-thin::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
/* ── ActionAnalyzes task sidebar ─────────────────────────────────────── */
|
||||
.strategy-data .task-item-active:hover {
|
||||
border: 1px solid var(--color-main);
|
||||
}
|
||||
|
||||
.strategy-data .task-item-active:hover .item-active {
|
||||
background: var(--color-main);
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.strategy-data .dark-theme:hover {
|
||||
background: #0062d966;
|
||||
color: #ffffff;
|
||||
border-color: #0062d9 !important;
|
||||
box-shadow: 0 3px 4px #8b8b8b0f;
|
||||
}
|
||||
|
||||
.strategy-data .action-item:hover {
|
||||
border-color: var(--card-border);
|
||||
}
|
||||
|
||||
.strategy-data .dark-theme.task-active,
|
||||
.strategy-data .dark-theme .task-active {
|
||||
border-color: #0062d9 !important;
|
||||
}
|
||||
|
||||
.strategy-data .task-active {
|
||||
background-color: var(--bg-color);
|
||||
border: 1px solid var(--color-main);
|
||||
}
|
||||
|
||||
.strategy-data .task-active .item-active {
|
||||
background: var(--color-main);
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.strategy-data .task-tab-color {
|
||||
color: var(--color-main);
|
||||
}
|
||||
|
||||
.dark .strategy-data .dark-theme .task-tab-color {
|
||||
color: #ffffff !important;
|
||||
}
|
||||
|
||||
.dark .strategy-data .dark-theme .t-progress__bar {
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
/* ── ActionColumn kanban backgrounds ─────────────────────────────────── */
|
||||
.strategy-data .action-bg0 {
|
||||
background: linear-gradient(180deg, #f2f9ff, #fff 17.89% 100%);
|
||||
}
|
||||
|
||||
.strategy-data .action-bg1 {
|
||||
background: linear-gradient(180deg, #fffaed, #fff 17.89% 100%);
|
||||
}
|
||||
|
||||
.strategy-data .action-bg2 {
|
||||
background: linear-gradient(180deg, #f2fff9, #fff 17.89% 100%);
|
||||
}
|
||||
|
||||
.strategy-data .action-bg3 {
|
||||
background: linear-gradient(180deg, #f5f5f5, #fff 17.89% 100%);
|
||||
}
|
||||
|
||||
.strategy-data .dark-action-bg {
|
||||
background: rgba(3, 38, 80, 0.6) !important;
|
||||
border: 1px solid rgba(43, 143, 254, 0.6) !important;
|
||||
border-radius: 8px !important;
|
||||
box-shadow: 0 1px 16px rgba(0, 0, 0, 0.08) !important;
|
||||
}
|
||||
|
||||
.strategy-data .dark-action-item {
|
||||
background: #0062d91a;
|
||||
border: 1px solid #2b8ffe99;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.strategy-data .dark-action-item:hover {
|
||||
background: #0062d966;
|
||||
border-color: #0062d9;
|
||||
}
|
||||
|
||||
/* ── TDesign overrides inside task pages ─────────────────────────────── */
|
||||
.strategy-data .t-loading__parent {
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.strategy-data .t-pagination {
|
||||
justify-content: center;
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
import axios from 'axios'
|
||||
// axios.interceptors
|
||||
const service = axios.create({
|
||||
// baseURL: '', // url = base url + request url
|
||||
// baseURL: '/api',
|
||||
// withCredentials: true, // send cookies when cross-domain requests
|
||||
timeout: 500000000*1000 // request timeout
|
||||
})
|
||||
|
||||
service.interceptors.response.use(
|
||||
response => {
|
||||
const res = response.data
|
||||
// if(!sessionStorage.getItem('userName')){
|
||||
// // window.location = '/#/lagchain-login'
|
||||
// }
|
||||
if (res.code !== 0) {
|
||||
return res
|
||||
} else {
|
||||
return res
|
||||
}
|
||||
},
|
||||
error => {
|
||||
if(!error.response) return Promise.reject(error)
|
||||
if (error.response.status == 401) {
|
||||
// window.location = sessionStorage.getItem('mhUrl');
|
||||
} else {
|
||||
return Promise.reject(error)
|
||||
}
|
||||
|
||||
}
|
||||
)
|
||||
service.interceptors.request.use(
|
||||
config => {
|
||||
// let token: any = localStorage.getItem('token');
|
||||
config.headers['Authorization'] = `Bearer admin`;
|
||||
// if (token) {
|
||||
// token = JSON.parse(token);
|
||||
// // 在请求头中添加 token
|
||||
// config.headers['Authorization'] = `Bearer ${token.token}`;
|
||||
// }
|
||||
return config
|
||||
},
|
||||
error => {
|
||||
return Promise.reject(error)
|
||||
}
|
||||
)
|
||||
|
||||
export default service
|
||||
@@ -0,0 +1,46 @@
|
||||
import axios from 'axios'
|
||||
// axios.interceptors
|
||||
const toolsService = axios.create({
|
||||
// baseURL: '', // url = base url + request url
|
||||
// baseURL: '/api',
|
||||
// withCredentials: true, // send cookies when cross-domain requests
|
||||
timeout: 500000000*1000 // request timeout
|
||||
})
|
||||
|
||||
toolsService.interceptors.response.use(
|
||||
response => {
|
||||
const res = response.data
|
||||
// if(!sessionStorage.getItem('userName')){
|
||||
// // window.location = '/#/lagchain-login'
|
||||
// }
|
||||
if (res.code !== 0) {
|
||||
return res
|
||||
} else {
|
||||
return res
|
||||
}
|
||||
},
|
||||
error => {
|
||||
if(!error.response) return Promise.reject(error)
|
||||
if (error.response.status == 401) {
|
||||
// window.location = sessionStorage.getItem('mhUrl');
|
||||
} else {
|
||||
return Promise.reject(error)
|
||||
}
|
||||
|
||||
}
|
||||
)
|
||||
toolsService.interceptors.request.use(
|
||||
config => {
|
||||
const token: any = localStorage.getItem('toolToken');
|
||||
if (token) {
|
||||
// 在请求头中添加 token
|
||||
config.headers['Authorization'] = `Bearer ${token}`;
|
||||
}
|
||||
return config
|
||||
},
|
||||
error => {
|
||||
return Promise.reject(error)
|
||||
}
|
||||
)
|
||||
|
||||
export default toolsService
|
||||
Reference in new issue
Block a user