Merge branch 'refs/heads/master' into production
commit
ffdecae67d
@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg class="icon" width="16px" height="16px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path fill="#ffffff" d="M340 865.142857q0-57.142857-94.285714-57.142857-90.285714 0-90.285714 59.428571 0 57.714286 98.285714 57.714286 86.285714 0 86.285714-60zm-33.714286-431.428571q0-34.857143-17.142857-58.285714t-50.857143-23.428571q-70.857143 0-70.857143 82.857143 0 77.142857 70.857143 77.142857 68 0 68-78.285714zm153.714286-185.142857l0 115.428571q-20.571429 6.857143-45.142857 12.571429 9.142857 24.571429 9.142857 48 0 72.571429-41.714286 123.714286t-112.571429 64.285714q-22.857143 4.571429-34 15.428571t-11.142857 33.142857q0 17.714286 12.857143 29.428571t33.142857 18.285714 44.857143 12.571429 49.142857 14.571429 44.857143 21.428571 33.142857 36.571429 12.857143 56.285714q0 173.714286-207.428571 173.714286-39.428571 0-74.285714-7.142857t-66.285714-23.428571-50-46.857143-18.571429-72.857143q0-94.285714 104-128.571429l0-2.285714q-38.285714-23.428571-38.285714-72 0-62.285714 36-78.285714l0-2.285714q-41.142857-13.714286-68.285714-62t-27.142857-94.571429q0-79.428571 54.285714-132.285714t134.285714-52.857143q54.857143 0 101.714286 26.857143 56 0 124.571429-26.857143zm181.714286 503.428571l-126.857143 0q2.285714-25.714286 2.285714-76.571429l0-348q0-53.714286-2.285714-73.142857l126.857143 0q-2.285714 18.857143-2.285714 70.857143l0 350.285714q0 50.857143 2.285714 76.571429zm343.428571-126.857143l0 112q-40.571429 22.285714-99.428571 22.285714-35.428571 0-61.142857-11.428571t-40-28.571429-22.571429-44.571429-10.571429-52.571429-2.285714-58.857143l0-200.571429 1.142857 0 0-2.285714q-4 0-10.857143-0.571429t-10.285714-0.571429q-12 0-33.714286 3.428571l0-108.571429 54.857143 0 0-43.428571q0-30.857143-3.428571-50.857143l129.714286 0q-3.428571 23.428571-3.428571 94.285714l97.714286 0 0 108.571429q-8.571429 0-24.857143-1.142857t-24.285714-1.142857l-48.571429 0 0 208.571429q0 74.857143 49.714286 74.857143 34.857143 0 62.285714-18.857143zm-329.142857-541.142857q0 33.142857-22.285714 58t-54.857143 24.857143q-33.142857 0-56-24.857143t-22.857143-58q0-33.714286 22.571429-58.857143t56.285714-25.142857q33.142857 0 55.142857 25.428571t22 58.571429z" /></svg>
|
||||
|
After Width: | Height: | Size: 2.3 KiB |
@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg class="icon" width="18px" height="18.00px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path fill="#ffffff" d="M689.3568 820.9408 333.6192 820.9408c-13.9264 0-25.1904-11.264-25.1904-25.1904L308.4288 465.152l-211.968 0c-10.1888 0-19.4048-6.144-23.296-15.5648C69.2224 440.2176 71.424 429.312 78.6432 422.144l415.0272-415.0784c9.472-9.472 26.1632-9.472 35.6352 0l411.392 411.3408c7.2704 4.4544 12.0832 12.4416 12.0832 21.5552 0 14.1312-11.52 24.576-25.7024 25.1904-0.1536 0-0.3072 0-0.512 0l-211.968 0 0 330.5472C714.5984 809.6256 703.2832 820.9408 689.3568 820.9408zM358.8096 770.5088l305.3568 0L664.1664 439.9616c0-13.9264 11.264-25.1904 25.1904-25.1904l176.3328 0L511.488 60.5184 157.2864 414.7712l176.3328 0c13.9264 0 25.1904 11.264 25.1904 25.1904L358.8096 770.5088zM96.4096 923.1872l830.1056 0L926.5152 1024 96.4096 1024 96.4096 923.1872 96.4096 923.1872zM96.4096 923.1872" /></svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg class="icon" width="18px" height="18.00px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path fill="#ffffff" d="M528 504a16 16 0 0 1 16 16v208h66.112c12.8 0 20.416 14.272 13.312 24.896l-98.112 147.136a16 16 0 0 1-26.624 0l-98.112-147.2a16 16 0 0 1 13.312-24.832H480v-208a16 16 0 0 1 16-16z m-16-384a256.384 256.384 0 0 1 254.08 224.384 240.32 240.32 0 0 1 225.92 240.768c-0.64 132.672-110.912 238.848-243.648 238.848h-28.352a16 16 0 0 1-16-16v-32a16 16 0 0 1 16-16h28.864c98.496 0 180.8-80.64 179.136-179.072a176.192 176.192 0 0 0-176-172.928h-32a16 16 0 0 1-16-16v-12.8c0-105.088-83.2-193.152-188.288-195.2a192.192 192.192 0 0 0-195.712 192v16a16 16 0 0 1-16 16h-32A176.192 176.192 0 0 0 96 580.928c-1.664 98.496 80.64 179.072 179.136 179.072h28.864a16 16 0 0 1 16 16v32a16 16 0 0 1-16 16h-28.352C142.912 824 32.64 717.824 32 585.152a240.32 240.32 0 0 1 225.92-240.768A256.384 256.384 0 0 1 512 120z" /></svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
@ -0,0 +1,44 @@
|
||||
import axios from 'axios';
|
||||
|
||||
// 公共路径
|
||||
const urlPrefix = '/api/v1/bpms-workbench';
|
||||
|
||||
// 我的组件
|
||||
export function getMyComponentList(params) {
|
||||
return axios.get(`${urlPrefix}/componentBase/list`, { params });
|
||||
}
|
||||
|
||||
// 协作组件
|
||||
export function getCooperationComponentList(params) {
|
||||
return axios.get(`${urlPrefix}/componentBase/page/collaborator`, { params });
|
||||
}
|
||||
|
||||
// 获取标签列表
|
||||
export function getTagList() {
|
||||
return axios.get(`${urlPrefix}/componentBase/tags`);
|
||||
}
|
||||
|
||||
// 校验项目标识
|
||||
export function compProjectValidate(projectId) {
|
||||
return axios.post(`${urlPrefix}/componentBase/validate?projectId=${projectId}`);
|
||||
}
|
||||
|
||||
// 组件信息提交/更新
|
||||
export function compSubmit(params) {
|
||||
return axios.post(`${urlPrefix}/componentBase/submit`, params);
|
||||
}
|
||||
|
||||
// 组件删除
|
||||
export const remove = (ids) => {
|
||||
return axios.post(`${urlPrefix}/componentBase/remove?ids=${ids}`);
|
||||
};
|
||||
|
||||
// 组件导出
|
||||
export const exportComponent = (id) => {
|
||||
return axios.get(`${urlPrefix}/componentBase/export?id=${id}`);
|
||||
};
|
||||
|
||||
// 复制代码和设计
|
||||
export const copyAll = (params) => {
|
||||
return axios.post(`${urlPrefix}/componentBase/copy`, params);
|
||||
};
|
||||
@ -0,0 +1,9 @@
|
||||
import axios from 'axios';
|
||||
|
||||
// 公共路径
|
||||
const urlPrefix = '/api/v1/bpms-workbench';
|
||||
|
||||
// 我的组件
|
||||
export function getComponentClassify(classifyType: 'component' | 'language_type') {
|
||||
return axios.get(`${urlPrefix}/componentClassify/list?classifyType=${classifyType}`);
|
||||
}
|
||||
@ -0,0 +1,24 @@
|
||||
import axios from 'axios';
|
||||
|
||||
// 公共路径
|
||||
const urlPrefix = '/api/v1/bpms-workbench';
|
||||
|
||||
// 获取组件设计
|
||||
export function getComponentDesign(componentBaseId) {
|
||||
return axios.get(`${urlPrefix}/componentDevelopProcess/design?componentBaseId=${componentBaseId}`);
|
||||
}
|
||||
|
||||
// 更新组件设计
|
||||
export function updateComponentDesign(params) {
|
||||
return axios.post(`${urlPrefix}/componentDevelopProcess/design`, params);
|
||||
}
|
||||
|
||||
// 获取组件基本信息
|
||||
export function getComponentBaseInfo(componentBaseId) {
|
||||
return axios.get(`${urlPrefix}/componentDevelopProcess/baseInfo?componentBaseId=${componentBaseId}`);
|
||||
}
|
||||
|
||||
// 代码初始化
|
||||
export function codeInit(componentBaseId) {
|
||||
return axios.post(`${urlPrefix}/componentDevelopProcess/generate?componentBaseId=${componentBaseId}`);
|
||||
}
|
||||
@ -0,0 +1,20 @@
|
||||
import axios from 'axios';
|
||||
import { ComponentMarketParams, ReviewGroup } from '@/api/interface';
|
||||
|
||||
// 公共路径
|
||||
const urlPrefix = '/api/v1/bpms-workbench';
|
||||
|
||||
// 组件审核列表
|
||||
export function getReviewGroupByNew(params: ReviewGroup) {
|
||||
return axios.get(`${urlPrefix}/componentMarket/reviewGroupByNew`, { params });
|
||||
}
|
||||
|
||||
// 复制组件设计
|
||||
export function copyDesign(params) {
|
||||
return axios.post(`${urlPrefix}/componentBase/copyDesign`, params);
|
||||
}
|
||||
|
||||
// 组件市场
|
||||
export function getComponentMarket(params: ComponentMarketParams) {
|
||||
return axios.get(`${urlPrefix}/componentMarket/list`, { params });
|
||||
}
|
||||
@ -0,0 +1,21 @@
|
||||
import axios from 'axios';
|
||||
import { AxiosPromise } from 'axios';
|
||||
|
||||
// 公共路径
|
||||
const urlPrefix = '/api/v1/bpms-workbench';
|
||||
|
||||
export interface ComponentReleaseParams {
|
||||
identifier: string;
|
||||
version: string;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
// 组件发布/公开
|
||||
export function componentRelease(params: ComponentReleaseParams): AxiosPromise<any> {
|
||||
return axios.post(`${urlPrefix}/componentRelease/publish`, params);
|
||||
}
|
||||
|
||||
// 组件撤销
|
||||
export function componentRevoke(params: ComponentReleaseParams): AxiosPromise<any> {
|
||||
return axios.post(`${urlPrefix}/componentRelease/revoke`, params);
|
||||
}
|
||||
@ -0,0 +1,9 @@
|
||||
import axios from 'axios';
|
||||
|
||||
// 公共路径
|
||||
const urlPrefix = '/api/v1/bpms-workbench';
|
||||
|
||||
// 我的组件
|
||||
export function fileUpload(params) {
|
||||
return axios.post(`${urlPrefix}/fileSystem/fileUpload`, params);
|
||||
}
|
||||
@ -0,0 +1,69 @@
|
||||
'use client';
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import type { Editor as ToastEditor } from '@toast-ui/react-editor';
|
||||
import '@toast-ui/editor/dist/toastui-editor.css';
|
||||
import { isSSR } from '@/utils/is';
|
||||
|
||||
interface EditorSectionProps {
|
||||
initialContent?: string;
|
||||
}
|
||||
|
||||
// 创建一个 Viewer 组件用于服务端渲染
|
||||
const EditorViewer: React.FC<{ content: string }> = ({ content }) => {
|
||||
const viewerRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isSSR && viewerRef.current) {
|
||||
// 在客户端激活时,动态加载 Viewer 并渲染内容
|
||||
import('@toast-ui/editor/dist/toastui-editor-viewer').then((module) => {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
const { Viewer } = module;
|
||||
new Viewer({
|
||||
el: viewerRef.current!,
|
||||
initialValue: content || ''
|
||||
});
|
||||
}).catch((error) => {
|
||||
console.error('Failed to load Toast UI Viewer:', error);
|
||||
});
|
||||
}
|
||||
}, [content]);
|
||||
|
||||
// 服务端直接渲染 HTML 内容
|
||||
return <div ref={viewerRef} dangerouslySetInnerHTML={{ __html: content || '' }} />;
|
||||
};
|
||||
|
||||
export default function EditorSection({ initialContent }: EditorSectionProps) {
|
||||
const [isClient, setIsClient] = useState(false);
|
||||
const editorRef = useRef<typeof ToastEditor | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isSSR) {
|
||||
setIsClient(true);
|
||||
|
||||
// 动态导入编辑器组件
|
||||
import('@toast-ui/react-editor').then((module) => {
|
||||
editorRef.current = module.Editor;
|
||||
}).catch((error) => {
|
||||
console.error('Failed to load Toast UI Editor:', error);
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
|
||||
// 在服务端或组件未加载完成时,使用 Viewer 模式显示内容
|
||||
if (!isClient || !editorRef.current) {
|
||||
return (
|
||||
<div className="mt-8">
|
||||
<EditorViewer content={initialContent || ''} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const DynamicEditor = editorRef.current;
|
||||
|
||||
return (
|
||||
<div className="mt-8">
|
||||
<DynamicEditor initialValue={initialContent} initialEditType="markdown" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
export const globalOption = {
|
||||
border: false,
|
||||
align: 'left',
|
||||
menuHeaderAlign: 'left',
|
||||
size: 'mini',
|
||||
refreshBtn: false,
|
||||
filterBtn: false,
|
||||
columnBtn: false,
|
||||
searchShowBtn: false,
|
||||
// labelPosition: "left"
|
||||
};
|
||||
export const TrimReg = /^[^\s]+$/;
|
||||
export const patternTrim = { pattern: TrimReg, message: '不能输入空格' };
|
||||
@ -0,0 +1,176 @@
|
||||
import { globalOption, patternTrim } from '@/const/globalOption';
|
||||
import { runTypeDic, runStatusDic, runStatusConstant } from '@/const/isdp/componentDeploy';
|
||||
export const runStatusConstant2 = runStatusConstant;
|
||||
export const componentTestOption = {
|
||||
...globalOption,
|
||||
height: '400',
|
||||
calcHeight: 30,
|
||||
tip: false,
|
||||
searchShow: false,
|
||||
selection: false,
|
||||
dialogClickModal: false,
|
||||
header: false,
|
||||
delBtn: true,
|
||||
viewBtn: true,
|
||||
editBtn: false,
|
||||
dialogWidth: '55%',
|
||||
menuWidth: 230,
|
||||
saveBtn: false,
|
||||
cancelBtn: false,
|
||||
menu: false,
|
||||
emptyBtn: false,
|
||||
submitBtn: false,
|
||||
menuPosition: 'right',
|
||||
column: [
|
||||
{
|
||||
label: '用例名称',
|
||||
prop: 'testCaseName',
|
||||
type: 'select',
|
||||
allowCreate: true,
|
||||
filterable: true,
|
||||
placeholder: '请输入或选择测试用例',
|
||||
rules: [{ required: true, message: '用例名称不能为空' }, { max: 20, message: '用例名称最长为20个字符' }, patternTrim],
|
||||
dicData: [
|
||||
{
|
||||
label: '通过用例',
|
||||
value: '通过用例',
|
||||
},
|
||||
{
|
||||
label: '失败用例',
|
||||
value: '失败用例',
|
||||
},
|
||||
{
|
||||
label: '参数有误',
|
||||
value: '参数有误',
|
||||
},
|
||||
{
|
||||
label: '数据为空',
|
||||
value: '数据为空',
|
||||
},
|
||||
{
|
||||
label: '缺少参数',
|
||||
value: '缺少参数',
|
||||
},
|
||||
{
|
||||
label: '记录不存在',
|
||||
value: '记录不存在',
|
||||
},
|
||||
],
|
||||
span: 12,
|
||||
},
|
||||
{
|
||||
type: 'none',
|
||||
display: 'none',
|
||||
},
|
||||
{
|
||||
label: '输入参数',
|
||||
prop: 'expectDataIns',
|
||||
type: 'dynamic',
|
||||
span: 24,
|
||||
children: {
|
||||
height: 250,
|
||||
addBtn: false,
|
||||
delBtn: false,
|
||||
column: [
|
||||
{
|
||||
label: '名称',
|
||||
prop: 'id',
|
||||
type: 'input',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
label: '参数类型',
|
||||
prop: 'type',
|
||||
type: 'input',
|
||||
width: 80,
|
||||
},
|
||||
{
|
||||
label: '数组类型',
|
||||
placeholder: '请选择数组类型',
|
||||
prop: 'generic',
|
||||
type: 'input',
|
||||
width: 80,
|
||||
},
|
||||
{
|
||||
label: '参数值',
|
||||
prop: 'value',
|
||||
type: 'input',
|
||||
cell: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '输出参数',
|
||||
prop: 'expectDataOuts',
|
||||
type: 'dynamic',
|
||||
span: 24,
|
||||
children: {
|
||||
height: 250,
|
||||
addBtn: false,
|
||||
delBtn: false,
|
||||
column: [
|
||||
{
|
||||
label: '名称',
|
||||
prop: 'id',
|
||||
type: 'input',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
label: '参数类型',
|
||||
prop: 'type',
|
||||
type: 'input',
|
||||
width: 80,
|
||||
},
|
||||
{
|
||||
label: '数组类型',
|
||||
prop: 'generic',
|
||||
type: 'input',
|
||||
width: 80,
|
||||
},
|
||||
{
|
||||
label: '参数值',
|
||||
prop: 'value',
|
||||
type: 'input',
|
||||
cell: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
export const option = {
|
||||
...globalOption,
|
||||
header: false,
|
||||
editBtn: false,
|
||||
delBtn: false,
|
||||
menuWidth: 100,
|
||||
column: [
|
||||
{
|
||||
label: '实例标识',
|
||||
prop: 'identifier',
|
||||
},
|
||||
{
|
||||
label: '实例名',
|
||||
prop: 'name',
|
||||
},
|
||||
{
|
||||
label: '运行类型',
|
||||
prop: 'runType',
|
||||
width: 80,
|
||||
dicData: runTypeDic,
|
||||
},
|
||||
{
|
||||
label: '实例状态',
|
||||
prop: 'runStatus',
|
||||
width: 80,
|
||||
dicData: runStatusDic,
|
||||
},
|
||||
{
|
||||
label: '实例测试时间',
|
||||
width: 130,
|
||||
prop: 'lastTestTime',
|
||||
slot: true,
|
||||
},
|
||||
],
|
||||
};
|
||||
@ -0,0 +1,113 @@
|
||||
import { globalOption } from '@/const/globalOption';
|
||||
/**
|
||||
* @数据映射
|
||||
*/
|
||||
// 数据显示的映射
|
||||
export const typeMap = {
|
||||
INTEGER: 'INT',
|
||||
DOUBLE: 'DOU',
|
||||
STRING: 'STR',
|
||||
LONG: 'LONG',
|
||||
FLOAT: 'FLOAT',
|
||||
DATE: 'DATE',
|
||||
TIMESTAMP: 'TS',
|
||||
DATETIME: 'DT',
|
||||
BOOLEAN: 'BOOL',
|
||||
ARRAY: 'ARR',
|
||||
OBJECT: 'OBJ',
|
||||
};
|
||||
export const dataTypeDict = [
|
||||
{
|
||||
label: '整数',
|
||||
value: 'INTEGER',
|
||||
},
|
||||
{
|
||||
label: '浮点数',
|
||||
value: 'DOUBLE',
|
||||
},
|
||||
{
|
||||
label: '字符串',
|
||||
value: 'STRING',
|
||||
},
|
||||
{
|
||||
label: '布尔值',
|
||||
value: 'BOOLEAN',
|
||||
},
|
||||
{
|
||||
label: '数组',
|
||||
value: 'ARRAY',
|
||||
},
|
||||
{
|
||||
label: '对象',
|
||||
value: 'OBJECT',
|
||||
},
|
||||
{
|
||||
label: '日期',
|
||||
value: 'DATE',
|
||||
},
|
||||
{
|
||||
label: '日期时间',
|
||||
value: 'DATETIME',
|
||||
},
|
||||
{
|
||||
label: '时间戳',
|
||||
value: 'TIMESTAMP',
|
||||
},
|
||||
];
|
||||
|
||||
// 显示查看表单
|
||||
export const showOption = {
|
||||
...globalOption,
|
||||
minHeight: '100',
|
||||
calcHeight: 30,
|
||||
tip: false,
|
||||
searchShow: false,
|
||||
searchMenuSpan: 24,
|
||||
addBtn: false,
|
||||
viewBtn: false,
|
||||
selection: false,
|
||||
editBtn: false,
|
||||
delBtn: false,
|
||||
dialogClickModal: false,
|
||||
header: false,
|
||||
dialogWidth: '50%',
|
||||
menuWidth: 60,
|
||||
// dialogFullscreen: true,
|
||||
column: [
|
||||
{
|
||||
label: '名称',
|
||||
prop: 'ident',
|
||||
span: 12,
|
||||
trigger: 'blur',
|
||||
placeholder: '请输入名称',
|
||||
rules: [
|
||||
{ required: true, message: '名称不能为空' },
|
||||
{ pattern: /^[a-z][a-zA-Z0-9]*$/, message: '名称符需要为首字母小写的驼峰命名法' },
|
||||
{ max: 50, message: '名称最长为50个字符' },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '数据类型',
|
||||
prop: 'type',
|
||||
type: 'select',
|
||||
placeholder: '请选择数据类型',
|
||||
dicData: dataTypeDict,
|
||||
trigger: 'blur',
|
||||
rules: [{ required: true, message: '数据类型不能为空' }],
|
||||
},
|
||||
// 需要加上change
|
||||
{
|
||||
label: '数组类型',
|
||||
prop: 'generic',
|
||||
slot: true,
|
||||
formSlot: true,
|
||||
placeholder: '请选择数组类型',
|
||||
dicData: dataTypeDict.filter((item) => item.value !== 'ARRAY'),
|
||||
rules: [{ required: true, message: '数组类型不能为空' }],
|
||||
},
|
||||
{
|
||||
label: '参数描述',
|
||||
prop: 'desc',
|
||||
},
|
||||
],
|
||||
};
|
||||
@ -0,0 +1,72 @@
|
||||
import { option as baseOption } from '../componentBase';
|
||||
import { deepClone } from '@/utils/common';
|
||||
|
||||
export const permissionConstant = {
|
||||
ADMIN: 'admin',
|
||||
READ: 'read',
|
||||
WRITE: 'write',
|
||||
};
|
||||
|
||||
// 系统模块
|
||||
export const permissionDic = [
|
||||
{
|
||||
label: '管理',
|
||||
value: 'admin',
|
||||
},
|
||||
{
|
||||
label: '读',
|
||||
value: 'read',
|
||||
},
|
||||
{
|
||||
label: '写',
|
||||
value: 'write',
|
||||
},
|
||||
];
|
||||
|
||||
export const option = deepClone(baseOption);
|
||||
option.header = false;
|
||||
option.addBtn = false;
|
||||
option.menuWidth = 200;
|
||||
|
||||
const hideArr = ['name', 'componentClassify', 'publicStatus'];
|
||||
const columnArr = [];
|
||||
option.column.forEach((element) => {
|
||||
if (hideArr.includes(element.prop)) {
|
||||
element.align = 'center';
|
||||
columnArr.push(element);
|
||||
}
|
||||
});
|
||||
|
||||
option.column = [
|
||||
...columnArr,
|
||||
{
|
||||
label: '组件标识',
|
||||
prop: 'identifier',
|
||||
type: 'input',
|
||||
width: 150,
|
||||
},
|
||||
{
|
||||
label: '组件版本',
|
||||
prop: 'componentVersion',
|
||||
type: 'input',
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
label: '组件描述',
|
||||
prop: 'recommend',
|
||||
type: 'input',
|
||||
width: 150,
|
||||
},
|
||||
{
|
||||
label: '组件评分',
|
||||
prop: 'stars',
|
||||
type: 'input',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
label: '审核结果',
|
||||
prop: 'reviewOpinion',
|
||||
type: 'input',
|
||||
width: 'auto',
|
||||
},
|
||||
];
|
||||
@ -0,0 +1,59 @@
|
||||
import { option as baseOption } from '../componentBase';
|
||||
import { deepClone } from '@/util/util';
|
||||
import { globalOption } from '@/const/globalOption';
|
||||
|
||||
export const permissionConstant = {
|
||||
ADMIN: 'admin',
|
||||
READ: 'read',
|
||||
WRITE: 'write',
|
||||
};
|
||||
|
||||
// 系统模块
|
||||
export const permissionDic = [
|
||||
{
|
||||
label: '管理',
|
||||
value: 'admin',
|
||||
},
|
||||
{
|
||||
label: '读',
|
||||
value: 'read',
|
||||
},
|
||||
{
|
||||
label: '写',
|
||||
value: 'write',
|
||||
},
|
||||
];
|
||||
|
||||
export const userOption = {
|
||||
...globalOption,
|
||||
addBtn: false,
|
||||
editBtn: false,
|
||||
viewBtn: false,
|
||||
header: false,
|
||||
delBtn: false,
|
||||
height: 200,
|
||||
menuWidth: 100,
|
||||
column: [
|
||||
{
|
||||
label: '用户账号',
|
||||
prop: 'collaboratorAccount',
|
||||
},
|
||||
{
|
||||
label: '操作权限',
|
||||
prop: 'permission',
|
||||
slot: true,
|
||||
width: '120px',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export const option = deepClone(baseOption);
|
||||
option.header = false;
|
||||
option.addBtn = false;
|
||||
option.column.push({
|
||||
label: '权限',
|
||||
prop: 'permission',
|
||||
type: 'select',
|
||||
dicData: permissionDic,
|
||||
display: false,
|
||||
});
|
||||
@ -0,0 +1,53 @@
|
||||
import { globalOption } from '../globalOption';
|
||||
|
||||
export const option = {
|
||||
...globalOption,
|
||||
height: 'auto',
|
||||
calcHeight: 30,
|
||||
tip: false,
|
||||
dialogClickModal: false,
|
||||
header: false,
|
||||
menu: true,
|
||||
editBtn: false,
|
||||
delBtn: false,
|
||||
menuWidth: '100',
|
||||
column: [
|
||||
{
|
||||
label: '#',
|
||||
prop: 'index',
|
||||
fixed: true,
|
||||
width: 40,
|
||||
},
|
||||
{
|
||||
label: '名称',
|
||||
prop: 'name',
|
||||
overHidden: true,
|
||||
width: 200,
|
||||
},
|
||||
{
|
||||
label: '类型',
|
||||
prop: 'type',
|
||||
overHidden: true,
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
label: '格式',
|
||||
prop: 'format',
|
||||
overHidden: true,
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
label: '状态',
|
||||
prop: 'status',
|
||||
overHidden: true,
|
||||
slot: true,
|
||||
width: 150,
|
||||
},
|
||||
{
|
||||
label: 'URL',
|
||||
prop: 'url',
|
||||
slot: true,
|
||||
overHidden: true,
|
||||
},
|
||||
],
|
||||
};
|
||||
@ -0,0 +1,172 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Modal, Form, Input, Message } from '@arco-design/web-react';
|
||||
import EditableTable from '@/pages/componentDevelopment/componentList/editableTable';
|
||||
import { updateComponentDesign } from '@/api/componentDevelopProcess';
|
||||
|
||||
const FormItem = Form.Item;
|
||||
const TextArea = Input.TextArea;
|
||||
|
||||
const AddApiModal = ({
|
||||
visible,
|
||||
baseInfo,
|
||||
componentDesignProgress,
|
||||
componentDesignData,
|
||||
onUpdateComponentDesign,
|
||||
onCancel,
|
||||
onOk
|
||||
}) => {
|
||||
const [form] = Form.useForm();
|
||||
const [parametersData, setParametersData] = useState([]);
|
||||
const [responsesData, setResponsesData] = useState([]);
|
||||
|
||||
// 当 visible 或 componentDesignProgress 变化时,设置表单初始值
|
||||
useEffect(() => {
|
||||
if (visible && componentDesignProgress) {
|
||||
// 设置表单字段值
|
||||
form.setFieldsValue({
|
||||
ident: componentDesignProgress.ident || '',
|
||||
desc: componentDesignProgress.desc || ''
|
||||
});
|
||||
|
||||
// 设置参数表格数据
|
||||
if (componentDesignProgress.parameters && Array.isArray(componentDesignProgress.parameters)) {
|
||||
setParametersData(componentDesignProgress.parameters.map((param, index) => ({
|
||||
key: param.id || index,
|
||||
...param
|
||||
})));
|
||||
}
|
||||
else {
|
||||
setParametersData([]);
|
||||
}
|
||||
|
||||
// 设置响应表格数据
|
||||
if (componentDesignProgress.responses && Array.isArray(componentDesignProgress.responses)) {
|
||||
setResponsesData(componentDesignProgress.responses.map((response, index) => ({
|
||||
key: response.id || index,
|
||||
...response
|
||||
})));
|
||||
}
|
||||
else {
|
||||
setResponsesData([]);
|
||||
}
|
||||
}
|
||||
else if (visible) {
|
||||
// 重置表单和表格数据
|
||||
form.resetFields();
|
||||
setParametersData([]);
|
||||
setResponsesData([]);
|
||||
}
|
||||
}, [visible, componentDesignProgress, form]);
|
||||
|
||||
const submit = async () => {
|
||||
try {
|
||||
await form.validate();
|
||||
const formData = form.getFields();
|
||||
|
||||
// 构造要提交的数据
|
||||
const params = {
|
||||
baseInfo
|
||||
};
|
||||
|
||||
// 如果是编辑模式且有完整的组件设计数据,需要更新对应的记录
|
||||
if (componentDesignProgress && componentDesignData && Array.isArray(componentDesignData)) {
|
||||
// 更新对应的记录
|
||||
const updatedOperates = componentDesignData.map(item => {
|
||||
if (item.ident === componentDesignProgress.ident) {
|
||||
// 更新记录
|
||||
return {
|
||||
...formData,
|
||||
type: 'EVENT',
|
||||
parameters: parametersData,
|
||||
responses: responsesData
|
||||
};
|
||||
}
|
||||
return item;
|
||||
});
|
||||
|
||||
params['operates'] = updatedOperates;
|
||||
}
|
||||
else if (componentDesignData && Array.isArray(componentDesignData)) {
|
||||
// 新增模式,将新记录添加到现有数据中
|
||||
const newOperate = {
|
||||
...formData,
|
||||
type: 'EVENT',
|
||||
parameters: parametersData,
|
||||
responses: responsesData
|
||||
};
|
||||
|
||||
params['operates'] = [...componentDesignData, newOperate];
|
||||
}
|
||||
else {
|
||||
// 没有现有数据时,创建包含单个元素的数组
|
||||
params['operates'] = [{
|
||||
...formData,
|
||||
type: 'EVENT',
|
||||
parameters: parametersData,
|
||||
responses: responsesData
|
||||
}];
|
||||
}
|
||||
|
||||
const res: any = await updateComponentDesign(params);
|
||||
|
||||
if (res.code === 200) {
|
||||
Message.success(componentDesignProgress ? '更新成功' : '新增成功');
|
||||
// 调用父组件传递的 onUpdateComponentDesign 回调,用于更新父组件中的数据
|
||||
onUpdateComponentDesign && onUpdateComponentDesign(params);
|
||||
}
|
||||
else {
|
||||
Message.error(res.message);
|
||||
}
|
||||
|
||||
// 调用父组件传递的 onOk 回调,并传递数据
|
||||
onOk && onOk(params);
|
||||
} catch (error) {
|
||||
console.error('表单验证失败:', error);
|
||||
Message.error('请检查表单填写是否正确');
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title={componentDesignProgress ? '编辑接口' : '新增接口'}
|
||||
visible={visible}
|
||||
onCancel={onCancel}
|
||||
onOk={submit}
|
||||
style={{ width: '50%' }}
|
||||
okButtonProps={{ disabled: !visible }}
|
||||
>
|
||||
<Form form={form} layout="vertical">
|
||||
<FormItem
|
||||
label="接口名称"
|
||||
field="ident"
|
||||
required
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: '请输入接口名称'
|
||||
}
|
||||
]}
|
||||
>
|
||||
<Input placeholder="请输入接口名称" />
|
||||
</FormItem>
|
||||
<FormItem label="描述" field="desc">
|
||||
<TextArea placeholder="请输入描述" />
|
||||
</FormItem>
|
||||
<FormItem label="输入参数" field="parameters">
|
||||
<EditableTable
|
||||
onDataUpdate={setParametersData}
|
||||
initialData={componentDesignProgress?.parameters || []}
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem label="输出参数" field="responses">
|
||||
<EditableTable
|
||||
onDataUpdate={setResponsesData}
|
||||
initialData={componentDesignProgress?.responses || []}
|
||||
/>
|
||||
</FormItem>
|
||||
</Form>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default AddApiModal;
|
||||
@ -0,0 +1,90 @@
|
||||
import React from 'react';
|
||||
import styles from './style/compReview.module.less';
|
||||
|
||||
const CompReview = ({ componentDesignData }) => {
|
||||
|
||||
// 确保数据是数组格式
|
||||
const dataArray = Array.isArray(componentDesignData) ? componentDesignData :
|
||||
componentDesignData ? [componentDesignData] : [];
|
||||
|
||||
return (
|
||||
<div className={styles['comp-review-container']}>
|
||||
<div className={styles['comp-review-header']}>
|
||||
<div className={styles['comp-review-title']}>组件预览</div>
|
||||
</div>
|
||||
<div className={styles['comp-review-body']}>
|
||||
<div className={styles['comp-review-content']}>
|
||||
{/* 上方内容 - 根据数组长度渲染 */}
|
||||
<div className={styles['comp-review-top']}>
|
||||
{dataArray.map((data, index) => (
|
||||
<div key={data.key || data.id || index} className={styles['comp-review-item']}>
|
||||
<div className={styles['comp-review-name']}>{data.ident}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* 输入输出部分 - 根据数组长度渲染 */}
|
||||
<div className={styles['comp-review-io']}>
|
||||
{/* 输入部分 */}
|
||||
<div className={styles['comp-review-input']}>
|
||||
<div className={styles['comp-review-io-label']}>input</div>
|
||||
<div className={styles['comp-review-io-items']}>
|
||||
{dataArray.map((data, index) => {
|
||||
// 确保参数始终是数组格式
|
||||
const parameters = Array.isArray(data.parameters) ? data.parameters :
|
||||
(data.parameters ? [data.parameters] : []);
|
||||
|
||||
return (
|
||||
<div key={data.key || data.id || index}>
|
||||
{parameters.length > 0 ? (
|
||||
parameters.map((param, paramIndex) => (
|
||||
<div key={paramIndex} className={styles['comp-review-io-item']}>
|
||||
{param.ident} {param.type}
|
||||
</div>
|
||||
))
|
||||
) : (
|
||||
<div className={styles['comp-review-io-placeholder']}>
|
||||
无输入参数
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 输出部分 */}
|
||||
<div className={styles['comp-review-output']}>
|
||||
<div className={styles['comp-review-io-label']}>output</div>
|
||||
<div className={styles['comp-review-io-items']}>
|
||||
{dataArray.map((data, index) => {
|
||||
// 确保响应始终是数组格式
|
||||
const responses = Array.isArray(data.responses) ? data.responses :
|
||||
(data.responses ? [data.responses] : []);
|
||||
|
||||
return (
|
||||
<div key={data.key || data.id || index}>
|
||||
{responses.length > 0 ? (
|
||||
responses.map((response, responseIndex) => (
|
||||
<div key={responseIndex} className={styles['comp-review-io-item']}>
|
||||
{response.type} {response.ident}
|
||||
</div>
|
||||
))
|
||||
) : (
|
||||
<div className={styles['comp-review-io-placeholder']}>
|
||||
无输出参数
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default CompReview;
|
||||
@ -0,0 +1,166 @@
|
||||
import React from 'react';
|
||||
import { Button, Dropdown, Menu, Message } from '@arco-design/web-react';
|
||||
import { ComponentItem } from '@/api/interface';
|
||||
import {
|
||||
componentStatusConstant,
|
||||
publicStatus,
|
||||
publishStatusConstant
|
||||
} from '@/const/isdp/componentBase';
|
||||
|
||||
interface HandleButtonGroupProps {
|
||||
row: ComponentItem;
|
||||
index: number;
|
||||
onHandlePublishComponent: (row: ComponentItem) => void;
|
||||
onGoToReview: (row: ComponentItem) => void;
|
||||
onPublishOrRevokeComponent: (action: 'publish' | 'revoke', identifier: string, version: string) => void;
|
||||
onNavTo: (id: number, type: string) => void;
|
||||
onSourceCodeView: (row: ComponentItem) => void;
|
||||
onShowEdit: (row: ComponentItem, index: number) => void;
|
||||
onCopyHandler: (row: ComponentItem) => void;
|
||||
onShareCollaboration: (row: ComponentItem) => void;
|
||||
onExportComponent: (id: number) => void;
|
||||
onStopComponentShow: (row: ComponentItem) => void;
|
||||
onRowDel: (row: ComponentItem) => void;
|
||||
}
|
||||
|
||||
const HandleButtonGroup: React.FC<HandleButtonGroupProps> = ({
|
||||
row,
|
||||
index,
|
||||
onHandlePublishComponent,
|
||||
onGoToReview,
|
||||
onPublishOrRevokeComponent,
|
||||
onNavTo,
|
||||
onSourceCodeView,
|
||||
onShowEdit,
|
||||
onCopyHandler,
|
||||
onShareCollaboration,
|
||||
onExportComponent,
|
||||
onStopComponentShow,
|
||||
onRowDel
|
||||
}) => {
|
||||
|
||||
// 检查组件状态是否符合条件
|
||||
const isEligible = (eligibleStatuses: string[], currentStatus: string) => {
|
||||
return eligibleStatuses.includes(currentStatus.toLowerCase());
|
||||
};
|
||||
|
||||
// 构建更多操作菜单
|
||||
const renderDropdownMenu = () => {
|
||||
const items = [];
|
||||
|
||||
// 组件复制
|
||||
if (!isEligible([componentStatusConstant.DEFAULT, componentStatusConstant.DESIGN], row.componentStatus)) {
|
||||
items.push(
|
||||
<Menu.Item key="copy">
|
||||
<Button type="text" onClick={() => onCopyHandler(row)}>组件复制</Button>
|
||||
</Menu.Item>
|
||||
);
|
||||
}
|
||||
|
||||
// 分享协作
|
||||
if (!isEligible([componentStatusConstant.DESIGN, componentStatusConstant.DEFAULT], row.componentStatus)) {
|
||||
items.push(
|
||||
<Menu.Item key="share">
|
||||
<Button type="text" onClick={() => onShareCollaboration(row)}>分享协作</Button>
|
||||
</Menu.Item>
|
||||
);
|
||||
}
|
||||
|
||||
// 导出组件
|
||||
items.push(
|
||||
<Menu.Item key="export">
|
||||
<Button type="text" onClick={() => onExportComponent(row.id)}>导出组件</Button>
|
||||
</Menu.Item>
|
||||
);
|
||||
|
||||
// 下架组件
|
||||
if (isEligible([componentStatusConstant.DEPLOYED], row.componentStatus)) {
|
||||
items.push(
|
||||
<Menu.Item key="stop">
|
||||
<Button type="text" onClick={() => onStopComponentShow(row)}>下架组件</Button>
|
||||
</Menu.Item>
|
||||
);
|
||||
}
|
||||
|
||||
// 删除组件
|
||||
if (!isEligible([componentStatusConstant.DEPLOYED, componentStatusConstant.PUBLISHED], row.componentStatus)) {
|
||||
items.push(
|
||||
<Menu.Item key="delete">
|
||||
<Button type="text" onClick={() => onRowDel(row)}>删除组件</Button>
|
||||
</Menu.Item>
|
||||
);
|
||||
}
|
||||
|
||||
return <Menu>{items}</Menu>;
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* 发布组件/更新版本*/}
|
||||
{row.publicStatus !== publicStatus.REVIEW && isEligible([componentStatusConstant.CODING, componentStatusConstant.DEPLOYED, componentStatusConstant.PUBLISHED], row.componentStatus) && (
|
||||
<Button
|
||||
type="text"
|
||||
onClick={() => onHandlePublishComponent(row)}
|
||||
>
|
||||
{row.publicStatus === publicStatus.PUBLISHED ? '更新版本' : '发布组件'}
|
||||
</Button>
|
||||
)}
|
||||
|
||||
{(row.publicStatus === publicStatus.REVIEW || row.publicStatus === publicStatus.REJECTED) && (
|
||||
<Button
|
||||
type="text"
|
||||
onClick={() => onGoToReview(row)}
|
||||
>
|
||||
查看审核
|
||||
</Button>
|
||||
)}
|
||||
|
||||
{/* 公开组件/取消公开 */}
|
||||
{row.publishStatus !== publishStatusConstant.PUBLISHED ? (
|
||||
<Button
|
||||
type="text"
|
||||
onClick={() => onPublishOrRevokeComponent('publish', row.identifier, row.version)}
|
||||
>
|
||||
公开组件
|
||||
</Button>
|
||||
) : (
|
||||
<Button
|
||||
type="text"
|
||||
onClick={() => onPublishOrRevokeComponent('revoke', row.identifier, row.version)}
|
||||
>
|
||||
取消公开
|
||||
</Button>
|
||||
)}
|
||||
|
||||
{/* 查看源码 */}
|
||||
{isEligible(
|
||||
[componentStatusConstant.CODING, componentStatusConstant.DEPLOYED, componentStatusConstant.PUBLISHED],
|
||||
row.componentStatus
|
||||
) ? (
|
||||
<Button
|
||||
type="text"
|
||||
onClick={() => onSourceCodeView(row)}
|
||||
>
|
||||
查看源码
|
||||
</Button>
|
||||
) : null}
|
||||
|
||||
{/* 编辑组件 */}
|
||||
<Button
|
||||
type="text"
|
||||
onClick={() => onShowEdit(row, index)}
|
||||
>
|
||||
编辑组件
|
||||
</Button>
|
||||
|
||||
{/* 更多操作 */}
|
||||
<Dropdown droplist={renderDropdownMenu()} position="bl">
|
||||
<Button type="text">
|
||||
更多操作
|
||||
</Button>
|
||||
</Dropdown>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default HandleButtonGroup;
|
||||
@ -0,0 +1,60 @@
|
||||
.add-component-modal {
|
||||
:global(.arco-modal-content) {
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
background-color: #f7f8fa
|
||||
}
|
||||
}
|
||||
|
||||
.add-component-container {
|
||||
|
||||
.first-half {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 20px;
|
||||
|
||||
.component-preview {
|
||||
overflow: hidden;
|
||||
width: 30%;
|
||||
background-color: #fff;
|
||||
padding: 15px 5px;
|
||||
border: 1px solid transparent;
|
||||
border-top-right-radius: 5px;
|
||||
border-bottom-right-radius: 5px;
|
||||
}
|
||||
|
||||
.component-info {
|
||||
width: 67%;
|
||||
background-color: #fff;
|
||||
padding: 15px 5px;
|
||||
border: 1px solid transparent;
|
||||
border-top-left-radius: 5px;
|
||||
border-bottom-left-radius: 5px;
|
||||
|
||||
.markdown-label {
|
||||
font-size: 14px;
|
||||
white-space: normal;
|
||||
color: var(--color-text-2);
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.markdown-editor {
|
||||
padding-left: 50px;
|
||||
padding-right: 40px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.last-half {
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
background-color: #fff;
|
||||
padding: 15px 20px;
|
||||
|
||||
.last-half-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,94 @@
|
||||
.comp-review-container {
|
||||
width: 300px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.comp-review-header {
|
||||
background-color: #FFA500;
|
||||
color: white;
|
||||
padding: 8px 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.comp-review-icon {
|
||||
font-size: 16px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.comp-review-title {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.comp-review-actions {
|
||||
font-size: 12px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.comp-review-body {
|
||||
background-color: white;
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.comp-review-content {
|
||||
border-radius: 4px;
|
||||
padding: 8px;
|
||||
min-height: 120px;
|
||||
}
|
||||
|
||||
.comp-review-top {
|
||||
margin-bottom: 12px;
|
||||
border-bottom: 1px solid #ebeef5;
|
||||
}
|
||||
|
||||
.comp-review-name {
|
||||
font-weight: bold;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.comp-review-desc {
|
||||
color: #666;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.comp-review-io {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.comp-review-input,
|
||||
.comp-review-output {
|
||||
flex: 1;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.comp-review-output {
|
||||
border-right: none;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.comp-review-io-label {
|
||||
font-weight: bold;
|
||||
margin-bottom: 4px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.comp-review-io-items {
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.comp-review-io-item {
|
||||
margin-bottom: 4px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
|
||||
.comp-review-io-placeholder {
|
||||
color: #999;
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
padding: 8px 0;
|
||||
}
|
||||
@ -0,0 +1,101 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import dayjs from 'dayjs';
|
||||
import { Tabs, Table, TableColumnProps, Input } from '@arco-design/web-react';
|
||||
import { IconCheckCircleFill, IconLoading, IconCloseCircleFill } from '@arco-design/web-react/icon';
|
||||
import { formatSeconds } from '@/utils/common';
|
||||
|
||||
const TabPane = Tabs.TabPane;
|
||||
|
||||
const columns: TableColumnProps[] = [
|
||||
{
|
||||
title: '节点',
|
||||
dataIndex: 'name'
|
||||
},
|
||||
{
|
||||
title: '时间',
|
||||
render: (_: any, record) => (
|
||||
<>
|
||||
<span>开始{dayjs(record.startTime).format('YYYY-MM-DD HH:mm:ss')}</span>
|
||||
<br></br>
|
||||
{record.endTime > 0 && <span>结束{dayjs(record.endTime).format('YYYY-MM-DD HH:mm:ss')}</span>}
|
||||
</>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: '耗时',
|
||||
dataIndex: 'duration',
|
||||
render: (_: any, record) => (
|
||||
record.duration > 1000 ?
|
||||
<span>{formatSeconds((record.duration / 1000).toString())}</span> :
|
||||
<span>{record.duration}毫秒</span>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'state',
|
||||
render: (_: any, record) => (
|
||||
<>
|
||||
{record.state === 1 && <IconCheckCircleFill fontSize={30} style={{ color: 'rgb(var(--green-6))' }} />}
|
||||
{record.state === 0 && <IconLoading fontSize={30} style={{ color: 'rgb(var(--arcoblue-4))' }} />}
|
||||
{record.state === -1 && <IconCloseCircleFill fontSize={30} style={{ color: 'rgb(var(--red-6))' }} />}
|
||||
</>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: '输入参数',
|
||||
dataIndex: 'input',
|
||||
render: (_: any, record) => (
|
||||
<span>{JSON.stringify(record.input)}</span>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: '输出参数',
|
||||
dataIndex: 'output',
|
||||
render: (_: any, record) => (
|
||||
<span>{JSON.stringify(record.output)}</span>
|
||||
)
|
||||
}
|
||||
];
|
||||
|
||||
const RunTimeData = ({ logData }) => {
|
||||
const [currentTab, setCurrentTab] = useState('0');
|
||||
const [tabList, setTabList] = useState([]);
|
||||
const [keyMap, setKeyMap] = useState({});
|
||||
const [data, setData] = useState([]);
|
||||
|
||||
const onTabChange = (key) => {
|
||||
setCurrentTab(key);
|
||||
setData(keyMap[key]);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const tempMap = {};
|
||||
const list = logData.map((item, index) => {
|
||||
tempMap[index] = item.nodes;
|
||||
return {
|
||||
key: index,
|
||||
title: item.parent
|
||||
};
|
||||
});
|
||||
setKeyMap(tempMap);
|
||||
setTabList(list);
|
||||
}, [logData]);
|
||||
|
||||
useEffect(() => {
|
||||
if (tabList.length > 0) {
|
||||
setData(keyMap[0]);
|
||||
onTabChange(0); // 默认选中第一个
|
||||
}
|
||||
}, [tabList]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Tabs type="card" onChange={onTabChange} activeTab={currentTab}>
|
||||
{tabList.map((item) => (<TabPane key={item.key} title={item.title}></TabPane>))}
|
||||
</Tabs>
|
||||
<Table columns={columns} data={data} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default RunTimeData;
|
||||
Loading…
Reference in New Issue