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 diff --git a/public/ideContainer/imgs/git.svg b/public/ideContainer/imgs/git.svg new file mode 100644 index 0000000..5c9e4aa --- /dev/null +++ b/public/ideContainer/imgs/git.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/ideContainer/imgs/tijiao.svg b/public/ideContainer/imgs/tijiao.svg new file mode 100644 index 0000000..b964c45 --- /dev/null +++ b/public/ideContainer/imgs/tijiao.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/ideContainer/imgs/tongbu.svg b/public/ideContainer/imgs/tongbu.svg new file mode 100644 index 0000000..8b34070 --- /dev/null +++ b/public/ideContainer/imgs/tongbu.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/api/componentBase.ts b/src/api/componentBase.ts new file mode 100644 index 0000000..3a7c0bf --- /dev/null +++ b/src/api/componentBase.ts @@ -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); +}; \ No newline at end of file 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/componentDevelopProcess.ts b/src/api/componentDevelopProcess.ts new file mode 100644 index 0000000..64f8d2f --- /dev/null +++ b/src/api/componentDevelopProcess.ts @@ -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}`); +} \ No newline at end of file diff --git a/src/api/componentMarket.ts b/src/api/componentMarket.ts new file mode 100644 index 0000000..fa1f988 --- /dev/null +++ b/src/api/componentMarket.ts @@ -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 }); +} \ No newline at end of file diff --git a/src/api/componentRelease.ts b/src/api/componentRelease.ts new file mode 100644 index 0000000..ee47ed1 --- /dev/null +++ b/src/api/componentRelease.ts @@ -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 { + return axios.post(`${urlPrefix}/componentRelease/publish`, params); +} + +// 组件撤销 +export function componentRevoke(params: ComponentReleaseParams): AxiosPromise { + return axios.post(`${urlPrefix}/componentRelease/revoke`, params); +} \ 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 diff --git a/src/api/interface/index.ts b/src/api/interface/index.ts index 0af6396..723cafa 100644 --- a/src/api/interface/index.ts +++ b/src/api/interface/index.ts @@ -1,4 +1,6 @@ // application +import { getReviewGroupByNew } from '@/api/componentMarket'; + export interface CronModel { appId: string; cron: string; @@ -230,4 +232,43 @@ export interface GlobalParams { arrayType: any, defaultValue: any, sceneId: string, +} + +// 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; +} + +export interface ReviewGroup { + queryType: string; + current?: number; + size?: number; +} + +export interface ComponentMarketParams { + componentClassify: string; + componentClassifyLabel: string; + keyword: string; + current?: string | number; + size?: string | number; } \ No newline at end of file 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 diff --git a/src/components/FlowEditor/node/style/baseOther.module.less b/src/components/FlowEditor/node/style/baseOther.module.less index e0a4444..41a63e6 100644 --- a/src/components/FlowEditor/node/style/baseOther.module.less +++ b/src/components/FlowEditor/node/style/baseOther.module.less @@ -59,32 +59,42 @@ padding-left: 10px; } - - //.node-inputs, - //.node-outputs, .node-inputs-api, .node-outputs-api { flex: 1; } - .node-outputs-api { + .node-inputs { + margin-bottom: 5px; + .node-input-label { font-size: 12px; padding: 1px 0; height: 20px; line-height: 20px; - } - } - .node-inputs { - margin-bottom: 5px; + .node-data-type { + color: #adadad; + } + } } .node-outputs, .node-outputs-api { text-align: right; } + + .node-output-label { + font-size: 12px; + padding: 1px 0; + height: 20px; + line-height: 20px; + + .node-data-type { + color: #adadad; + } + } } .node-content-api { diff --git a/src/components/FlowEditor/nodeEditors/components/ParamsTable.tsx b/src/components/FlowEditor/nodeEditors/components/ParamsTable.tsx index 683748e..1263994 100644 --- a/src/components/FlowEditor/nodeEditors/components/ParamsTable.tsx +++ b/src/components/FlowEditor/nodeEditors/components/ParamsTable.tsx @@ -76,6 +76,7 @@ const ParamsTable: React.FC = ({ ) : ( handleSave({ ...record, id: value })} /> ) @@ -89,7 +90,7 @@ const ParamsTable: React.FC = ({ {record.dataType === 'INTEGER' ? '整数' : record.dataType} ) : ( handleSave({ ...record, arrayType: value })} placeholder="请选择数组类型" /> ) : ( - +
-
) ) }, @@ -151,9 +152,10 @@ const ParamsTable: React.FC = ({ allowClear options={options} style={{ width: '100%', minWidth: 100 }} + autoWidth={{ minWidth: 150, maxWidth: 200 }} /> handleSave({ ...record, defaultValue: value })} placeholder="请输入默认值" @@ -166,6 +168,7 @@ const ParamsTable: React.FC = ({ if (record.id === 'maxTime') { return ( = ({ return ( handleSave({ ...record, defaultValue: value })} placeholder="请输入默认值" @@ -189,7 +193,8 @@ const ParamsTable: React.FC = ({ title: '操作', dataIndex: 'op', render: (_: any, record: TableDataItem) => ( - record.id !== 'maxTime' && ) diff --git a/src/components/FlowEditor/nodeEditors/validators/nodeValidators.ts b/src/components/FlowEditor/nodeEditors/validators/nodeValidators.ts index 82a62f7..39a85a0 100644 --- a/src/components/FlowEditor/nodeEditors/validators/nodeValidators.ts +++ b/src/components/FlowEditor/nodeEditors/validators/nodeValidators.ts @@ -468,6 +468,7 @@ export const validateAllEdges = (edges: Edge[], nodes: any[]): ValidationResult break; case 'start': + case 'START': // 开始节点应该有输出连接,但不需要输入连接 if (sourceEdges === 0) { allErrors.push(`开始节点"${nodeName}"缺少输出连接`); @@ -475,6 +476,7 @@ export const validateAllEdges = (edges: Edge[], nodes: any[]): ValidationResult break; case 'end': + case 'END': // 结束节点应该有输入连接,但不需要输出连接 if (targetEdges === 0) { allErrors.push(`结束节点"${nodeName}"缺少输入连接`); 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..e40d2ab --- /dev/null +++ b/src/const/isdp/componentRelease.ts @@ -0,0 +1,178 @@ +import { globalOption, patternTrim } from '../globalOption'; + +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: '/api/blade-isdp/componentBase/tags', + 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/hooks/useFlowCallbacks.ts b/src/hooks/useFlowCallbacks.ts index 8d8b294..c39ea0f 100644 --- a/src/hooks/useFlowCallbacks.ts +++ b/src/hooks/useFlowCallbacks.ts @@ -39,10 +39,10 @@ import { appFLowHandle } from '@/pages/flowEditor/utils/appFlowhandle'; import { handelEventNodeList, updateEvent, upDatePublish } from '@/pages/flowEditor/utils/common'; import { Dispatch } from 'redux'; -import { runMainFlow, stopApp } from '@/api/apps'; +import { getAppListBySceneId, runMainFlow, stopApp } from '@/api/apps'; import store from '@/store'; import { updateAppEvent, updateAppEventChannel, updateAppFlowData } from '@/api/appEvent'; -import { sleep } from '@/utils/common'; +import { getUrlParams, sleep } from '@/utils/common'; import { queryEventItemBySceneIdOld, deleteEventSub, deleteEventPub } from '@/api/event'; export const useFlowCallbacks = ( @@ -912,26 +912,10 @@ export const useFlowCallbacks = ( }; } else if (nodeType === 'SUB') { - const flowSubMap = flowData[currentAppData.id]?.subMap || {}; - const sameData: any = flowSubMap[newNode.data.compId]; - if (sameData) { - newNode.data.component = { - type: nodeType, - compId: newNode.data.compId, - customDef: JSON.stringify({ - dataIns: newNode.data.parameters.dataIns, - dataOuts: newNode.data.parameters.dataOuts, - subflowId: sameData, - name: newNode.data.title - }) - }; - } - else { - newNode.data.component = { - type: nodeType, - compId: newNode.data.compId - }; - } + newNode.data.component = { + type: nodeType, + compId: newNode.data.compId + }; } else if (nodeType === 'EVENTSEND' || nodeType === 'EVENTLISTENE') { const { eventList } = store.getState().ideContainer; @@ -1067,26 +1051,10 @@ export const useFlowCallbacks = ( }; } else if (nodeType === 'SUB') { - const flowSubMap = flowData[currentAppData.id]?.subMap || {}; - const sameData: any = flowSubMap[newNode.data.compId]; - if (sameData) { - newNode.data.component = { - type: nodeType, - compId: newNode.data.compId, - customDef: JSON.stringify({ - dataIns: newNode.data.parameters.dataIns, - dataOuts: newNode.data.parameters.dataOuts, - subflowId: sameData, - name: newNode.data.title - }) - }; - } - else { - newNode.data.component = { - type: nodeType, - compId: newNode.data.compId - }; - } + newNode.data.component = { + type: nodeType, + compId: newNode.data.compId + }; } else if (nodeType === 'EVENTSEND' || nodeType === 'EVENTLISTENE') { const { eventList } = store.getState().ideContainer; @@ -1181,22 +1149,15 @@ export const useFlowCallbacks = ( const res1: any = await queryEventItemBySceneIdOld(info.id); if (res1.code === 200) dispatch(updateEventListOld(res1.data)); - // 更新缓存数据 - 与主流程保持一致 - dispatch(updateCanvasDataMap({ - ...canvasDataMap, - [currentAppData.id]: { nodes, edges } - })); - const appRes: any = await getAppInfoNew(currentAppData.id); + const appRes: any = await getAppInfoNew(currentAppData.parentAppId); // 更新 flowData 中的数据 - dispatch(updateFlowData({ [currentAppData.id]: appRes.data })); + dispatch(updateFlowData({ [currentAppData.parentAppId]: appRes.data })); // 同步更新到 canvasDataMap if (appRes.data.main?.components) { const { nodes, edges } = convertFlowData(appRes.data.main.components, true); - setNodes(nodes); - setEdges(edges); dispatch(updateCanvasDataMap({ ...canvasDataMap, - [currentAppData.id]: { nodes, edges } + [currentAppData.parentAppId]: { nodes, edges } })); } } @@ -1213,6 +1174,7 @@ export const useFlowCallbacks = ( appEventDefinition, sceneId: info.id }; + const res: any = await setMainFlowNew(params, initialData.appId); if (res.code === 200) { Message.success('保存成功'); @@ -1328,6 +1290,7 @@ export const useFlowCallbacks = ( } } }, [nodes, edges, initialData?.appId]); + // 运行处理函数 const handleRun = useCallback(async (running: boolean) => { const { currentAppData, socketId, appRuntimeData } = store.getState().ideContainer; diff --git a/src/pages/componentDevelopment/componentCoding/index.tsx b/src/pages/componentDevelopment/componentCoding/index.tsx index 7309ad9..b618264 100644 --- a/src/pages/componentDevelopment/componentCoding/index.tsx +++ b/src/pages/componentDevelopment/componentCoding/index.tsx @@ -1,185 +1,119 @@ -import React, { useEffect, useState } from 'react'; +import React, { useEffect, useState, useRef } from 'react'; import styles from './style/index.module.less'; -import { Button, Input, Select, Space, Cascader } from '@arco-design/web-react'; -import { IconSearch } from '@arco-design/web-react/icon'; +import { Button, Select, Space } from '@arco-design/web-react'; +import { IconFullscreen, IconFullscreenExit } from '@arco-design/web-react/icon'; +import { useSelector, useDispatch } from 'react-redux'; +import { getMyComponentList } from '@/api/componentBase'; +import { updateComponentCodingPath } from '@/store/ideContainer'; +import { getComponentBaseInfo } from '@/api/componentDevelopProcess'; const Option = Select.Option; const ComponentCoding = () => { - const [serverUrl, setServerUrl] = useState('https://arco.design/vue/component/button'); + const [serverUrl, setServerUrl] = useState(''); // code-server 地址 + const [optionsList, setOptionsList] = useState([]); // 下拉选择菜单 + const [originList, setOriginList] = useState([]); // 原始数据-组件列表 + const [currentComponent, setCurrentComponent] = useState({}); // 当前组件信息 + const [isFullscreen, setIsFullscreen] = useState(false); // 全屏状态 + const iframeRef = useRef(null); + const { componentCoding } = useSelector((state: any) => state.ideContainer); + const dispatch = useDispatch(); + const getOptionsList = async () => { + const res: any = await getMyComponentList({ + currPage: 1, + pageSize: 999 + }); + if (res.code === 200) { + setOriginList(res.data.list); + setOptionsList(res.data.list.map(item => { + return { label: item.name, value: item.localProjectPath, ...item }; + })); + } + }; + + const getComponentInfo = async () => { + const res: any = await getComponentBaseInfo(componentCoding.id); + if (res.code === 200) { + setCurrentComponent(res.data); + } + }; useEffect(() => { + getOptionsList(); + }, []); + + useEffect(() => { + componentCoding.id && getComponentInfo(); const uri = process.env.NEXT_PUBLIC_DEV_CODE_SERVER_HOST; const codeServerFolderPre = '/app/data'; - const tempData = '/000000/admin_testcode1/master'; - setServerUrl(`${uri}?folder=${codeServerFolderPre}${tempData}`); - - }); - - const componentScreening = () => { - // 将数据结构修改为级联结构 - const cascaderOptions = [ - { - label: '设备数采与控制交互组件', - value: '2', - children: [ - { - label: 'Java:8', - value: '2-1', - children: [ - { - label: '数据采集模块', - value: '2-1-1' - }, - { - label: '设备控制接口', - value: '2-1-2' - } - ] - }, - { - label: 'Python:3.10.12', - value: '2-2', - children: [ - { - label: '数据采集模块', - value: '2-2-1' - }, - { - label: '设备控制接口', - value: '2-2-2' - } - ] - } - ] - }, - { - label: '视觉AI组件', - value: '3', - children: [ - { - label: 'Java:8', - value: '3-1', - children: [ - { - label: '图像识别', - value: '3-1-1' - }, - { - label: '目标检测', - value: '3-1-2' - } - ] - }, - { - label: 'Python:3.10.12', - value: '3-2', - children: [ - { - label: '图像识别', - value: '3-2-1' - }, - { - label: '目标检测', - value: '3-2-2' - } - ] - } - ] - }, - { - label: '运动规划组件', - value: '4', - children: [ - { - label: 'Java:8', - value: '4-1', - children: [ - { - label: '路径规划', - value: '4-1-1' - } - ] - }, - { - label: 'Python:3.10.12', - value: '4-2', - children: [ - { - label: '路径规划', - value: '4-2-1' - } - ] - } - ] - }, - { - label: '工艺知识服务组件', - value: '5', - children: [ - { - label: 'Java:8', - value: '5-1', - children: [ - { - label: '工艺参数优化', - value: '5-1-1' - } - ] - }, - { - label: 'Python:3.10.12', - value: '5-2', - children: [ - { - label: '工艺参数优化', - value: '5-2-1' - } - ] - } - ] - }, - { - label: '时序数据AI组件', - value: '6', - children: [ - { - label: 'Java:8', - value: '6-1', - children: [ - { - label: '时间序列预测', - value: '6-1-1' - } - ] - }, - { - label: 'Python:3.10.12', - value: '6-2', - children: [ - { - label: '时间序列预测', - value: '6-2-1' - } - ] - } - ] + + // 使用传入的localProjectPath或默认值 + const path = componentCoding.localProjectPath || '/000000/admin_testcode1/master'; + setServerUrl(`${uri}?folder=${codeServerFolderPre}${path}`); + }, [componentCoding]); + + // 监听全屏状态变化 + useEffect(() => { + const handleFullscreenChange = () => { + const isCurrentlyFullscreen = + document.fullscreenElement || + (document as any).webkitFullscreenElement || + (document as any).mozFullScreenElement || + (document as any).msFullscreenElement; + + setIsFullscreen(!!isCurrentlyFullscreen); + }; + + document.addEventListener('fullscreenchange', handleFullscreenChange); + document.addEventListener('webkitfullscreenchange', handleFullscreenChange); + document.addEventListener('mozfullscreenchange', handleFullscreenChange); + document.addEventListener('MSFullscreenChange', handleFullscreenChange); + + return () => { + document.removeEventListener('fullscreenchange', handleFullscreenChange); + document.removeEventListener('webkitfullscreenchange', handleFullscreenChange); + document.removeEventListener('mozfullscreenchange', handleFullscreenChange); + document.removeEventListener('MSFullscreenChange', handleFullscreenChange); + }; + }, []); + + // 切换全屏状态 + const toggleFullscreen = () => { + const iframeContainer = document.querySelector(`.${styles['code-iframe']}`) as HTMLElement; + + if (!isFullscreen) { + // 进入全屏 + if (iframeContainer.requestFullscreen) { + iframeContainer.requestFullscreen(); + } + else if ((iframeContainer as any).webkitRequestFullscreen) { + (iframeContainer as any).webkitRequestFullscreen(); + } + else if ((iframeContainer as any).mozRequestFullScreen) { + (iframeContainer as any).mozRequestFullScreen(); + } + else if ((iframeContainer as any).msRequestFullscreen) { + (iframeContainer as any).msRequestFullscreen(); + } + } + else { + // 退出全屏 + if (document.exitFullscreen) { + document.exitFullscreen(); } - ]; - - return ( - // 使用 Cascader 组件替换 Select 组件 - - ); + else if ((document as any).webkitExitFullscreen) { + (document as any).webkitExitFullscreen(); + } + else if ((document as any).mozCancelFullScreen) { + (document as any).mozCancelFullScreen(); + } + else if ((document as any).msExitFullscreen) { + (document as any).msExitFullscreen(); + } + } }; - return (
@@ -187,9 +121,48 @@ const ComponentCoding = () => { size={50} style={{ marginTop: '20px', marginBottom: '30px' }} > -
- 组件筛选: - {componentScreening()} +
+ +
+ 组件名称: + {componentCoding.name || '未选择组件'} +
+
+ 组件标识: + {componentCoding.projectId || '未选择组件'} +
+
+ 组件选择: + +
+
@@ -200,17 +173,35 @@ const ComponentCoding = () => { > 启动项设置 - } - placeholder={'搜索组件关键词'} - style={{ width: 236 }} - />
-