From c6b1eb6e9aef4d4489e4a550cea662f4e62f1912 Mon Sep 17 00:00:00 2001 From: ZLY Date: Fri, 7 Nov 2025 10:04:54 +0800 Subject: [PATCH 01/41] =?UTF-8?q?feat(ide):=20=E6=B7=BB=E5=8A=A0=E8=BF=90?= =?UTF-8?q?=E8=A1=8C=E6=97=B6=E6=95=B0=E6=8D=AE=E5=B1=95=E7=A4=BA=E7=BB=84?= =?UTF-8?q?=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ideContainer/components/runTimeData.tsx | 101 ++++++++++++++++++ src/pages/ideContainer/logBar.tsx | 30 ++---- 2 files changed, 112 insertions(+), 19 deletions(-) create mode 100644 src/pages/ideContainer/components/runTimeData.tsx diff --git a/src/pages/ideContainer/components/runTimeData.tsx b/src/pages/ideContainer/components/runTimeData.tsx new file mode 100644 index 0000000..e8da0c1 --- /dev/null +++ b/src/pages/ideContainer/components/runTimeData.tsx @@ -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) => ( + <> + 开始{dayjs(record.startTime).format('YYYY-MM-DD HH:mm:ss')} +

+ {record.endTime > 0 && 结束{dayjs(record.endTime).format('YYYY-MM-DD HH:mm:ss')}} + + ) + }, + { + title: '耗时', + dataIndex: 'duration', + render: (_: any, record) => ( + record.duration > 1000 ? + {formatSeconds((record.duration / 1000).toString())} : + {record.duration}毫秒 + ) + }, + { + title: '状态', + dataIndex: 'state', + render: (_: any, record) => ( + <> + {record.state === 1 && } + {record.state === 0 && } + {record.state === -1 && } + + ) + }, + { + title: '输入参数', + dataIndex: 'input', + render: (_: any, record) => ( + {JSON.stringify(record.input)} + ) + }, + { + title: '输出参数', + dataIndex: 'output', + render: (_: any, record) => ( + {JSON.stringify(record.output)} + ) + } +]; + +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 ( + <> + + {tabList.map((item) => ())} + + + + ); +}; + +export default RunTimeData; \ No newline at end of file diff --git a/src/pages/ideContainer/logBar.tsx b/src/pages/ideContainer/logBar.tsx index a36aeef..3b6744c 100644 --- a/src/pages/ideContainer/logBar.tsx +++ b/src/pages/ideContainer/logBar.tsx @@ -3,7 +3,8 @@ import { ResizeBox, Tabs } from '@arco-design/web-react'; import styles from './style/logBar.module.less'; import { updateLogBarStatus } from '@/store/ideContainer'; import { useSelector, useDispatch } from 'react-redux'; -import { getNodeData } from '@/api/appIns'; // 添加导入 +import { getNodeData } from '@/api/appIns'; +import RunTimeData from './components/runTimeData'; const TabPane = Tabs.TabPane; @@ -50,15 +51,14 @@ const LogBar: React.FC = () => { const [tabs] = useState(data); const [activeTab, setActiveTab] = useState('1'); const resizeBoxRef = useRef(null); // 引用 ResizeBox 容器 - const { logBarStatus, appRuntimeData } = useSelector((state: any) => state.ideContainer); - const dispatch = useDispatch(); const [validationLogs, setValidationLogs] = useState([]); const [runtimeLogs, setRuntimeLogs] = useState([]); // 添加运行时日志状态 const [logContainerHeight, setLogContainerHeight] = useState('250px'); // 添加日志容器高度状态 - const { currentAppData } = useSelector((state: any) => state.ideContainer); - // 添加运行数据状态 - const [runtimeData, setRuntimeData] = useState({}); + const [runtimeData, setRuntimeData] = useState({}); // 添加运行数据状态 const [loading, setLoading] = useState(false); + const { logBarStatus, appRuntimeData, currentAppData } = useSelector((state: any) => state.ideContainer); + + const dispatch = useDispatch(); // 处理 Tab 点击事件 const handleTabClick = (key: string) => { @@ -113,7 +113,7 @@ const LogBar: React.FC = () => { setValidationLogs(prev => [...prev, newLog]); // 自动切换到校验日志tab并展开logBar - setActiveTab('2'); + // setActiveTab('2'); dispatch(updateLogBarStatus(true)); } // 如果是运行时日志 @@ -128,7 +128,6 @@ const LogBar: React.FC = () => { setRuntimeLogs(prev => [...prev, newLog]); // 自动切换到运行日志tab并展开logBar - setActiveTab('1'); dispatch(updateLogBarStatus(true)); // 同时将日志添加到当前应用的运行日志中 @@ -159,11 +158,11 @@ const LogBar: React.FC = () => { let intervalId: NodeJS.Timeout | null = null; // 只有在当前tab是运行数据且有当前应用时才开始轮询 - if (activeTab === '3' && currentAppData?.id && logBarStatus) { + if (activeTab === '3' && currentAppData?.id && logBarStatus && appRuntimeData[currentAppData.id]?.runId) { const fetchRuntimeData = async () => { try { setLoading(true); - const response = await getNodeData(currentAppData.id); + const response = await getNodeData(appRuntimeData[currentAppData.id].runId); setRuntimeData(prev => ({ ...prev, [currentAppData.id]: response.data @@ -245,17 +244,10 @@ const LogBar: React.FC = () => { return (
- {loading ? ( -

加载中...

- ) : !currentAppData?.id ? ( -

请选择应用

- ) : !currentAppDataContent ? ( + {!currentAppDataContent ? (

暂无运行数据

) : ( -
-

应用: {currentAppData.name}

-
{JSON.stringify(currentAppDataContent, null, 2)}
-
+ )}
); From 9ce152a85ce51791f091f4e47abc8579c5d93fa8 Mon Sep 17 00:00:00 2001 From: ZLY Date: Fri, 7 Nov 2025 14:12:30 +0800 Subject: [PATCH 02/41] =?UTF-8?q?feat(scene):=20=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E8=87=AA=E5=AE=9A=E4=B9=89=E5=B0=81=E9=9D=A2=E5=9B=BE=E7=89=87?= =?UTF-8?q?=E5=AE=BD=E5=BA=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/scene/cover.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/pages/scene/cover.tsx b/src/pages/scene/cover.tsx index 2605528..327d1f1 100644 --- a/src/pages/scene/cover.tsx +++ b/src/pages/scene/cover.tsx @@ -5,10 +5,11 @@ import { getImageUrl, imageList } from '@/utils/pubUse'; interface CoverProps { defaultImage?: string; + imgWidth?: number | string; onImageChange?: (image: string) => void; } -const Cover: React.FC = ({ defaultImage, onImageChange }) => { +const Cover: React.FC = ({ defaultImage, onImageChange, imgWidth = 200 }) => { const [visible, setVisible] = useState(false); const [currentImage, setCurrentImage] = useState(''); @@ -35,7 +36,7 @@ const Cover: React.FC = ({ defaultImage, onImageChange }) => { return (
- setVisible(true)}> + setVisible(true)}>
= ({ defaultImage, onImageChange }) => { render={(item, index) => { return ( - { + { handleImageSelect(item.src); }}> From 4a9390a211ce83eadca20fd5a85db18a2dae2ec6 Mon Sep 17 00:00:00 2001 From: ZLY Date: Fri, 7 Nov 2025 14:12:44 +0800 Subject: [PATCH 03/41] =?UTF-8?q?feat(component):=20=E5=AE=9E=E7=8E=B0?= =?UTF-8?q?=E7=BB=84=E4=BB=B6=E5=88=97=E8=A1=A8=E9=A1=B5=E9=9D=A2=E5=8F=8A?= =?UTF-8?q?=E6=96=B0=E5=A2=9E=E7=BB=84=E4=BB=B6=E5=BC=B9=E7=AA=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/componentBase.ts | 9 + src/api/interface/index.ts | 27 +- .../componentList/addComponentModal.tsx | 146 ++++++++++ .../componentList/index.tsx | 270 ++++++++++++------ .../style/addComponentModal.module.less | 48 ++++ 5 files changed, 419 insertions(+), 81 deletions(-) create mode 100644 src/api/componentBase.ts create mode 100644 src/pages/componentDevelopment/componentList/addComponentModal.tsx create mode 100644 src/pages/componentDevelopment/componentList/style/addComponentModal.module.less diff --git a/src/api/componentBase.ts b/src/api/componentBase.ts new file mode 100644 index 0000000..1710f92 --- /dev/null +++ b/src/api/componentBase.ts @@ -0,0 +1,9 @@ +import axios from 'axios'; + +// 公共路径 +const urlPrefix = '/api/v1/bpms-workbench'; + +// 我的组件 +export function getMyComponentList(params) { + return axios.get(`${urlPrefix}/componentBase/list`, { params }); +} \ No newline at end of file diff --git a/src/api/interface/index.ts b/src/api/interface/index.ts index 0af6396..36a1832 100644 --- a/src/api/interface/index.ts +++ b/src/api/interface/index.ts @@ -230,4 +230,29 @@ export interface GlobalParams { arrayType: any, defaultValue: any, sceneId: string, -} \ No newline at end of file +} + +// component +export interface ComponentItem { + id: number; + name: string; + identifier: string; + projectId: string; + componentClassify: string; + codeLanguage: string; + desc: string; + componentStatus: string; + tags: string[]; + version: string; + logoUrl: string; + localProjectPath: string; + publishStatus: number; + publicStatus: number; + publishTime: string; + updateTime: string; + onlineInstanceCount: number; + createUser: string; + permission: string; + repoCloneUrl: string; + createUserName: string; +} diff --git a/src/pages/componentDevelopment/componentList/addComponentModal.tsx b/src/pages/componentDevelopment/componentList/addComponentModal.tsx new file mode 100644 index 0000000..7bb2a94 --- /dev/null +++ b/src/pages/componentDevelopment/componentList/addComponentModal.tsx @@ -0,0 +1,146 @@ +import React, { useState } from 'react'; +import { Modal, Form, Input, Grid, Space, Divider, Button, Table, TableColumnProps } from '@arco-design/web-react'; +import styles from './style/addComponentModal.module.less'; +import Cover from '@/pages/scene/cover'; + +const FormItem = Form.Item; + +const columns: TableColumnProps[] = [ + { + title: '接口名称', + dataIndex: 'name' + }, + { + title: '输入参数', + dataIndex: 'salary' + }, + { + title: '输出参数', + dataIndex: 'address' + }, + { + title: '操作', + dataIndex: 'email' + } +]; +const data = [ + { + key: '1', + name: 'Jane Doe', + salary: 23000, + address: '32 Park Road, London', + email: 'jane.doe@example.com' + } +]; + + +const AddComponentModal = ({ visible, setVisible }) => { + const [selectedImage, setSelectedImage] = useState(''); + const [form] = Form.useForm(); + + const transformImageUrl = (image: string) => { + if (!image) return; + if (image.includes('/')) setSelectedImage(image.split('/')[image.split('/').length - 1]); + else setSelectedImage(image); + }; + + const modalFooter = () => { + return ( + + + + + + ); + }; + return ( + setVisible(false)} + onCancel={() => setVisible(false)} + autoFocus={false} + focusLock={true} + style={{ width: '60%' }} + footer={modalFooter} + > +
+
+
+ + transformImageUrl(selectedImage)} + /> + + +
组件外壳 +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 富文本编辑器 + +
+
+
+
+

组件接口

+ +
+ +
+ + + + ); +}; + +export default AddComponentModal; \ No newline at end of file diff --git a/src/pages/componentDevelopment/componentList/index.tsx b/src/pages/componentDevelopment/componentList/index.tsx index a3289a0..520b443 100644 --- a/src/pages/componentDevelopment/componentList/index.tsx +++ b/src/pages/componentDevelopment/componentList/index.tsx @@ -1,98 +1,208 @@ -import React, { useState } from 'react'; +import React, { useState, useEffect } from 'react'; import styles from './style/index.module.less'; -import { Button, Divider, Input, Space, Table, Radio } from '@arco-design/web-react'; +import { Button, Divider, Input, Space, Table, Radio, Pagination } from '@arco-design/web-react'; import { IconSearch } from '@arco-design/web-react/icon'; +import { getMyComponentList } from '@/api/componentBase'; +import { ComponentItem } from '@/api/interface'; +import AddComponentModal from '@/pages/componentDevelopment/componentList/addComponentModal'; const Group = Radio.Group; +const menuItems = [ + { + key: '1', + label: '我的组件', + icon: '/ideContainer/icon/myComp.png', + activeIcon: '/ideContainer/icon/myComp_active.png' + }, + { + key: '2', + label: '协同组件', + icon: '/ideContainer/icon/teamComp.png', + activeIcon: '/ideContainer/icon/teamComp_active.png' + }, + { + key: '3', + label: '组件审核', + icon: '/ideContainer/icon/compAudit.png', + activeIcon: '/ideContainer/icon/compAudit_active.png' + } +]; + +const columns = [ + { + title: '组件名称', + dataIndex: 'name' + }, + { + title: '标识符', + dataIndex: 'identifier' + }, + { + title: '分类', + dataIndex: 'componentClassify' + }, + { + title: '语言', + dataIndex: 'codeLanguage' + }, + { + title: '描述', + dataIndex: 'desc' + }, + { + title: '状态', + dataIndex: 'componentStatus' + }, + { + title: '更新时间', + dataIndex: 'updateTime' + }, + { + title: '操作', + dataIndex: 'operations', + render: () => ( + + ) + } +]; + const GlobalVarContainer = () => { - const [selectedItem, setSelectedItem] = useState('数字类型'); + const [selectedItem, setSelectedItem] = useState('我的组件'); + const [componentData, setComponentData] = useState([]); + const [pagination, setPagination] = useState({ + totalCount: 0, + pageSize: 10, + totalPage: 0, + currPage: 1 + }); + const [loading, setLoading] = useState(false); + const [visible, setVisible] = useState(false); - const menuItems = [ - { - key: '1', - label: '我的组件', - icon: '/ideContainer/icon/myComp.png', - activeIcon: '/ideContainer/icon/myComp_active.png' - }, - { - key: '2', - label: '协同组件', - icon: '/ideContainer/icon/teamComp.png', - activeIcon: '/ideContainer/icon/teamComp_active.png' - }, - { - key: '3', - label: '组件审核', - icon: '/ideContainer/icon/compAudit.png', - activeIcon: '/ideContainer/icon/compAudit_active.png' + useEffect(() => { + if (selectedItem === '我的组件') { + fetchComponentData(); } - ]; + }, [selectedItem, pagination.currPage, pagination.pageSize]); + + const fetchComponentData = async () => { + setLoading(true); + try { + const res: any = await getMyComponentList({ + currPage: pagination.currPage, + pageSize: pagination.pageSize + }); + + setComponentData(res.list); + setPagination({ + totalCount: res.totalCount, + pageSize: res.pageSize, + totalPage: res.totalPage, + currPage: res.currPage + }); + } catch (error) { + console.error('获取组件列表失败:', error); + } finally { + setLoading(false); + } + }; + + const handlePageChange = (page: number, pageSize?: number) => { + setPagination({ + ...pagination, + currPage: page, + pageSize: pageSize || pagination.pageSize + }); + }; + return ( -
- {/*左侧菜单*/} -
- {menuItems.map((item, index) => ( -
setSelectedItem(item.label)} - > - - {item.label} + <> +
+ {/*左侧菜单*/} +
+ {menuItems.map((item, index) => ( +
setSelectedItem(item.label)} + > + + {item.label} +
+ ))} +
+
+ {/*头部*/} +
+
+ + } + placeholder={'搜索'} + style={{ width: 236 }} + /> + + +
+
+ + {['未设计', '编码中', '已部署'].map((item) => { + return ( + + {({ checked }) => { + return ( + + ); + }} + + ); + })} + + }> + + + + +
+
- ))} -
-
- {/*头部*/} -
-
- - } - placeholder={'搜索'} - style={{ width: 236 }} + {/*数据列表*/} +
+
+
+ `第 ${range[0]}-${range[1]} 条,共 ${total} 条`} + onChange={handlePageChange} /> - - -
-
- - {['未设计', '编码中', '已部署'].map((item) => { - return ( - - {({ checked }) => { - return ( - - ); - }} - - ); - })} - - }> - - - - +
- - - {/*数据列表*/} -
- {/*
*/} - + + {/*新增弹窗*/} + + ); }; diff --git a/src/pages/componentDevelopment/componentList/style/addComponentModal.module.less b/src/pages/componentDevelopment/componentList/style/addComponentModal.module.less new file mode 100644 index 0000000..4ef4d57 --- /dev/null +++ b/src/pages/componentDevelopment/componentList/style/addComponentModal.module.less @@ -0,0 +1,48 @@ +.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; + } + } + + .last-half { + box-sizing: border-box; + width: 100%; + background-color: #fff; + padding: 15px 20px; + + .last-half-header { + display: flex; + justify-content: space-between; + } + } +} \ No newline at end of file From eb24c838648be030531107d0057d1fc40350054a Mon Sep 17 00:00:00 2001 From: ZLY Date: Fri, 7 Nov 2025 17:33:59 +0800 Subject: [PATCH 04/41] =?UTF-8?q?build:=20=E5=A2=9E=E5=8A=A0@toast-ui/reac?= =?UTF-8?q?t-editor=E4=BE=9D=E8=B5=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 1 + pnpm-lock.yaml | 109 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 110 insertions(+) diff --git a/package.json b/package.json index 88598b9..33680f5 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "@codemirror/lang-python": "^6.2.1", "@loadable/component": "^5.13.2", "@reduxjs/toolkit": "^2.9.0", + "@toast-ui/react-editor": "^3.2.3", "@turf/turf": "^6.5.0", "@uiw/codemirror-theme-github": "^4.25.2", "@uiw/react-codemirror": "^4.21.25", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ba27a2e..f0d54d9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -35,6 +35,9 @@ importers: '@reduxjs/toolkit': specifier: ^2.9.0 version: 2.9.0(react-redux@7.2.9(react-dom@17.0.2(react@17.0.2))(react@17.0.2))(react@17.0.2) + '@toast-ui/react-editor': + specifier: ^3.2.3 + version: 3.2.3(react@17.0.2) '@turf/turf': specifier: ^6.5.0 version: 6.5.0 @@ -1140,6 +1143,14 @@ packages: resolution: {integrity: sha512-DOBOK255wfQxguUta2INKkzPj6AIS6iafZYiYmHn6W3pHlycSRRlvWKCfLDG10fXfLWqE3DJHgRUOyJYmARa7g==} engines: {node: '>=10'} + '@toast-ui/editor@3.2.2': + resolution: {integrity: sha512-ASX7LFjN2ZYQJrwmkUajPs7DRr9FsM1+RQ82CfTO0Y5ZXorBk1VZS4C2Dpxinx9kl55V4F8/A2h2QF4QMDtRbA==} + + '@toast-ui/react-editor@3.2.3': + resolution: {integrity: sha512-86QdgiOkBeSwRBEUWRKsTpnm6yu5j9HNJ3EfQN8EGcd7kI8k8AhExXyUJ3NNgNTzN7FfSKMw+1VaCDDC+aZ3dw==} + peerDependencies: + react: ^17.0.1 + '@turf/along@6.5.0': resolution: {integrity: sha512-LLyWQ0AARqJCmMcIEAXF4GEu8usmd4Kbz3qk1Oy5HoRNpZX47+i5exQtmIWKdqJ1MMhW26fCTXgpsEs5zgJ5gw==} @@ -2415,6 +2426,9 @@ packages: domelementtype@2.3.0: resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} + dompurify@2.5.8: + resolution: {integrity: sha512-o1vSNgrmYMQObbSSvF/1brBYEQPHhV1+gsmrusO7/GXtp1T9rCS8cXFqVxK/9crT1jA6Ccv+5MTSjBNqr7Sovw==} + domutils@1.7.0: resolution: {integrity: sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==} @@ -3547,6 +3561,9 @@ packages: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} engines: {node: '>= 0.8.0'} + orderedmap@2.1.1: + resolution: {integrity: sha512-TvAWxi0nDe1j/rtMcWcIj94+Ffe6n7zhow33h40SKxmsmozs6dz/e+EajymfoFcHd7sxNn8yHM8839uixMOV6g==} + os-browserify@0.3.0: resolution: {integrity: sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A==} @@ -3721,6 +3738,30 @@ packages: prop-types@15.8.1: resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + prosemirror-commands@1.7.1: + resolution: {integrity: sha512-rT7qZnQtx5c0/y/KlYaGvtG411S97UaL6gdp6RIZ23DLHanMYLyfGBV5DtSnZdthQql7W+lEVbpSfwtO8T+L2w==} + + prosemirror-history@1.4.1: + resolution: {integrity: sha512-2JZD8z2JviJrboD9cPuX/Sv/1ChFng+xh2tChQ2X4bB2HeK+rra/bmJ3xGntCcjhOqIzSDG6Id7e8RJ9QPXLEQ==} + + prosemirror-inputrules@1.5.1: + resolution: {integrity: sha512-7wj4uMjKaXWAQ1CDgxNzNtR9AlsuwzHfdFH1ygEHA2KHF2DOEaXl1CJfNPAKCg9qNEh4rum975QLaCiQPyY6Fw==} + + prosemirror-keymap@1.2.3: + resolution: {integrity: sha512-4HucRlpiLd1IPQQXNqeo81BGtkY8Ai5smHhKW9jjPKRc2wQIxksg7Hl1tTI2IfT2B/LgX6bfYvXxEpJl7aKYKw==} + + prosemirror-model@1.25.4: + resolution: {integrity: sha512-PIM7E43PBxKce8OQeezAs9j4TP+5yDpZVbuurd1h5phUxEKIu+G2a+EUZzIC5nS1mJktDJWzbqS23n1tsAf5QA==} + + prosemirror-state@1.4.4: + resolution: {integrity: sha512-6jiYHH2CIGbCfnxdHbXZ12gySFY/fz/ulZE333G6bPqIZ4F+TXo9ifiR86nAHpWnfoNjOb3o5ESi7J8Uz1jXHw==} + + prosemirror-transform@1.10.4: + resolution: {integrity: sha512-pwDy22nAnGqNR1feOQKHxoFkkUtepoFAd3r2hbEDsnf4wp57kKA36hXsB3njA9FtONBEwSDnDeCiJe+ItD+ykw==} + + prosemirror-view@1.41.3: + resolution: {integrity: sha512-SqMiYMUQNNBP9kfPhLO8WXEk/fon47vc52FQsUiJzTBuyjKgEcoAwMyF04eQ4WZ2ArMn7+ReypYL60aKngbACQ==} + prr@1.0.1: resolution: {integrity: sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==} @@ -3991,6 +4032,9 @@ packages: resolution: {integrity: sha512-a2S4Bh3bgrdO4BhKr2E4nZkjTvrJ2m2bWjMTzVYtoqSCn0HnuxosXnaJUHrMEziOWr3CzL9GjilQQKcyCQpJoA==} hasBin: true + rope-sequence@1.3.4: + resolution: {integrity: sha512-UT5EDe2cu2E/6O4igUr5PSFs23nvvukicWHx6GnOPlHAiiYbzNuCRQCuiUdHJQcqKalLKlrYJnjY0ySGsXNQXQ==} + run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} @@ -5993,6 +6037,22 @@ snapshots: transitivePeerDependencies: - supports-color + '@toast-ui/editor@3.2.2': + dependencies: + dompurify: 2.5.8 + prosemirror-commands: 1.7.1 + prosemirror-history: 1.4.1 + prosemirror-inputrules: 1.5.1 + prosemirror-keymap: 1.2.3 + prosemirror-model: 1.25.4 + prosemirror-state: 1.4.4 + prosemirror-view: 1.41.3 + + '@toast-ui/react-editor@3.2.3(react@17.0.2)': + dependencies: + '@toast-ui/editor': 3.2.2 + react: 17.0.2 + '@turf/along@6.5.0': dependencies: '@turf/bearing': 6.5.0 @@ -7956,6 +8016,8 @@ snapshots: domelementtype@2.3.0: {} + dompurify@2.5.8: {} + domutils@1.7.0: dependencies: dom-serializer: 0.2.2 @@ -9253,6 +9315,8 @@ snapshots: type-check: 0.4.0 word-wrap: 1.2.5 + orderedmap@2.1.1: {} + os-browserify@0.3.0: {} own-keys@1.0.1: @@ -9414,6 +9478,49 @@ snapshots: object-assign: 4.1.1 react-is: 16.13.1 + prosemirror-commands@1.7.1: + dependencies: + prosemirror-model: 1.25.4 + prosemirror-state: 1.4.4 + prosemirror-transform: 1.10.4 + + prosemirror-history@1.4.1: + dependencies: + prosemirror-state: 1.4.4 + prosemirror-transform: 1.10.4 + prosemirror-view: 1.41.3 + rope-sequence: 1.3.4 + + prosemirror-inputrules@1.5.1: + dependencies: + prosemirror-state: 1.4.4 + prosemirror-transform: 1.10.4 + + prosemirror-keymap@1.2.3: + dependencies: + prosemirror-state: 1.4.4 + w3c-keyname: 2.2.8 + + prosemirror-model@1.25.4: + dependencies: + orderedmap: 2.1.1 + + prosemirror-state@1.4.4: + dependencies: + prosemirror-model: 1.25.4 + prosemirror-transform: 1.10.4 + prosemirror-view: 1.41.3 + + prosemirror-transform@1.10.4: + dependencies: + prosemirror-model: 1.25.4 + + prosemirror-view@1.41.3: + dependencies: + prosemirror-model: 1.25.4 + prosemirror-state: 1.4.4 + prosemirror-transform: 1.10.4 + prr@1.0.1: optional: true @@ -9720,6 +9827,8 @@ snapshots: minimist: 1.2.8 source-map-support: 0.3.3 + rope-sequence@1.3.4: {} + run-parallel@1.2.0: dependencies: queue-microtask: 1.2.3 From 94ba3bae4a16f827313969fbf89e50a822e984cd Mon Sep 17 00:00:00 2001 From: ZLY Date: Fri, 7 Nov 2025 17:34:11 +0800 Subject: [PATCH 05/41] =?UTF-8?q?feat(api):=20=E6=B7=BB=E5=8A=A0=E7=BB=84?= =?UTF-8?q?=E4=BB=B6=E5=88=86=E7=B1=BB=E5=92=8C=E6=96=87=E4=BB=B6=E4=B8=8A?= =?UTF-8?q?=E4=BC=A0=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/componentClassify.ts | 9 +++++++++ src/api/fileSystem.ts | 9 +++++++++ 2 files changed, 18 insertions(+) create mode 100644 src/api/componentClassify.ts create mode 100644 src/api/fileSystem.ts diff --git a/src/api/componentClassify.ts b/src/api/componentClassify.ts new file mode 100644 index 0000000..f71aee8 --- /dev/null +++ b/src/api/componentClassify.ts @@ -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}`); +} \ No newline at end of file diff --git a/src/api/fileSystem.ts b/src/api/fileSystem.ts new file mode 100644 index 0000000..8c56000 --- /dev/null +++ b/src/api/fileSystem.ts @@ -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); +} \ No newline at end of file From cd9fce74b51a43622a8ed2c23fb0de9fb1bbe777 Mon Sep 17 00:00:00 2001 From: ZLY Date: Fri, 7 Nov 2025 17:34:23 +0800 Subject: [PATCH 06/41] =?UTF-8?q?feat(editor):=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E6=9C=8D=E5=8A=A1=E7=AB=AF=E6=B8=B2=E6=9F=93?= =?UTF-8?q?=E7=9A=84=E7=BC=96=E8=BE=91=E5=99=A8=E7=BB=84=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/EditorSection/index.tsx | 69 ++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 src/components/EditorSection/index.tsx diff --git a/src/components/EditorSection/index.tsx b/src/components/EditorSection/index.tsx new file mode 100644 index 0000000..a465c7b --- /dev/null +++ b/src/components/EditorSection/index.tsx @@ -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(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
; +}; + +export default function EditorSection({ initialContent }: EditorSectionProps) { + const [isClient, setIsClient] = useState(false); + const editorRef = useRef(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 ( +
+ +
+ ); + } + + const DynamicEditor = editorRef.current; + + return ( +
+ +
+ ); +} \ No newline at end of file From c986114e750f7b90e097fa25e8c4cfff5517543f Mon Sep 17 00:00:00 2001 From: ZLY Date: Fri, 7 Nov 2025 17:34:41 +0800 Subject: [PATCH 07/41] =?UTF-8?q?feat(component):=20=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E7=BB=84=E4=BB=B6=E8=A1=A8=E5=8D=95=E4=B8=8E=E6=8F=90=E4=BA=A4?= =?UTF-8?q?=E5=8A=9F=E8=83=BD(=E6=8E=A5=E5=8F=A3=E8=AE=BE=E8=AE=A1?= =?UTF-8?q?=E6=9C=AA=E5=AE=8C=E6=88=90)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/componentBase.ts | 12 + .../componentList/addComponentModal.tsx | 331 ++++++++++++++++-- .../componentList/index.tsx | 11 +- .../style/addComponentModal.module.less | 12 + 4 files changed, 325 insertions(+), 41 deletions(-) diff --git a/src/api/componentBase.ts b/src/api/componentBase.ts index 1710f92..ea7ad53 100644 --- a/src/api/componentBase.ts +++ b/src/api/componentBase.ts @@ -6,4 +6,16 @@ const urlPrefix = '/api/v1/bpms-workbench'; // 我的组件 export function getMyComponentList(params) { return axios.get(`${urlPrefix}/componentBase/list`, { 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); } \ No newline at end of file diff --git a/src/pages/componentDevelopment/componentList/addComponentModal.tsx b/src/pages/componentDevelopment/componentList/addComponentModal.tsx index 7bb2a94..4ddb90f 100644 --- a/src/pages/componentDevelopment/componentList/addComponentModal.tsx +++ b/src/pages/componentDevelopment/componentList/addComponentModal.tsx @@ -1,9 +1,29 @@ -import React, { useState } from 'react'; -import { Modal, Form, Input, Grid, Space, Divider, Button, Table, TableColumnProps } from '@arco-design/web-react'; +import React, { useEffect, useState } from 'react'; +import { + Modal, + Form, + Input, + Grid, + Space, + Divider, + Button, + Table, + TableColumnProps, + Select, + Message, + Upload, + Progress +} from '@arco-design/web-react'; +import { IconPlus, IconEdit } from '@arco-design/web-react/icon'; import styles from './style/addComponentModal.module.less'; import Cover from '@/pages/scene/cover'; +import EditorSection from '@/components/EditorSection'; +import { getComponentClassify } from '@/api/componentClassify'; +import { compProjectValidate, compSubmit, getTagList } from '@/api/componentBase'; const FormItem = Form.Item; +const Option = Select.Option; + const columns: TableColumnProps[] = [ { @@ -34,32 +54,177 @@ const data = [ ]; -const AddComponentModal = ({ visible, setVisible }) => { +const AddComponentModal = ({ visible, setVisible, onReFresh }) => { const [selectedImage, setSelectedImage] = useState(''); + const [description, setDescription] = useState(''); + const [classifyList, setClassifyList] = useState([]); + const [showSaveBtn, setShowSaveBtn] = useState(false); + const [tagsList, setTagsList] = useState([]); + const [componentData, setComponentData] = useState({}); + const [file, setFile] = useState(null); const [form] = Form.useForm(); - const transformImageUrl = (image: string) => { - if (!image) return; - if (image.includes('/')) setSelectedImage(image.split('/')[image.split('/').length - 1]); - else setSelectedImage(image); + const cs = `arco-upload-list-item${file && file.status === 'error' ? ' is-error' : ''}`; + + + const getComponentClassifyList = async () => { + const res: any = await getComponentClassify('component'); + if (res.code === 200) { + const data = []; + res.data.forEach((item) => { + data.push({ + label: item.classifyName, + value: item.id + }); + }); + setClassifyList(data); + } + }; + + const getTageList = async () => { + const res: any = await getTagList(); + if (res.code === 200) setTagsList(res.data); + }; + + const validateProjectId = async (projectId: string) => { + if (!projectId) return; + + try { + const res = await compProjectValidate(projectId); + if (res.data === false) { + // 项目标识已存在,设置表单字段错误 + form.setFields({ + projectId: { + value: projectId, + error: { + message: '项目标识已存在' + } + } + }); + } + else { + // 项目标识可用,清除错误 + form.setFields({ + projectId: { + value: projectId, + error: null + } + }); + } + } catch (error) { + // API调用出错 + form.setFields({ + projectId: { + value: projectId, + error: { + message: '验证项目标识时发生错误' + } + } + }); + } + }; + + const onSubmit = async () => { + try { + await form.validate(); + const formData = form.getFields(); + const params = { + name: formData.name, + projectId: formData.projectId, + logoUrl: selectedImage, + desc: description, + componentClassify: formData.componentClassify, + codeLanguage: formData.codeLanguage === 'Java:8' ? 'Java' : 'Python', + type: formData.type === '普通组件' ? 'normal' : 'loop', + tags: formData.tags + }; + const res: any = await compSubmit(params); + if (res.code === 200) { + setComponentData(res.data); + setShowSaveBtn(true); + Message.success('组件信息提交完成,可继续组件接口设计'); + onReFresh(); + } + else { + Message.error('组件信息提交失败'); + } + } catch (error) { + + } }; + useEffect(() => { + getComponentClassifyList(); + getTageList(); + }, []); + const modalFooter = () => { return ( - - - + + {showSaveBtn && + } + {!showSaveBtn && } ); }; + + const UploadImage = () => { + return ( + { + setFile({ + ...currentFile, + url: URL.createObjectURL(currentFile.originFile) + }); + if (currentFile.status === 'done') setSelectedImage(currentFile.response.data.link); + }} + onProgress={(currentFile) => { + setFile(currentFile); + }} + > +
+ {file && file.url ? ( +
+ +
+ +
+ {file.status === 'uploading' && file.percent < 100 && ( + + )} +
+ ) : ( +
+
+ +
+
+ )} +
+
+ ); + }; + return ( setVisible(false)} - onCancel={() => setVisible(false)} autoFocus={false} focusLock={true} style={{ width: '60%' }} @@ -68,12 +233,8 @@ const AddComponentModal = ({ visible, setVisible }) => {
- - transformImageUrl(selectedImage)} - /> + +
{ border: '1px solid #dcdfe6', borderRadius: 5 }} - >组件外壳
@@ -91,53 +251,152 @@ const AddComponentModal = ({ visible, setVisible }) => {
- + - - + + validateProjectId(e)} + /> - - + + - - + + - - + + - - + + - - 富文本编辑器 - + +
+
描述:
+ +
-
+ + {showSaveBtn &&

组件接口

- + }> + + +
- + } ); diff --git a/src/pages/componentDevelopment/componentList/index.tsx b/src/pages/componentDevelopment/componentList/index.tsx index 520b443..88dc77b 100644 --- a/src/pages/componentDevelopment/componentList/index.tsx +++ b/src/pages/componentDevelopment/componentList/index.tsx @@ -93,12 +93,12 @@ const GlobalVarContainer = () => { pageSize: pagination.pageSize }); - setComponentData(res.list); + setComponentData(res.data.list); setPagination({ - totalCount: res.totalCount, - pageSize: res.pageSize, - totalPage: res.totalPage, - currPage: res.currPage + totalCount: res.data.totalCount, + pageSize: res.data.pageSize, + totalPage: res.data.totalPage, + currPage: res.data.currPage }); } catch (error) { console.error('获取组件列表失败:', error); @@ -201,6 +201,7 @@ const GlobalVarContainer = () => { ); diff --git a/src/pages/componentDevelopment/componentList/style/addComponentModal.module.less b/src/pages/componentDevelopment/componentList/style/addComponentModal.module.less index 4ef4d57..a884c5c 100644 --- a/src/pages/componentDevelopment/componentList/style/addComponentModal.module.less +++ b/src/pages/componentDevelopment/componentList/style/addComponentModal.module.less @@ -31,6 +31,18 @@ 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; + } } } From 2d3bb0f7f38f4e7e4c6e5293c4f7eb2842c3707e Mon Sep 17 00:00:00 2001 From: ZLY Date: Mon, 10 Nov 2025 16:11:11 +0800 Subject: [PATCH 08/41] =?UTF-8?q?feat(component):=20=E5=AE=9E=E7=8E=B0?= =?UTF-8?q?=E7=BB=84=E4=BB=B6=E5=BC=80=E5=8F=91=E6=B5=81=E7=A8=8B=E7=9A=84?= =?UTF-8?q?=E6=96=B0=E5=BB=BA=E7=BB=84=E4=BB=B6=EF=BC=8C=E6=8E=A5=E5=8F=A3?= =?UTF-8?q?=E8=AE=BE=E8=AE=A1=EF=BC=8C=E7=BB=84=E4=BB=B6=E7=BC=96=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增组件接口设计功能,支持新增和编辑接口 - 实现组件基本信息的增删改查功能 - 添加组件状态、发布状态、公开状态的管理 - 支持组件删除功能,增加确认提示 - 实现组件编辑时的数据回显和表单重置 - 新增API接口设计弹窗,支持参数配置 - 添加数据类型字典和组件类型映射 - 实现组件列表的分页和筛选功能 - 增加工具函数:对象类型判断和深拷贝方法 --- src/api/componentBase.ts | 6 +- src/api/componentDevelopProcess.ts | 14 + src/const/globalOption.ts | 13 + src/const/isdp/componentBase.ts | 541 +++++++++++++++++ src/const/isdp/componentDeploy.ts | 562 ++++++++++++++++++ src/const/isdp/componentRelease.ts | 179 ++++++ src/const/isdp/componentTest.ts | 176 ++++++ src/const/isdp/envManagement.ts | 153 +++++ src/const/isdp/mod/AddPropList.ts | 113 ++++ src/const/isdp/mod/componentAudit.ts | 72 +++ src/const/isdp/mod/componentCollaboration.ts | 59 ++ src/const/isdp/repository.ts | 53 ++ .../componentList/addApiModal.tsx | 115 ++++ .../componentList/addComponentModal.tsx | 216 +++++-- .../componentList/editableTable.tsx | 197 ++++++ .../componentList/handleButtonGroup.tsx | 184 ++++++ .../componentList/index.tsx | 243 ++++++-- src/utils/common.ts | 45 ++ 18 files changed, 2830 insertions(+), 111 deletions(-) create mode 100644 src/api/componentDevelopProcess.ts create mode 100644 src/const/globalOption.ts create mode 100644 src/const/isdp/componentBase.ts create mode 100644 src/const/isdp/componentDeploy.ts create mode 100644 src/const/isdp/componentRelease.ts create mode 100644 src/const/isdp/componentTest.ts create mode 100644 src/const/isdp/envManagement.ts create mode 100644 src/const/isdp/mod/AddPropList.ts create mode 100644 src/const/isdp/mod/componentAudit.ts create mode 100644 src/const/isdp/mod/componentCollaboration.ts create mode 100644 src/const/isdp/repository.ts create mode 100644 src/pages/componentDevelopment/componentList/addApiModal.tsx create mode 100644 src/pages/componentDevelopment/componentList/editableTable.tsx create mode 100644 src/pages/componentDevelopment/componentList/handleButtonGroup.tsx diff --git a/src/api/componentBase.ts b/src/api/componentBase.ts index ea7ad53..02d7281 100644 --- a/src/api/componentBase.ts +++ b/src/api/componentBase.ts @@ -18,4 +18,8 @@ export function compProjectValidate(projectId) { export function compSubmit(params) { return axios.post(`${urlPrefix}/componentBase/submit`, params); -} \ No newline at end of file +} + +export const remove = (ids) => { + return axios.post(`${urlPrefix}/componentBase/remove?ids=${ids}`); +}; \ No newline at end of file diff --git a/src/api/componentDevelopProcess.ts b/src/api/componentDevelopProcess.ts new file mode 100644 index 0000000..9b888ac --- /dev/null +++ b/src/api/componentDevelopProcess.ts @@ -0,0 +1,14 @@ +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); +} \ No newline at end of file diff --git a/src/const/globalOption.ts b/src/const/globalOption.ts new file mode 100644 index 0000000..750ead1 --- /dev/null +++ b/src/const/globalOption.ts @@ -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: '不能输入空格' }; diff --git a/src/const/isdp/componentBase.ts b/src/const/isdp/componentBase.ts new file mode 100644 index 0000000..41ddb72 --- /dev/null +++ b/src/const/isdp/componentBase.ts @@ -0,0 +1,541 @@ +import { globalOption, patternTrim } from '../globalOption'; + +// 组件公开状态码 +export const publishStatusConstant = { + // 未公开 + UNPUBLISHED: -1, + // 已公开 + PUBLISHED: 1, + // 已撤销 + REVOKED: 0, + // 审批中 + APPROVAL: 2 +}; +export const publishStatusDict = [ + { + label: '未公开', + value: publishStatusConstant.UNPUBLISHED + }, + { + label: '未公开', + value: publishStatusConstant.REVOKED + }, + { + label: '已公开', + value: publishStatusConstant.PUBLISHED + }, + { + label: '审批中', + value: publishStatusConstant.APPROVAL + } +]; +// 组价发布的状态码 +export const publicStatus = { + // 未发布 + UNPUBLISHED: -1, + UNPUBLISHED1: 0, + // 已发布 + PUBLISHED: 1, + // 审核中 + REVIEW: 2, + // 审核被拒 + REJECTED: 3 +}; +export const publicStatusDict = [ + { + label: '未发布', + value: publicStatus.UNPUBLISHED + }, + { + label: '未发布', + value: publicStatus.UNPUBLISHED1 + }, + { + label: '已发布', + value: publicStatus.PUBLISHED + }, + { + label: '审核中', + value: publicStatus.REVIEW + }, + { + label: '审核被拒', + value: publicStatus.REJECTED + } +]; +// 组件基本信息状态字段 +export const componentStatusConstant = { + // 未设计 + DEFAULT: 'default', + // 设计中 + DESIGN: 'design', + // 编码中 + CODING: 'coding', + // 已部署 + DEPLOYED: 'deployed', + // 已公开 + PUBLISHED: 'published' +}; +// 组件所处阶段状态 +export const componentStatusDict = [ + { + label: '未设计', + value: componentStatusConstant.DEFAULT + }, + { + label: '设计中', + value: componentStatusConstant.DESIGN + }, + { + label: '编码中', + value: componentStatusConstant.CODING + }, + { + label: '已部署', + value: componentStatusConstant.DEPLOYED + }, + { + label: '已公开', + value: componentStatusConstant.PUBLISHED + } +]; +// 组件所处阶段状态 +export const componentStatusMap = componentStatusDict.reduce((obj, item) => { + obj[item.value] = item.label; + return obj; +}, {}); +// 组件类型字典映射 +export const componentTypeDict = [ + { + dictKey: '普通组件', + dictValue: 'normal' + }, + { + dictKey: '监听组件', + dictValue: 'loop' + } +]; + +// 表格参数 +export const option = { + ...globalOption, + height: 'auto', + calcHeight: 30, + tip: false, + searchShow: true, + searchMenuSpan: 6, + viewBtn: true, + delBtn: false, + selection: false, + dialogClickModal: false, + labelWidth: '100', + menuWidth: 500, + menuHeaderAlign: 'center', + header: true, + addBtn: true, + editBtn: false, + updateBtnText: '保 存', + addBtnText: '新增组件', + addBtnIcon: 'none', + column: [ + { + label: '组件名称', + prop: 'name', + type: 'input', + width: 150, + placeholder: '请输入组件名称', + rules: [ + { + required: true, + message: '组件名称不能为空', + trigger: 'blur' + }, + { + pattern: /^[\u4e00-\u9fa5a-zA-Z0-9_-]+$/, + message: '组件名称只能为中文、字母、数字、"-"、"_"组成' + }, + patternTrim, + { max: 50, message: '组件名称最长为50字符' } + ] + }, + { + label: '组件标识', + prop: 'projectId', + type: 'input', + width: 200, + overHidden: true, + placeholder: '请输入组件标识', + editDisabled: true, + rules: [ + { required: true, message: '组件标识不能为空' }, + { + pattern: /^([a-z][a-z0-9]*[_-]?)+$/, + message: '组件标识由小写字母、数字、"-"、"_"组成,以小写字母开头,且“-”、"_"后第一个字符需为小写字母' + }, + { max: 30, message: '组件标识最长为30个字符' }, + patternTrim + ] + }, + { + label: '分类', + prop: 'componentClassify', + type: 'select', + placeholder: '请选择分类', + width: 200, + dicUrl: '/api/blade-system/dict-biz/dictionary-tree?code=component_classify', + props: { + label: 'dictValue', + value: 'dictKey' + }, + rules: [{ required: true, message: '分类不能为空' }] + }, + { + label: '创建人', + prop: 'createUserName', + type: 'input', + display: false, + overHidden: true, + editDisabled: false, + width: 180, + hide: false + }, + { + label: '代码语言', + prop: 'codeLanguage', + type: 'select', + dicUrl: '/api/blade-system/dict/dictionary?code=language_type', + value: 'Java', + editDisabled: true, + width: 150, + placeholder: '请选择代码语言', + props: { + label: 'dictValue', + value: 'dictKey' + }, + rules: [{ required: true, message: '代码语言不能为空' }] + }, + { + label: '标签', + prop: 'tags', + type: 'select', + remote: true, + multiple: true, + allowCreate: true, + filterable: true, + placeholder: '请选择标签', + width: 200, + dicUrl: '/api/blade-isdp/componentBase/tags', + hide: true + }, + { + label: '组件类型', + prop: 'type', + type: 'select', + dicData: componentTypeDict, + value: 'normal', + editDisabled: true, + width: 150, + placeholder: '请选择组件类型', + props: { + label: 'dictKey', + value: 'dictValue' + }, + rules: [{ required: true, message: '组件类型不能为空' }] + }, + { + label: '图标', + prop: 'logoUrl', + type: 'upload', + hide: true, + listType: 'picture-card', + action: '/api/blade-isdp/fileSystem/fileUpload', + propsHttp: { + res: 'data', + url: 'link' + }, + fileType: 'img', + limit: 1, + fileSize: 1024, + accept: 'image/png, image/jpeg, image/jpg', + tip: '图片尺寸不超过300*300像素,大小限制为1mb以内' + }, + { + label: '描述', + prop: 'desc', + type: 'textarea', + overHidden: true, + placeholder: '请输入描述', + formSlot: true, + hide: true, + minRows: 2, + maxLen: 100, + span: 24 + }, + { + label: '组件状态', + prop: 'componentStatus', + type: 'input', + display: false, + width: 100, + dicData: componentStatusDict + }, + { + label: '创建部门', + prop: 'createDept', + type: 'input', + display: false, + hide: true + }, + { + label: '创建时间', + prop: 'createTime', + type: 'input', + hide: true, + display: false + }, + { + label: '发布状态', + prop: 'publicStatus', + type: 'input', + width: 90, + dicData: [ + { + label: '未发布', + value: publicStatus.UNPUBLISHED + }, + { + label: '未发布', + value: publicStatus.UNPUBLISHED1 + }, + { + label: '已发布', + value: publicStatus.PUBLISHED + }, + { + label: '审核中', + value: publicStatus.REVIEW + }, + { + label: '审核被拒', + value: publicStatus.REJECTED + } + ], + display: false + }, + { + label: '公开状态', + prop: 'publishStatus', + type: 'input', + width: 90, + dicData: [ + { + label: '未公开', + value: publishStatusConstant.UNPUBLISHED + }, + { + label: '未公开', + value: publishStatusConstant.REVOKED + }, + { + label: '已公开', + value: publishStatusConstant.PUBLISHED + }, + { + label: '审批中', + value: publishStatusConstant.APPROVAL + } + ], + display: false + }, + { + label: '修改人', + prop: 'updateUser', + type: 'input', + display: false, + hide: true + }, + { + label: '修改时间', + prop: 'updateTime', + type: 'input', + width: 130, + display: false + }, + { + label: '是否已删除', + prop: 'isDeleted', + type: 'input', + display: false, + hide: true + } + ] +}; + +export const operateOption = { + ...globalOption, + height: '400', + calcHeight: 30, + tip: false, + searchShow: false, + selection: false, + dialogClickModal: false, + header: false, + delBtn: true, + viewBtn: false, + dialogWidth: '55%', + menuWidth: 100, + saveBtn: false, + cancelBtn: false, + editBtn: true, + editBtnIcon: 'none', + delBtnIcon: 'none', + column: [ + { + label: '接口名称', + prop: 'ident', + type: 'input', + placeholder: '请输入名称', + rules: [ + { required: true, message: '名称不能为空' }, + { pattern: /^[a-z][a-zA-Z0-9]*$/, message: '名称符需要为首字母小写的驼峰命名法' }, + { max: 20, message: '名称最长为20个字符' }, + patternTrim + ] + }, + { + label: '描述', + prop: 'desc', + type: 'textarea', + placeholder: '请输入描述', + hide: true, + overHidden: true, + minRows: 2, + rules: [{ max: 200, message: '最长为200个字符' }, patternTrim], + span: 24 + }, + { + label: '输入参数', + prop: 'parameters', + type: 'input', + span: 24, + formSlot: true, + overHidden: true, + value: [], + slot: true + }, + { + label: '输出参数', + prop: 'responses', + type: 'input', + span: 24, + formSlot: true, + overHidden: true, + value: [], + slot: true + } + ] +}; + +export const baseConfigOption = { + ...globalOption, + addBtn: false, + addRowBtn: true, + menu: true, + cellBtn: false, + editBtn: false, + delBtn: false, + dialogWidth: '40%', + menuWidth: '80', + column: [ + { + label: '启动项名称', + prop: 'name', + placeholder: '启动项名称', + width: '150px', + rules: [{ required: true, message: '启动项名称不能为空' }, { + max: 50, + message: '启动项名称最长为50字符' + }, patternTrim], + cell: true, + span: 24 + }, + { + label: '参数名(key)', + prop: 'key', + input: 'input', + width: '200px', + rules: [{ required: true, message: '请输入参数名(key)' }, { + max: 50, + message: '参数名(key)最长为50字符' + }, patternTrim], + cell: true, + span: 24 + }, + { + label: '默认值', + prop: 'value', + width: '200px', + span: 24, + cell: true, + rules: [{ max: 200, message: '默认值最长为200字符' }, patternTrim] + }, + { + label: '描述说明', + prop: 'desc', + overHidden: true, + span: 24, + cell: true, + rules: [{ max: 200, message: '描述说明最长为200字符' }, patternTrim] + } + ] +}; + +export const baseDisplayToEntity = (row) => { + row.desc = row.desc || ''; + row.tags = row.tags || []; + row.logoUrl = row.logoUrl || ''; + return row; +}; + +export const baseEntityToDisplay = (row) => { + row.desc = row.desc || ''; + row.logoUrl = row.logoUrl || ''; + row.tags = row.tags || []; + return row; +}; + +export const toolbars = { + bold: true, // 粗体 + italic: true, // 斜体 + header: true, // 标题 + underline: true, // 下划线 + strikethrough: true, // 中划线 + mark: true, // 标记 + superscript: true, // 上角标 + subscript: true, // 下角标 + quote: true, // 引用 + ol: true, // 有序列表 + ul: true, // 无序列表 + link: true, // 链接 + imagelink: true, // 图片链接 + code: true, // code + table: true, // 表格 + fullscreen: true, // 全屏编辑 + readmodel: true, // 沉浸式阅读 + htmlcode: true, // 展示html源码 + help: false, // 帮助 + /* 1.3.5 */ + undo: true, // 上一步 + redo: true, // 下一步 + trash: true, // 清空 + save: false, // 保存(触发events中的save事件) + /* 1.4.2 */ + navigation: true, // 导航目录 + /* 2.1.8 */ + alignleft: true, // 左对齐 + aligncenter: true, // 居中 + alignright: true, // 右对齐 + /* 2.2.1 */ + subfield: true, // 单双栏模式 + preview: true // 预览 +}; diff --git a/src/const/isdp/componentDeploy.ts b/src/const/isdp/componentDeploy.ts new file mode 100644 index 0000000..1a455a9 --- /dev/null +++ b/src/const/isdp/componentDeploy.ts @@ -0,0 +1,562 @@ +import { globalOption, patternTrim } from '../globalOption'; + +/** + * 实例运行状态 + */ +export const runStatusConstant = { + HEALTHY: 'healthy', + UNKNOWN: 'unknown', + RUN: 'run', + ERROR: 'error', + STOP: 'stop', +}; +export const runStatusDic = [ + { + label: '可用', + value: runStatusConstant.HEALTHY, + }, + { + label: '未知', + value: runStatusConstant.UNKNOWN, + }, + { + // 部署 可停止 + label: '运行', + value: runStatusConstant.RUN, + }, + { + label: '异常', + value: runStatusConstant.ERROR, + }, + { + // 部署 可启用 + label: '停止', + value: runStatusConstant.STOP, + }, +]; +export const runStatusMap = runStatusDic.reduce((obj, item) => { + obj[item.value] = item.label; + return obj; +}, {}); +/** + * 组件运行状态 + */ +export const startStatusConstant = { + RUN: 'run', + STOP: 'stop', +}; +export const startStatusDic = [ + { + label: '启用中', + value: startStatusConstant.RUN, + }, + { + label: '已下架', + value: startStatusConstant.STOP, + }, +]; +export const startStatusMap = startStatusDic.reduce((obj, item) => { + obj[item.value] = item.label; + return obj; +}, {}); + +/** + * 组件编译状态 + * BUILT_DOING, BUILT_IGNORE,BUILT_DOCKER,BUILT_FAIL + */ +export const compileStatusConstant = { + BUILT_DOING: 'built-doing', + BUILT_IGNORE: 'built-ignore', + BUILT_DOCKER: 'built-docker', + BUILT_FAIL: 'built-fail', + // 前端单独使用 + NOT_COMPILED: 'not-compiled', + get(type) { + return compileStatusConstant[type] || compileStatusConstant.NOT_COMPILED; + }, +}; +export const compileStatusDic = [ + { + label: '编译中', + value: compileStatusConstant.BUILT_DOING, + }, + { + label: '免编译', + value: compileStatusConstant.BUILT_IGNORE, + }, + { + label: '已编译', + value: compileStatusConstant.BUILT_DOCKER, + }, + { + label: '编译失败', + value: compileStatusConstant.BUILT_FAIL, + }, +]; +export const compileStatusMap = startStatusDic.reduce((obj, item) => { + obj[item.value] = item.label; + return obj; +}, {}); +/** + * 实例运行类型 + */ +export const runTypeConstant = { + LOCAL: 0, + ONLINE: 1, +}; +export const runTypeDic = [ + { + label: '本地运行', + value: runTypeConstant.LOCAL, + }, + { + label: '线上运行', + value: runTypeConstant.ONLINE, + }, +]; +export const option = { + ...globalOption, + height: '300', + calcHeight: 30, + tip: false, + searchShow: false, + header: false, + viewBtn: false, + delBtn: false, + index: true, + menuWidth: 300, + addBtn: false, + editBtn: false, + submitText: '修改', + emptyText: '暂无数据', + span: 24, + column: [ + { + label: 'id', + prop: 'id', + hide: true, + display: false, + }, + { + label: '标识', + prop: 'identifier', + type: 'input', + overHidden: true, + display: false, + }, + { + label: '实例名称', + prop: 'name', + type: 'input', + overHidden: true, + rules: [ + { required: true, message: '必须填写实例名称' }, + { max: 50, message: '实例名称不能超过50字符' }, + ], + }, + { + label: '运行类型', + prop: 'runType', + type: 'input', + width: 80, + display: false, + dicData: runTypeDic, + }, + { + label: '运行状态', + prop: 'runStatus', + type: 'input', + width: 80, + dicData: runStatusDic, + display: false, + slot: true, + }, + { + label: '创建时间', + prop: 'createTime', + type: 'input', + // width: 160, + display: false, + }, + { + label: '实例描述', + prop: 'description', + type: 'textarea', + hide: true, + minRows: 3, + span: 24, + }, + ], +}; +export const instanceOption = { + ...globalOption, + calcHeight: 'auto', + // height: '300', + // calcHeight: 30, + tip: false, + searchShow: false, + header: false, + viewBtn: false, + delBtn: false, + index: true, + menuWidth: 350, + addBtn: false, + editBtn: false, + submitText: '修改', + emptyText: '取消', + span: 24, + column: [ + ...option.column, + { + label: '图标', + prop: 'logoUrl', + type: 'upload', + hide: true, + listType: 'picture-img', + action: '/api/blade-isdp/fileSystem/fileUpload', + propsHttp: { + res: 'data', + url: 'link', + }, + fileType: 'img', + limit: 1, + fileSize: 1024 * 10, + accept: 'image/png, image/jpeg, image/jpg', + // tip: '图片尺寸不超过300*300像素,大小限制为1mb以内', + // rules: [{ + // required: true, + // message: "请输入图片地址", + // trigger: "blur" + // }] + }, + ], +}; +export const envConfigOption = { + ...globalOption, + height: 300, + calcHeight: 30, + tip: false, + searchShow: false, + addBtn: false, + addRowBtn: false, + cellBtn: false, + index: true, + menuWidth: 80, + editBtn: false, + delBtn: false, + column: [ + { + label: '启动项名称', + prop: 'name', + placeholder: '启动项名称', + formSlot: true, + rules: [ + { required: true, message: '启动项名称不能为空' }, + { max: 50, message: '启动项名称最长为50字符' }, + ], + width: 120, + span: 24, + cell: true, + }, + { + label: '参数名(key)', + prop: 'key', + formSlot: true, + rules: [ + { required: true, message: '请输入参数名(key)' }, + { max: 50, message: '参数名(key)最长为50字符' }, + ], + span: 24, + cell: true, + }, + { + label: '配置值', + prop: 'value', + span: 24, + formSlot: true, + rules: [{ max: 200, message: '配置值最长为200字符' }], + cell: true, + }, + { + label: '描述说明', + prop: 'desc', + type: 'input', + formSlot: true, + span: 24, + rules: [{ max: 200, message: '描述说明最长为200字符' }], + cell: true, + }, + // { + // label: "配置类型", + // prop: "unmodifiable", + // value: false, + // width: 80, + // dicData: [ + // { + // label: "自定义配置", + // value: false + // }, + // { + // label: "必须配置", + // value: true + // } + // ] + // }, + ], +}; +export const addInstanceOption = { + ...globalOption, + submitBtn: false, + emptyBtn: false, + column: [ + { + label: '部署环境', + prop: 'environment', + type: 'select', + value: 'docker-env-prod', + // disabled: true, + dicData: [], + rules: [{ required: true, message: '请选择环境变量' }], + overHidden: true, + }, + { + label: '主机', + prop: 'deployEnvId', + type: 'select', + value: '', + props: { + label: 'name', + value: 'id', + desc: 'ip', + }, + rules: [{ required: true, message: '请输入主机' }], + overHidden: true, + }, + { + label: 'CPU核数', + prop: 'cpuCount', + type: 'slider', + step: 1, + min: 1, + max: 12, + showStops: true, + marks: [1, 4, 8].reduce((obj, pre) => { + obj[pre] = pre + '核'; + return obj; + }, {}), + }, + // { + // label: "容器名称", + // prop: "containerName", + // tip: "为空时自动生成", + // rules: [ + // { min: 1, max: 63, message: "容器名称长度为1-63个字符" }, + // patternTrim, + // ], + // }, + { + label: '是否使用GPU', + prop: 'useGpu', + type: 'switch', + labelWidth: 100, + value: false, + dicData: [ + { label: '否', value: false }, + { label: '是', value: true }, + ], + }, + { + label: '最大内存', + prop: 'memory', + type: 'slider', + step: 64, + min: 64, + max: 8192, + formSlot: true, + span: 24, + marks: { + 64: '64MB', + 1024: '1GB', + 2048: '2GB', + 4096: '4GB', + 6144: '6GB', + }, + showStops: true, + }, + // 网络模式 选择框 + { + label: '网络模式', + prop: 'networkType', + type: 'select', + span: 6, + value: 'bridge', + dicData: [ + { + label: '桥接模式', + value: 'bridge', + }, + { + label: '主机模式', + value: 'host', + }, + { + label: '禁用网络', + value: 'null', + }, + ], + rules: [{ required: true, message: '请选择网络模式' }], + overHidden: true, + }, + // 网卡 + { + label: '网卡', + prop: 'networkMode', + type: 'select', + value: 'bridge', + span: 9, + overHidden: true, + rules: [{ required: true, message: '请选择网卡' }], + }, + // ip + { + label: 'IP', + prop: 'ipv4Address', + type: 'input', + formSlot: true, + value: '', + overHidden: true, + span: 9, + }, + { + label: '端口映射', + prop: 'ports', + type: 'dynamic', + span: 24, + children: { + height: 120, + column: [ + { + label: '主机端口', + prop: 'hostPort', + type: 'number', + value: '80', + max: 65535, + rules: [{ required: true, message: '请输入主机端口' }], + }, + { + label: '容器端口', + prop: 'containerPort', + type: 'number', + max: 65535, + rules: [{ required: true, message: '请输入容器端口' }], + }, + { + label: '备注', + prop: 'remake', + type: 'input', + value: '', + }, + ], + }, + }, + { + label: '目录挂载', + prop: 'volumes', + type: 'dynamic', + span: 24, + children: { + height: 170, + column: [ + { + label: '主机路径', + prop: 'hostPath', + type: 'input', + value: '', + rules: [{ required: true, message: '请输入主机路径' }, patternTrim], + }, + { + label: '容器路径', + prop: 'containerPath', + type: 'input', + value: '', + rules: [{ required: true, message: '请输入容器路径' }, patternTrim], + }, + { + label: '备注', + prop: 'remake', + type: 'input', + }, + ], + }, + }, + { + // 设备挂载 + label: '设备挂载', + prop: 'devices', + type: 'dynamic', + span: 24, + children: { + height: 120, + column: [ + { + label: '主机路径', + prop: 'hostPath', + type: 'input', + value: '', + rules: [{ required: true, message: '请输入主机路径' }, patternTrim], + }, + { + label: '容器路径', + prop: 'containerPath', + type: 'input', + value: '', + rules: [{ required: true, message: '请输入容器路径' }, patternTrim], + }, + { + label: '权限', + prop: 'permissions', + type: 'select', + value: 'rwm', + dicData: [ + { value: 'rwm', label: 'rwm' }, + { value: 'rw', label: 'rw' }, + { value: 'r', label: 'r' }, + ], + rules: [{ required: true, message: '请输入权限' }], + }, + { + label: '备注', + prop: 'remake', + type: 'input', + value: '', + }, + ], + }, + }, + // 重启策略 + { + label: '重启策略', + prop: 'restartPolicy', + type: 'select', + span: 6, + display: false, + hide: true, + value: 'always', + dicData: [ + { + label: '始终', + value: 'always', + }, + { + label: '失败', + value: 'on-failure', + }, + { + label: '无', + value: 'no', + }, + ], + overHidden: true, + }, + ], +}; diff --git a/src/const/isdp/componentRelease.ts b/src/const/isdp/componentRelease.ts new file mode 100644 index 0000000..17b9d1c --- /dev/null +++ b/src/const/isdp/componentRelease.ts @@ -0,0 +1,179 @@ +import { globalOption, patternTrim } from '../globalOption'; +import { componentBaseTagsUrl } from '@/api/isdp/componentBase'; + +export const releaseMaping = { + 0: { + label: '未公开', + type: 'info', + }, + '-1': { + label: '未公开', + type: 'info', + }, + 1: { + label: '已公开', + type: 'success', + }, +}; + +export const option = { + ...globalOption, + height: 'auto', + calcHeight: 30, + tip: false, + searchShow: true, + searchSpan: 5, + searchMenuSpan: 4, + menuWidth: 120, + viewBtn: true, + delBtn: false, + selection: false, + header: false, + editBtn: false, + column: [ + { + label: '组件名称', + prop: 'name', + type: 'input', + overHidden: true, + search: true, + placeholder: '请输入组件名称', + rules: [ + { + required: true, + message: '组件名称不能为空', + trigger: 'blur', + }, + patternTrim, + { max: 20, message: '组件名称最长为20字符' }, + ], + }, + { + label: '项目名', + prop: 'identifier', + type: 'input', + overHidden: true, + search: true, + placeholder: '请输入项目名', + rules: [ + { required: true, message: '项目名不能为空' }, + { + pattern: /^([a-z][a-z0-9]*[_-]?)+$/, + message: '项目名由小写字母、数字、"-"、"_"组成,以小写字母开头,且“-”、"_"后第一个字符需为小写字母', + }, + { max: 30, message: '项目名最长为30个字符' }, + patternTrim, + ], + }, + { + label: '版本号', + prop: 'version', + type: 'input', + overHidden: true, + disabled: true, + value: 'v1', + width: 60, + span: 6, + rules: [ + { + required: true, + message: '请输入组件版本号', + trigger: 'blur', + }, + // { + // pattern: /^\d+(\.\d+){0,3}$/, + // message: "版本只能是由数字组成的x.x或x.x.x格式", + // trigger: "blur" + // }, + { max: 20, message: '版本号最长为20字符' }, + ], + }, + { + label: '分类', + prop: 'componentClassify', + type: 'select', + placeholder: '请选择分类', + search: true, + dicUrl: '/api/blade-system/dict-biz/dictionary-tree?code=component_classify', + props: { + label: 'dictValue', + value: 'dictKey', + }, + span: 9, + rules: [{ required: true, message: '分类不能为空' }], + // search: true, + }, + { + label: '代码语言', + prop: 'codeLanguage', + type: 'select', + search: true, + dicUrl: '/api/blade-system/dict/dictionary?code=language_type', + value: 'Java', + width: 70, + span: 9, + placeholder: '请选择代码语言', + props: { + label: 'dictValue', + value: 'dictKey', + }, + rules: [{ required: true, message: '代码语言不能为空' }], + }, + { + label: '标签', + prop: 'tags', + type: 'select', + remote: true, + multiple: true, + allowCreate: true, + filterable: true, + placeholder: '请选择标签', + span: 24, + dicUrl: componentBaseTagsUrl, + hide: true, + }, + { + label: '描述', + prop: 'desc', + type: 'textarea', + overHidden: true, + placeholder: '请输入描述', + minRows: 2, + maxLen: 100, + rules: [{ max: 200, message: '最长为200个字符' }], + span: 24, + }, + { + label: '图标', + prop: 'logoUrl', + type: 'upload', + hide: true, + listType: 'picture-img', + action: '/api/blade-isdp/fileSystem/fileUpload', + propsHttp: { + res: 'data', + url: 'link', + }, + fileType: 'img', + limit: 1, + fileSize: 1024, + accept: 'image/png, image/jpeg, image/jpg', + tip: '图片尺寸不超过300*300像素,大小限制为1mb以内', + }, + { + label: '公开状态', + prop: 'publishStatus', + type: 'input', + slot: true, + display: false, + width: 90, + }, + { + label: '修改时间', + prop: 'updateTime', + type: 'input', + width: 130, + display: false, + }, + ], +}; diff --git a/src/const/isdp/componentTest.ts b/src/const/isdp/componentTest.ts new file mode 100644 index 0000000..b42c986 --- /dev/null +++ b/src/const/isdp/componentTest.ts @@ -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, + }, + ], +}; diff --git a/src/const/isdp/envManagement.ts b/src/const/isdp/envManagement.ts new file mode 100644 index 0000000..c6ab63f --- /dev/null +++ b/src/const/isdp/envManagement.ts @@ -0,0 +1,153 @@ +import { globalOption, patternTrim } from '../globalOption'; + +export const archDict = [ + { + label: 'x86_64', + value: 'x86_64', + }, + { + label: 'aarch64', + value: 'aarch64', + }, +]; + +export const option = { + ...globalOption, + height: 'auto', + calcHeight: 30, + searchShow: true, + searchMenuSpan: 6, + viewBtn: false, + delBtn: false, + selection: false, + header: true, + addBtn: false, + editBtn: false, + updateBtnText: '保 存', + menuHeaderAlign: 'center', + column: [ + { + label: '环境IP', + prop: 'ip', + type: 'input', + overHidden: true, + span: 12, + rules: [ + { + required: true, + message: '环境IP不能为空', + trigger: 'blur', + }, + { + pattern: /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/, + message: '请输入正确的IP地址:范围在0.0.0.0-255.255.255.255之间', + trigger: 'blur', + }, + ], + }, + { + label: 'docker端口', + prop: 'dockerTcpPort', + type: 'number', + labelWidth: 100, + overHidden: true, + value: 2375, + span: 12, + rules: [ + { + required: true, + message: 'docker端口不能为空', + trigger: 'blur', + }, + { + pattern: /^(?:[1-9]\d{0,3}|[1-5]\d{4}|6[0-4]\d{3}|65[0-4]\d{2}|655[0-2]\d|6553[0-5])$/, + message: 'docker端口只能是1-65535之间', + trigger: 'blur', + }, + ], + }, + { + label: '环境类型', + prop: 'type', + type: 'select', + overHidden: true, + search: true, + span: 12, + dicUrl: '/api/blade-system/dict/dictionary-tree?code=docker-env', + props: { + label: 'dictValue', + value: 'dictKey', + }, + rules: [ + { + required: true, + message: '环境类型不能为空', + trigger: 'blur', + }, + ], + }, + { + label: '架构类型', + prop: 'arch', + type: 'select', + overHidden: true, + search: true, + span: 12, + dicData: archDict, + formSlot: true, + props: { + label: 'label', + value: 'value', + }, + rules: [ + { + required: true, + message: '架构类型不能为空', + trigger: 'blur', + }, + ], + }, + { + label: '环境别名', + prop: 'name', + type: 'input', + search: true, + span: 24, + rules: [ + { + required: true, + message: '环境IP不能为空', + trigger: 'blur', + }, + patternTrim, + // 最大50 + { + max: 50, + message: '环境别名最大长度不能超过50', + trigger: 'blur', + }, + ], + }, + { + label: '备注', + prop: 'description', + type: 'textarea', + span: 24, + rules: [ + { + max: 200, + message: '长度不超过200字符', + trigger: 'blur', + }, + ], + overHidden: true, + }, + { + label: '实例数量', + labelWidth: 20, + span: 24, + prop: 'instanceCount', + display: false, + }, + ], +}; diff --git a/src/const/isdp/mod/AddPropList.ts b/src/const/isdp/mod/AddPropList.ts new file mode 100644 index 0000000..663641e --- /dev/null +++ b/src/const/isdp/mod/AddPropList.ts @@ -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', + }, + ], +}; diff --git a/src/const/isdp/mod/componentAudit.ts b/src/const/isdp/mod/componentAudit.ts new file mode 100644 index 0000000..d04fb05 --- /dev/null +++ b/src/const/isdp/mod/componentAudit.ts @@ -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', + }, +]; diff --git a/src/const/isdp/mod/componentCollaboration.ts b/src/const/isdp/mod/componentCollaboration.ts new file mode 100644 index 0000000..8169ed7 --- /dev/null +++ b/src/const/isdp/mod/componentCollaboration.ts @@ -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, +}); diff --git a/src/const/isdp/repository.ts b/src/const/isdp/repository.ts new file mode 100644 index 0000000..f8c3872 --- /dev/null +++ b/src/const/isdp/repository.ts @@ -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, + }, + ], +}; diff --git a/src/pages/componentDevelopment/componentList/addApiModal.tsx b/src/pages/componentDevelopment/componentList/addApiModal.tsx new file mode 100644 index 0000000..e5d935a --- /dev/null +++ b/src/pages/componentDevelopment/componentList/addApiModal.tsx @@ -0,0 +1,115 @@ +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, 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 + }))); + } + + // 设置响应表格数据 + if (componentDesignProgress.responses && Array.isArray(componentDesignProgress.responses)) { + setResponsesData(componentDesignProgress.responses.map((response, index) => ({ + key: response.id || index, + ...response + }))); + } + } + else if (visible) { + // 重置表单和表格数据 + form.resetFields(); + setParametersData([]); + setResponsesData([]); + } + }, [visible, componentDesignProgress, form]); + + const submit = async () => { + try { + await form.validate(); + const formData = form.getFields(); + const params = { + baseInfo + }; + + params['operates'] = { + ...formData, + type: 'EVENT', + parameters: parametersData, + responses: responsesData + }; + const res: any = await updateComponentDesign(params); + + if (res.code === 200) { + Message.success('新增成功'); + } + else { + Message.error(res.message); + } + + // 调用父组件传递的 onOk 回调,并传递数据 + onOk && onOk(params); + } catch (error) { + console.error('表单验证失败:', error); + Message.error('请检查表单填写是否正确'); + } + }; + + return ( + +
+ + + + +