├── public ├── favicon.ico ├── logo192.png ├── logo512.png ├── robots.txt ├── manifest.json └── index.html ├── src ├── pages │ └── design │ │ ├── components │ │ ├── XmlHub.tsx │ │ ├── style2.less │ │ ├── CustomModeler │ │ │ ├── Custom │ │ │ │ ├── index.ts │ │ │ │ ├── CustomPalette.ts │ │ │ │ └── CustomContextPadProvider.ts │ │ │ └── index.ts │ │ ├── sourceXml.tsx │ │ ├── initXMLData.ts │ │ ├── style.module.scss │ │ ├── contact │ │ │ └── index.tsx │ │ ├── design.tsx │ │ ├── translationsGerman.ts │ │ ├── PropertiesPanel.tsx │ │ └── activiti.json │ │ ├── service │ │ └── design.ts │ │ └── index.tsx ├── setupTests.js ├── App.test.js ├── index.css ├── index.js ├── App.css ├── App.js ├── logo.svg └── serviceWorker.js ├── .gitignore ├── package.json └── README.md /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shengbid/design-test/master/public/favicon.ico -------------------------------------------------------------------------------- /public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shengbid/design-test/master/public/logo192.png -------------------------------------------------------------------------------- /public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shengbid/design-test/master/public/logo512.png -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /src/pages/design/components/XmlHub.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export const XmlContent = React.createContext({ 4 | xml: '', 5 | setXml: () => null, 6 | }) 7 | -------------------------------------------------------------------------------- /src/pages/design/components/style2.less: -------------------------------------------------------------------------------- 1 | 2 | // @import '~antd/es/style/themes/default.less'; 3 | 4 | .CodeMirror { 5 | height: 100%; 6 | } 7 | .bjs-powered-by { 8 | display: none; 9 | } -------------------------------------------------------------------------------- /src/setupTests.js: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom/extend-expect'; 6 | -------------------------------------------------------------------------------- /src/App.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { render } from '@testing-library/react'; 3 | import App from './App'; 4 | 5 | test('renders learn react link', () => { 6 | const { getByText } = render(); 7 | const linkElement = getByText(/learn react/i); 8 | expect(linkElement).toBeInTheDocument(); 9 | }); 10 | -------------------------------------------------------------------------------- /src/pages/design/components/CustomModeler/Custom/index.ts: -------------------------------------------------------------------------------- 1 | import CustomPalette from './CustomPalette' 2 | import CustomContextPadProvider from './CustomContextPadProvider' 3 | export default { 4 | __init__: ['paletteProvider', 'contextPadProvider'], 5 | paletteProvider: ['type', CustomPalette], 6 | contextPadProvider: ['type', CustomContextPadProvider] 7 | } 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 | monospace; 13 | } 14 | -------------------------------------------------------------------------------- /src/pages/design/components/CustomModeler/index.ts: -------------------------------------------------------------------------------- 1 | // @ts-nocheck 2 | import Modeler from 'bpmn-js/lib/Modeler' 3 | 4 | import inherits from 'inherits' 5 | 6 | import CustomModule from './Custom' 7 | 8 | export default function CustomModeler(options) { 9 | Modeler.call(this, options) 10 | 11 | this._customElements = [] 12 | } 13 | 14 | inherits(CustomModeler, Modeler) 15 | 16 | CustomModeler.prototype._modules = [].concat(CustomModeler.prototype._modules, [CustomModule]) 17 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.css'; 4 | import App from './App'; 5 | import * as serviceWorker from './serviceWorker'; 6 | 7 | ReactDOM.render( 8 | 9 | 10 | , 11 | document.getElementById('root') 12 | ); 13 | 14 | // If you want your app to work offline and load faster, you can change 15 | // unregister() to register() below. Note this comes with some pitfalls. 16 | // Learn more about service workers: https://bit.ly/CRA-PWA 17 | serviceWorker.unregister(); 18 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /src/App.css: -------------------------------------------------------------------------------- 1 | /* .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | height: 40vmin; 7 | pointer-events: none; 8 | } 9 | 10 | @media (prefers-reduced-motion: no-preference) { 11 | .App-logo { 12 | animation: App-logo-spin infinite 20s linear; 13 | } 14 | } 15 | 16 | .App-header { 17 | background-color: #282c34; 18 | min-height: 100vh; 19 | display: flex; 20 | flex-direction: column; 21 | align-items: center; 22 | justify-content: center; 23 | font-size: calc(10px + 2vmin); 24 | color: white; 25 | } 26 | 27 | .App-link { 28 | color: #61dafb; 29 | } 30 | 31 | @keyframes App-logo-spin { 32 | from { 33 | transform: rotate(0deg); 34 | } 35 | to { 36 | transform: rotate(360deg); 37 | } 38 | } */ 39 | -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | // import logo from './logo.svg'; 3 | import './App.css'; 4 | import Design from './pages/design/index.tsx' 5 | 6 | function App() { 7 | return ( 8 |
9 |
10 | {/* logo 11 |

12 | Edit src/App.js and save to reload. 13 |

14 | 20 | Learn React 21 | */} 22 | 23 |
24 |
25 | ); 26 | } 27 | 28 | export default App; 29 | -------------------------------------------------------------------------------- /src/pages/design/service/design.ts: -------------------------------------------------------------------------------- 1 | import request from '@/commons/request' 2 | 3 | export function getAllDept() { 4 | return request({ 5 | url: '/getAll/dept', 6 | method: 'get' 7 | }) 8 | } 9 | 10 | export function getAllList(params: any) { 11 | return request({ 12 | url: '/getAll/list', 13 | method: 'get', 14 | params, 15 | }) 16 | } 17 | 18 | export function getAllRoleList() { 19 | return request({ 20 | url: '/getAll/role/list', 21 | method: 'get' 22 | }) 23 | } 24 | 25 | export function getUserById(data: any) { 26 | return request({ 27 | url: '/get/user/id', 28 | method: 'post', 29 | data, 30 | }) 31 | } 32 | 33 | export function deployBpmnByString() { 34 | return request({ 35 | url: '/deploy/BpmnBy/String', 36 | method: 'post' 37 | }) 38 | } 39 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "design-test", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^4.2.4", 7 | "@testing-library/react": "^9.3.2", 8 | "@testing-library/user-event": "^7.1.2", 9 | "react": "^16.13.1", 10 | "react-dom": "^16.13.1", 11 | "react-scripts": "3.4.3" 12 | }, 13 | "scripts": { 14 | "start": "react-scripts start", 15 | "build": "react-scripts build", 16 | "test": "react-scripts test", 17 | "eject": "react-scripts eject" 18 | }, 19 | "eslintConfig": { 20 | "extends": "react-app" 21 | }, 22 | "browserslist": { 23 | "production": [ 24 | ">0.2%", 25 | "not dead", 26 | "not op_mini all" 27 | ], 28 | "development": [ 29 | "last 1 chrome version", 30 | "last 1 firefox version", 31 | "last 1 safari version" 32 | ] 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/pages/design/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react' 2 | import { Tabs } from 'antd' 3 | import ProcessInit from './components/design' 4 | import SourceXML from './components/sourceXml' 5 | 6 | const TableList: React.FC<{}> = () => { 7 | const [tab, setTab] = useState('0') 8 | const [xml, setXml] = useState(null) 9 | 10 | const { TabPane } = Tabs 11 | const setXmls = (l: any) => { 12 | setXml(l) 13 | } 14 | 15 | return ( 16 |
17 |
18 | { setTab(key) }}> 19 | {['流程设计器', 'XML 源码'].map((v, i) => { 20 | return 21 | {tab === '0' 22 | ? 25 | })} 26 | 27 |
28 |
29 | ) 30 | } 31 | 32 | export default TableList 33 | -------------------------------------------------------------------------------- /src/pages/design/components/sourceXml.tsx: -------------------------------------------------------------------------------- 1 | // @ts-nocheck 2 | import React from 'react' 3 | import { UnControlled as CodeMirror } from 'react-codemirror2' 4 | import 'codemirror/mode/xml/xml.js' 5 | import 'codemirror/lib/codemirror.css' 6 | import 'codemirror/theme/idea.css' 7 | import 'codemirror/addon/fold/foldgutter.css' 8 | import 'codemirror/addon/fold/foldcode.js' 9 | import 'codemirror/addon/fold/foldgutter.js' 10 | import 'codemirror/addon/fold/xml-fold.js' 11 | import 'codemirror/addon/fold/indent-fold.js' 12 | import 'codemirror/addon/fold/brace-fold' 13 | import 'codemirror/addon/fold/markdown-fold.js' 14 | import 'codemirror/addon/fold/comment-fold.js' 15 | import 'codemirror/addon/selection/active-line' 16 | import styles from './style.module.scss' 17 | import './style2.less' 18 | 19 | interface IpProps { 20 | data: any 21 | } 22 | const TableList: React.FC = (props: any) => { 23 | const { data } = props 24 | return ( 25 |
26 | {}} 43 | /> 44 |
45 | ) 46 | } 47 | 48 | export default TableList 49 | -------------------------------------------------------------------------------- /src/pages/design/components/initXMLData.ts: -------------------------------------------------------------------------------- 1 | export const initXML = ` 2 | 3 | 4 | 5 | Flow_1wzoseh 6 | 7 | 8 | 9 | Flow_1wzoseh 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | ` 30 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | React App 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /src/pages/design/components/style.module.scss: -------------------------------------------------------------------------------- 1 | .processUl { 2 | position: absolute; 3 | left: 10px; 4 | bottom: 8px; 5 | font-size: 0; 6 | background-color: #fff; 7 | border: 1px solid #dcdfe6; 8 | display: flex; 9 | flex-direction: row; 10 | border-radius: 4px; 11 | padding: 0; 12 | li { 13 | text-align: center; 14 | width: 44px; 15 | font-size: 14px; 16 | border-left: 1px solid #dcdfe6; 17 | margin-left: -1px; 18 | color: #606266; 19 | &:hover { 20 | color: #1890ff; 21 | background-color: #e8f4ff; 22 | } 23 | .ant-btn { 24 | border: none !important; 25 | background: none !important; 26 | } 27 | } 28 | } 29 | 30 | .sourceContaier { 31 | height: calc(100vh - 200px); 32 | overflow-y: auto; 33 | overflow-x: hidden; 34 | .CodeMirror { 35 | height: 100%; 36 | } 37 | } 38 | 39 | .processContanier { 40 | display: flex; 41 | .ModelerContainer { 42 | min-height: 655px; 43 | flex: 1; 44 | background-image: linear-gradient(90deg, hsla(0, 0%, 86.3%, 0.5) 6%, transparent 0), 45 | linear-gradient(hsla(0, 0%, 75.3%, 0.5) 6%, transparent 0); 46 | background-size: 15px 15px; 47 | } 48 | .propertyContanier { 49 | overflow-y: auto; 50 | background: #fff; 51 | flex-basis: 360px; 52 | } 53 | } 54 | 55 | .userContainer { 56 | .userList { 57 | display: flex; 58 | border: 1px solid #ccc; 59 | .userLeft { 60 | flex: 1; 61 | border-right: 1px solid #ccc; 62 | height: 390px; 63 | overflow: auto; 64 | padding-left: 10px; 65 | } 66 | .userRight { 67 | flex: 1; 68 | height: 390px; 69 | overflow: auto; 70 | padding-left: 10px; 71 | .userInfo { 72 | display: inline-block; 73 | max-width: 250px; 74 | white-space: nowrap; 75 | text-overflow: ellipsis; 76 | padding-left: 10px; 77 | padding-top: 12px; 78 | } 79 | } 80 | } 81 | .selectList { 82 | border: 1px solid #ccc; 83 | height: 80px; 84 | border-top: none; 85 | .selectItem { 86 | display: inline-block; 87 | padding: 10px 15px; 88 | } 89 | } 90 | } -------------------------------------------------------------------------------- /src/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). 2 | 3 | ## Available Scripts 4 | 5 | In the project directory, you can run: 6 | 7 | ### `yarn start` 8 | 9 | Runs the app in the development mode.
10 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser. 11 | 12 | The page will reload if you make edits.
13 | You will also see any lint errors in the console. 14 | 15 | ### `yarn test` 16 | 17 | Launches the test runner in the interactive watch mode.
18 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. 19 | 20 | ### `yarn build` 21 | 22 | Builds the app for production to the `build` folder.
23 | It correctly bundles React in production mode and optimizes the build for the best performance. 24 | 25 | The build is minified and the filenames include the hashes.
26 | Your app is ready to be deployed! 27 | 28 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. 29 | 30 | ### `yarn eject` 31 | 32 | **Note: this is a one-way operation. Once you `eject`, you can’t go back!** 33 | 34 | If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project. 35 | 36 | Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own. 37 | 38 | You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it. 39 | 40 | ## Learn More 41 | 42 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started). 43 | 44 | To learn React, check out the [React documentation](https://reactjs.org/). 45 | 46 | ### Code Splitting 47 | 48 | This section has moved here: https://facebook.github.io/create-react-app/docs/code-splitting 49 | 50 | ### Analyzing the Bundle Size 51 | 52 | This section has moved here: https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size 53 | 54 | ### Making a Progressive Web App 55 | 56 | This section has moved here: https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app 57 | 58 | ### Advanced Configuration 59 | 60 | This section has moved here: https://facebook.github.io/create-react-app/docs/advanced-configuration 61 | 62 | ### Deployment 63 | 64 | This section has moved here: https://facebook.github.io/create-react-app/docs/deployment 65 | 66 | ### `yarn build` fails to minify 67 | 68 | This section has moved here: https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify 69 | -------------------------------------------------------------------------------- /src/serviceWorker.js: -------------------------------------------------------------------------------- 1 | // This optional code is used to register a service worker. 2 | // register() is not called by default. 3 | 4 | // This lets the app load faster on subsequent visits in production, and gives 5 | // it offline capabilities. However, it also means that developers (and users) 6 | // will only see deployed updates on subsequent visits to a page, after all the 7 | // existing tabs open on the page have been closed, since previously cached 8 | // resources are updated in the background. 9 | 10 | // To learn more about the benefits of this model and instructions on how to 11 | // opt-in, read https://bit.ly/CRA-PWA 12 | 13 | const isLocalhost = Boolean( 14 | window.location.hostname === 'localhost' || 15 | // [::1] is the IPv6 localhost address. 16 | window.location.hostname === '[::1]' || 17 | // 127.0.0.0/8 are considered localhost for IPv4. 18 | window.location.hostname.match( 19 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ 20 | ) 21 | ); 22 | 23 | export function register(config) { 24 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { 25 | // The URL constructor is available in all browsers that support SW. 26 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href); 27 | if (publicUrl.origin !== window.location.origin) { 28 | // Our service worker won't work if PUBLIC_URL is on a different origin 29 | // from what our page is served on. This might happen if a CDN is used to 30 | // serve assets; see https://github.com/facebook/create-react-app/issues/2374 31 | return; 32 | } 33 | 34 | window.addEventListener('load', () => { 35 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; 36 | 37 | if (isLocalhost) { 38 | // This is running on localhost. Let's check if a service worker still exists or not. 39 | checkValidServiceWorker(swUrl, config); 40 | 41 | // Add some additional logging to localhost, pointing developers to the 42 | // service worker/PWA documentation. 43 | navigator.serviceWorker.ready.then(() => { 44 | console.log( 45 | 'This web app is being served cache-first by a service ' + 46 | 'worker. To learn more, visit https://bit.ly/CRA-PWA' 47 | ); 48 | }); 49 | } else { 50 | // Is not localhost. Just register service worker 51 | registerValidSW(swUrl, config); 52 | } 53 | }); 54 | } 55 | } 56 | 57 | function registerValidSW(swUrl, config) { 58 | navigator.serviceWorker 59 | .register(swUrl) 60 | .then(registration => { 61 | registration.onupdatefound = () => { 62 | const installingWorker = registration.installing; 63 | if (installingWorker == null) { 64 | return; 65 | } 66 | installingWorker.onstatechange = () => { 67 | if (installingWorker.state === 'installed') { 68 | if (navigator.serviceWorker.controller) { 69 | // At this point, the updated precached content has been fetched, 70 | // but the previous service worker will still serve the older 71 | // content until all client tabs are closed. 72 | console.log( 73 | 'New content is available and will be used when all ' + 74 | 'tabs for this page are closed. See https://bit.ly/CRA-PWA.' 75 | ); 76 | 77 | // Execute callback 78 | if (config && config.onUpdate) { 79 | config.onUpdate(registration); 80 | } 81 | } else { 82 | // At this point, everything has been precached. 83 | // It's the perfect time to display a 84 | // "Content is cached for offline use." message. 85 | console.log('Content is cached for offline use.'); 86 | 87 | // Execute callback 88 | if (config && config.onSuccess) { 89 | config.onSuccess(registration); 90 | } 91 | } 92 | } 93 | }; 94 | }; 95 | }) 96 | .catch(error => { 97 | console.error('Error during service worker registration:', error); 98 | }); 99 | } 100 | 101 | function checkValidServiceWorker(swUrl, config) { 102 | // Check if the service worker can be found. If it can't reload the page. 103 | fetch(swUrl, { 104 | headers: { 'Service-Worker': 'script' }, 105 | }) 106 | .then(response => { 107 | // Ensure service worker exists, and that we really are getting a JS file. 108 | const contentType = response.headers.get('content-type'); 109 | if ( 110 | response.status === 404 || 111 | (contentType != null && contentType.indexOf('javascript') === -1) 112 | ) { 113 | // No service worker found. Probably a different app. Reload the page. 114 | navigator.serviceWorker.ready.then(registration => { 115 | registration.unregister().then(() => { 116 | window.location.reload(); 117 | }); 118 | }); 119 | } else { 120 | // Service worker found. Proceed as normal. 121 | registerValidSW(swUrl, config); 122 | } 123 | }) 124 | .catch(() => { 125 | console.log( 126 | 'No internet connection found. App is running in offline mode.' 127 | ); 128 | }); 129 | } 130 | 131 | export function unregister() { 132 | if ('serviceWorker' in navigator) { 133 | navigator.serviceWorker.ready 134 | .then(registration => { 135 | registration.unregister(); 136 | }) 137 | .catch(error => { 138 | console.error(error.message); 139 | }); 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /src/pages/design/components/contact/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react' 2 | import { Modal, Button, Tree, Avatar, Checkbox, Row, Col, Tag, message } from 'antd' 3 | import { UserOutlined } from '@ant-design/icons' 4 | import { getAllDept, getAllList } from '../../service/design' 5 | import styles from '../style.module.scss' 6 | 7 | interface UpdateFormProps { 8 | onCancel: () => void 9 | setCandidateUsers: (values: any) => void 10 | setAssignee: (values: any) => void 11 | userModalVisible: boolean 12 | values: any 13 | } 14 | 15 | const TableList: React.FC = (props: any) => { 16 | const [count, setCount] = useState(0) 17 | const [treeData, setTreeData] = useState([]) 18 | const [userData, setUserData] = useState([]) 19 | const [selectUsers, setSelectUsers] = useState([]) 20 | const [selectIds, setSelectIds] = useState([]) 21 | 22 | const { 23 | setCandidateUsers, 24 | setAssignee, 25 | userModalVisible, 26 | onCancel, 27 | } = props 28 | 29 | const { 30 | name, 31 | selectedIds, 32 | } = props.values 33 | 34 | const maxCount = props.values.maxSelectNum ? props.values.maxSelectNum : 1000 35 | 36 | const onSelect = async(expandedKeys: any, update = 0) => { 37 | const result = await getAllList({ 38 | deptId: Number(expandedKeys[0]), 39 | }) 40 | if (result.data && result.data.userList) { 41 | setUserData(result.data.userList) 42 | if (update === 1) { 43 | let ids: any[] = [] 44 | ids = selectedIds.map((item: any) => { return item.id }) 45 | setSelectIds([...ids]) 46 | const users = result.data.userList.filter((item: any) => { 47 | return ids.includes(item.id) 48 | }) 49 | setSelectUsers(users) 50 | setCount(users.length) 51 | console.log(selectedIds, treeData, selectIds, users) 52 | } 53 | } else setUserData([]) 54 | } 55 | 56 | const initFn = () => { 57 | if (treeData.length) { 58 | onSelect([treeData[0].id], 1) 59 | } 60 | } 61 | 62 | const handleSubmit = () => { 63 | if (!selectUsers.length) { 64 | message.warning('请选择成员后再提交') 65 | return 66 | } 67 | if (name === 'assignee') { 68 | setAssignee(selectUsers) 69 | } else setCandidateUsers(selectUsers) 70 | setSelectIds([]) 71 | setSelectUsers([]) 72 | setCount(0) 73 | } 74 | 75 | const getRoleList = async() => { 76 | const result = await getAllDept() 77 | if (result.data && result.data.deptList) { 78 | setTreeData(result.data.deptList) 79 | onSelect([result.data.deptList[0].id]) 80 | } else setTreeData([]) 81 | } 82 | 83 | // 选择成员 84 | const onCheckUser = (e: any, item: any) => { 85 | if (e.target.checked) { 86 | selectUsers.push(item) 87 | setCount((pre: number) => { 88 | return pre + 1 89 | }) 90 | } else { 91 | selectUsers.some((sub: any, i: number) => { 92 | if (sub.id === item.id) { 93 | selectUsers.splice(i, 1) 94 | return 95 | } 96 | }) 97 | setCount((pre: number) => { 98 | return pre - 1 99 | }) 100 | } 101 | const selectId = selectUsers.map((sub: any) => { 102 | return sub.id 103 | }) 104 | console.log(4, selectId, selectUsers) 105 | setSelectIds(selectId) 106 | setSelectUsers(selectUsers) 107 | } 108 | 109 | const renderFooter = () => { 110 | return ( 111 | <> 112 | 113 | 114 | ) 115 | } 116 | 117 | useEffect(() => { 118 | if (userModalVisible) { 119 | getRoleList() 120 | } 121 | }, [userModalVisible]) 122 | 123 | useEffect(() => { 124 | initFn() 125 | }, [selectedIds]) 126 | 127 | return ( 128 | onCancel()} 136 | > 137 |
138 |
139 |
140 |

组织架构

141 | { 142 | treeData.length > 0 && 143 | { onSelect(v) }} 147 | treeData={treeData} 148 | /> 149 | } 150 |
151 |
152 |

成员列表(写死数据)

153 | 154 | {userData.length && userData.map((item: any) => { 155 | return 156 | = maxCount && !selectIds.includes(item.id)} 159 | onChange={(values) => onCheckUser(values, item)}> 160 | } style={{ marginTop: '-12px' }} /> 161 |
162 |
163 | {item.name} 164 | {item.status === '2' && 冻结} 165 |
166 |
{item.deptName}
167 |
168 |
169 | 170 | })} 171 |
172 |
173 |
174 |
175 | {selectUsers.map((item: any) => { 176 | return
177 | } /> 178 |
{item.name}
179 |
180 | })} 181 |
182 |
已选: {count}/{maxCount} 位
183 |
184 |
185 | ) 186 | } 187 | 188 | export default TableList 189 | -------------------------------------------------------------------------------- /src/pages/design/components/CustomModeler/Custom/CustomPalette.ts: -------------------------------------------------------------------------------- 1 | // @ts-nocheck 2 | import { assign } from 'lodash' 3 | 4 | /** 5 | * A palette provider for BPMN 2.0 elements. 6 | */ 7 | export default function PaletteProvider( 8 | palette, 9 | create, 10 | elementFactory, 11 | spaceTool, 12 | lassoTool, 13 | handTool, 14 | globalConnect, 15 | translate 16 | ) { 17 | this._palette = palette 18 | this._create = create 19 | this._elementFactory = elementFactory 20 | this._spaceTool = spaceTool 21 | this._lassoTool = lassoTool 22 | this._handTool = handTool 23 | this._globalConnect = globalConnect 24 | this._translate = translate 25 | 26 | palette.registerProvider(this) 27 | } 28 | 29 | PaletteProvider.$inject = [ 30 | 'palette', 31 | 'create', 32 | 'elementFactory', 33 | 'spaceTool', 34 | 'lassoTool', 35 | 'handTool', 36 | 'globalConnect', 37 | 'translate' 38 | ] 39 | 40 | PaletteProvider.prototype.getPaletteEntries = function(element) { 41 | // eslint-disable-next-line one-var 42 | const actions = {}, 43 | create = this._create, 44 | elementFactory = this._elementFactory, 45 | spaceTool = this._spaceTool, 46 | lassoTool = this._lassoTool, 47 | handTool = this._handTool, 48 | globalConnect = this._globalConnect, 49 | translate = this._translate 50 | 51 | function createAction(type?, group?, className?, title?, options?) { 52 | function createListener(event) { 53 | const shape = elementFactory.createShape(assign({ type: type }, options)) 54 | 55 | if (options) { 56 | shape.businessObject.di.isExpanded = options.isExpanded 57 | } 58 | 59 | create.start(event, shape) 60 | } 61 | 62 | const shortType = type.replace(/^bpmn:/, '') 63 | 64 | return { 65 | group: group, 66 | className: className, 67 | title: title || translate('Create {type}', { type: shortType }), 68 | action: { 69 | dragstart: createListener, 70 | click: createListener 71 | } 72 | } 73 | } 74 | 75 | function createSubprocess(event) { 76 | const subProcess = elementFactory.createShape({ 77 | type: 'bpmn:SubProcess', 78 | x: 0, 79 | y: 0, 80 | isExpanded: true 81 | }) 82 | 83 | const startEvent = elementFactory.createShape({ 84 | type: 'bpmn:StartEvent', 85 | x: 40, 86 | y: 82, 87 | parent: subProcess 88 | }) 89 | 90 | create.start(event, [subProcess, startEvent], { 91 | hints: { 92 | autoSelect: [startEvent] 93 | } 94 | }) 95 | } 96 | 97 | function createParticipant(event) { 98 | create.start(event, elementFactory.createParticipantShape()) 99 | } 100 | 101 | assign(actions, { 102 | 'hand-tool': { 103 | group: 'tools', 104 | className: 'bpmn-icon-hand-tool', 105 | title: translate('Activate the hand tool'), 106 | action: { 107 | click: function(event) { 108 | handTool.activateHand(event) 109 | } 110 | } 111 | }, 112 | 'lasso-tool': { 113 | group: 'tools', 114 | className: 'bpmn-icon-lasso-tool', 115 | title: translate('Activate the lasso tool'), 116 | action: { 117 | click: function(event) { 118 | lassoTool.activateSelection(event) 119 | } 120 | } 121 | }, 122 | 'space-tool': { 123 | group: 'tools', 124 | className: 'bpmn-icon-space-tool', 125 | title: translate('Activate the create/remove space tool'), 126 | action: { 127 | click: function(event) { 128 | spaceTool.activateSelection(event) 129 | } 130 | } 131 | }, 132 | 'global-connect-tool': { 133 | group: 'tools', 134 | className: 'bpmn-icon-connection-multi', 135 | title: translate('Activate the global connect tool'), 136 | action: { 137 | click: function(event) { 138 | globalConnect.toggle(event) 139 | } 140 | } 141 | }, 142 | 'tool-separator': { 143 | group: 'tools', 144 | separator: true 145 | }, 146 | 'create.start-event': createAction( 147 | 'bpmn:StartEvent', 148 | 'event', 149 | 'bpmn-icon-start-event-none', 150 | translate('Create StartEvent') 151 | ), 152 | 'create.intermediate-event': createAction( 153 | 'bpmn:IntermediateThrowEvent', 154 | 'event', 155 | 'bpmn-icon-intermediate-event-none', 156 | translate('Create Intermediate/Boundary Event') 157 | ), 158 | 'create.end-event': createAction( 159 | 'bpmn:EndEvent', 160 | 'event', 161 | 'bpmn-icon-end-event-none', 162 | translate('Create EndEvent') 163 | ), 164 | 'create.exclusive-gateway': createAction( 165 | 'bpmn:ExclusiveGateway', 166 | 'gateway', 167 | 'bpmn-icon-gateway-none', 168 | translate('Create Gateway') 169 | ), 170 | 'create.task': createAction( 171 | 'bpmn:Task', 172 | 'activity', 173 | 'bpmn-icon-task', 174 | translate('Create Task') 175 | ), 176 | 'create.data-object': createAction( 177 | 'bpmn:DataObjectReference', 178 | 'data-object', 179 | 'bpmn-icon-data-object', 180 | translate('Create DataObjectReference') 181 | ), 182 | 'create.data-store': createAction( 183 | 'bpmn:DataStoreReference', 184 | 'data-store', 185 | 'bpmn-icon-data-store', 186 | translate('Create DataStoreReference') 187 | ), 188 | 'create.subprocess-expanded': { 189 | group: 'activity', 190 | className: 'bpmn-icon-subprocess-expanded', 191 | title: translate('Create expanded SubProcess'), 192 | action: { 193 | dragstart: createSubprocess, 194 | click: createSubprocess 195 | } 196 | }, 197 | 'create.participant-expanded': { 198 | group: 'collaboration', 199 | className: 'bpmn-icon-participant', 200 | title: translate('Create Pool/Participant'), 201 | action: { 202 | dragstart: createParticipant, 203 | click: createParticipant 204 | } 205 | }, 206 | 'create.group': createAction( 207 | 'bpmn:Group', 208 | 'artifact', 209 | 'bpmn-icon-group', 210 | translate('Create Group') 211 | ) 212 | }) 213 | 214 | return actions 215 | } 216 | -------------------------------------------------------------------------------- /src/pages/design/components/design.tsx: -------------------------------------------------------------------------------- 1 | // @ts-nocheck 2 | import React, { useState, useEffect, useRef } from 'react' 3 | import { Tooltip, Button, message, Modal } from 'antd' 4 | import { 5 | FolderAddOutlined, 6 | DownloadOutlined, 7 | FileImageOutlined, 8 | ArrowLeftOutlined, 9 | ArrowRightOutlined, 10 | ReloadOutlined, 11 | PlusCircleOutlined, 12 | MinusCircleOutlined, 13 | ToolOutlined, 14 | PlayCircleOutlined, 15 | } from '@ant-design/icons' 16 | import { deployBpmnByString } from '../service/design' 17 | import FileSaver from 'file-saver' 18 | import propertiesPanelModule from 'bpmn-js-properties-panel' 19 | import propertiesProviderModule from 'bpmn-js-properties-panel/lib/provider/camunda' 20 | import BpmnModeler from './CustomModeler/index' 21 | import activitiModdleDescriptor from './activiti.json' 22 | import PropertiesPanel from './PropertiesPanel' 23 | import { initXML } from './initXMLData' 24 | import 'bpmn-js/dist/assets/diagram-js.css' 25 | import 'bpmn-js/dist/assets/bpmn-font/css/bpmn.css' 26 | import 'bpmn-js/dist/assets/bpmn-font/css/bpmn-codes.css' 27 | import 'bpmn-js/dist/assets/bpmn-font/css/bpmn-embedded.css' 28 | import transLateGerman from './translationsGerman' 29 | import styles from './style.module.scss' 30 | import './style2.less' 31 | 32 | const { confirm } = Modal 33 | interface IProps { 34 | hidden: boolean 35 | setXml: any 36 | } 37 | const TableList: React.FC = (props: any) => { 38 | const { setXml, hidden } = props 39 | const action = 'create' 40 | const [modeler, setModeler] = useState() 41 | const [modelerInfo, setModelerInfo] = useState({ 42 | name: 'My process', 43 | key: 'myProcess', 44 | }) 45 | const [scale, setScale] = useState(1) 46 | const fileEl = useRef(null) 47 | 48 | const initProcess = () => { 49 | const modelers = new BpmnModeler({ 50 | container: '#modeler-container', 51 | additionalModules: [ 52 | propertiesPanelModule, 53 | propertiesProviderModule, 54 | { 55 | translate: ['value', transLateGerman], 56 | }, 57 | ], 58 | moddleExtensions: { 59 | activiti: activitiModdleDescriptor, 60 | }, 61 | }) 62 | if (action === 'create') { 63 | modelers.importXML(initXML, (error: any) => { 64 | if (error) { 65 | message.info('fail import xml') 66 | } else { 67 | const canvas = modelers.get('canvas') 68 | 69 | canvas.zoom('fit-viewport') 70 | } 71 | }) 72 | } 73 | 74 | setModeler(modelers) 75 | } 76 | 77 | const openFile = () => { 78 | fileEl.current.click() 79 | } 80 | 81 | const openDiagram = (xml: any) => { 82 | modeler.importXML(xml, (err: any) => { 83 | if (err) { 84 | message.error('加载流程数据失败!') 85 | } else { 86 | setXml(xml) 87 | } 88 | }) 89 | } 90 | 91 | const showRealPath = () => { 92 | const selectedFile = fileEl.current.files[0] 93 | // 后缀获取 94 | let suffix = '' 95 | try { 96 | const fileArr = selectedFile.name.split('.') 97 | suffix = fileArr[fileArr.length - 1] 98 | } catch (err) { 99 | suffix = '' 100 | } 101 | if (suffix === '' || (suffix !== 'xml' && suffix !== 'bpmn')) { 102 | message.error('不是有效的流程文件!') 103 | return 104 | } 105 | const reader = new FileReader() 106 | reader.readAsText(selectedFile) 107 | reader.onload = e => { 108 | openDiagram(e.target?.result) 109 | } 110 | // 防止选择同一个文件不执行此方法 111 | fileEl.current.value = null 112 | } 113 | 114 | const saveDiagram = () => { 115 | modeler.saveXML({ format: true }, (err: any, xml: any) => { 116 | if (!err) { 117 | FileSaver.saveAs( 118 | new Blob([xml], { type: 'application/octet-stream' }), 119 | `${modelerInfo.name}.bpmn`, 120 | ) 121 | } 122 | }) 123 | } 124 | const saveSVG = () => { 125 | modeler.saveSVG({ format: true }, (err: any, svg: any) => { 126 | if (!err) { 127 | FileSaver.saveAs( 128 | new Blob([svg], { type: 'application/octet-stream' }), 129 | `${modelerInfo.name}.svg`, 130 | ) 131 | } 132 | }) 133 | } 134 | const handleTabClick = () => { 135 | modeler.saveXML({ format: true }, (err: any, xml: any) => { 136 | if (!err) { 137 | if (setXml) setXml(xml) 138 | } 139 | }) 140 | } 141 | 142 | const undo = () => { 143 | modeler.get('commandStack').undo() 144 | } 145 | const redo = () => { 146 | modeler.get('commandStack').redo() 147 | } 148 | const reset = () => { 149 | confirm({ 150 | title: '提示', 151 | content: '是否确定重置画布到初始状态,重置后将无法还原', 152 | okText: '确定', 153 | cancelText: '取消', 154 | onOk() { 155 | // todo 打开初始文件 156 | openDiagram(initXML) 157 | }, 158 | }) 159 | } 160 | const handleZoom = (radio: any) => { 161 | let newScale = 1.0 162 | if (radio) { 163 | if (scale + radio <= 0.2) { 164 | newScale = 0.2 165 | } else newScale = scale + radio 166 | } 167 | 168 | modeler.get('canvas').zoom(newScale) 169 | setScale(newScale) 170 | } 171 | const resetView = () => { 172 | modeler.get('canvas').zoom('fit-viewport') 173 | } 174 | 175 | const deployBpmn = () => { 176 | /* eslint-disable */ 177 | modeler.saveXML(async(err, xml) => { 178 | const result = await deployBpmnByString({ 179 | fileName: modeler.key + '.bpmn20.xml', 180 | processXml: xml, 181 | }) 182 | if (result.responseCode === '000000') { 183 | message.success('流程部署成功!') 184 | } 185 | }) 186 | } 187 | 188 | const deployConfirm = () => { 189 | confirm({ 190 | title: '提示', 191 | content: `是否确定部署${modelerInfo.name}流程`, 192 | okText: '确定', 193 | cancelText: '取消', 194 | onOk() { 195 | deployBpmn() 196 | }, 197 | }) 198 | } 199 | 200 | useEffect(() => { 201 | initProcess() 202 | }, []) 203 | 204 | useEffect(() => { 205 | if (modeler) { 206 | handleTabClick() 207 | } 208 | }, [modeler, hidden]) 209 | 210 | return ( 211 |
212 |
213 |
214 | 215 |
216 |
    217 |
  • 218 | 219 | 223 | 224 |
  • 225 |
  • 226 | 227 | 230 | 231 |
  • 232 |
  • 233 | 234 | 237 | 238 |
  • 239 |
  • 240 | 241 | 244 | 245 |
  • 246 |
  • 247 | 248 | 251 | 252 |
  • 253 |
  • 254 | 255 | 258 | 259 |
  • 260 |
  • 261 | 262 | 265 | 266 |
  • 267 |
  • 268 | 269 | 272 | 273 |
  • 274 |
  • 275 | 276 | 279 | 280 |
  • 281 |
  • 282 | 283 | 286 | 287 |
  • 288 |
289 |
290 | ) 291 | } 292 | 293 | export default TableList 294 | -------------------------------------------------------------------------------- /src/pages/design/components/translationsGerman.ts: -------------------------------------------------------------------------------- 1 | // @ts-nocheck 2 | const translations = { 3 | // Labels 4 | 'Activate the global connect tool': '激活全局连接工具', 5 | 'Append {type}': '添加 {type}', 6 | 'Add Lane above': '在上面添加道', 7 | 'Divide into two Lanes': '分割成两个道', 8 | 'Divide into three Lanes': '分割成三个道', 9 | 'Add Lane below': '在下面添加道', 10 | 'Append compensation activity': '追加补偿活动', 11 | 'Change type': '修改类型', 12 | 'Connect using Association': '使用关联连接', 13 | 'Connect using Sequence/MessageFlow or Association': '使用顺序/消息流或者关联连接', 14 | 'Connect using DataInputAssociation': '使用数据输入关联连接', 15 | Remove: '移除', 16 | 'Activate the hand tool': '激活抓手工具', 17 | 'Activate the lasso tool': '激活套索工具', 18 | 'Activate the create/remove space tool': '激活创建/删除空间工具', 19 | 'Create expanded SubProcess': '创建扩展子过程', 20 | 'Create IntermediateThrowEvent/BoundaryEvent': '创建中间抛出事件/边界事件', 21 | 'Create Pool/Participant': '创建池/参与者', 22 | 'Parallel Multi Instance': '并行多重事件', 23 | 'Sequential Multi Instance': '时序多重事件', 24 | DataObjectReference: '数据对象参考', 25 | DataStoreReference: '数据存储参考', 26 | Loop: '循环', 27 | 'Ad-hoc': '即席', 28 | 'Create {type}': '创建 {type}', 29 | Task: '任务', 30 | 'Task Listener': '任务监听器', 31 | 'Send Task': '发送任务', 32 | 'Receive Task': '接收任务', 33 | 'User Task': '用户任务', 34 | 'Manual Task': '手工任务', 35 | 'Business Rule Task': '业务规则任务', 36 | 'Service Task': '服务任务', 37 | 'Script Task': '脚本任务', 38 | 'Call Activity': '调用活动', 39 | 'Sub Process (collapsed)': '子流程(折叠的)', 40 | 'Sub Process (expanded)': '子流程(展开的)', 41 | 'Start Event': '开始事件', 42 | StartEvent: '开始事件', 43 | 'Intermediate Throw Event': '中间事件', 44 | 'End Event': '结束事件', 45 | EndEvent: '结束事件', 46 | 'Create Gateway': '创建网关', 47 | 'Create Intermediate/Boundary Event': '创建中间/边界事件', 48 | 'Message Start Event': '消息开始事件', 49 | 'Timer Start Event': '定时开始事件', 50 | 'Conditional Start Event': '条件开始事件', 51 | 'Signal Start Event': '信号开始事件', 52 | 'Error Start Event': '错误开始事件', 53 | 'Escalation Start Event': '升级开始事件', 54 | 'Compensation Start Event': '补偿开始事件', 55 | 'Message Start Event (non-interrupting)': '消息开始事件(非中断)', 56 | 'Timer Start Event (non-interrupting)': '定时开始事件(非中断)', 57 | 'Conditional Start Event (non-interrupting)': '条件开始事件(非中断)', 58 | 'Signal Start Event (non-interrupting)': '信号开始事件(非中断)', 59 | 'Escalation Start Event (non-interrupting)': '升级开始事件(非中断)', 60 | 'Message Intermediate Catch Event': '消息中间捕获事件', 61 | 'Message Intermediate Throw Event': '消息中间抛出事件', 62 | 'Timer Intermediate Catch Event': '定时中间捕获事件', 63 | 'Escalation Intermediate Throw Event': '升级中间抛出事件', 64 | 'Conditional Intermediate Catch Event': '条件中间捕获事件', 65 | 'Link Intermediate Catch Event': '链接中间捕获事件', 66 | 'Link Intermediate Throw Event': '链接中间抛出事件', 67 | 'Compensation Intermediate Throw Event': '补偿中间抛出事件', 68 | 'Signal Intermediate Catch Event': '信号中间捕获事件', 69 | 'Signal Intermediate Throw Event': '信号中间抛出事件', 70 | 'Message End Event': '消息结束事件', 71 | 'Escalation End Event': '定时结束事件', 72 | 'Error End Event': '错误结束事件', 73 | 'Cancel End Event': '取消结束事件', 74 | 'Compensation End Event': '补偿结束事件', 75 | 'Signal End Event': '信号结束事件', 76 | 'Terminate End Event': '终止结束事件', 77 | 'Message Boundary Event': '消息边界事件', 78 | 'Message Boundary Event (non-interrupting)': '消息边界事件(非中断)', 79 | 'Timer Boundary Event': '定时边界事件', 80 | 'Timer Boundary Event (non-interrupting)': '定时边界事件(非中断)', 81 | 'Escalation Boundary Event': '升级边界事件', 82 | 'Escalation Boundary Event (non-interrupting)': '升级边界事件(非中断)', 83 | 'Conditional Boundary Event': '条件边界事件', 84 | 'Conditional Boundary Event (non-interrupting)': '条件边界事件(非中断)', 85 | 'Error Boundary Event': '错误边界事件', 86 | 'Cancel Boundary Event': '取消边界事件', 87 | 'Signal Boundary Event': '信号边界事件', 88 | 'Signal Boundary Event (non-interrupting)': '信号边界事件(非中断)', 89 | 'Compensation Boundary Event': '补偿边界事件', 90 | 'Exclusive Gateway': '互斥网关', 91 | 'Parallel Gateway': '并行网关', 92 | 'Inclusive Gateway': '相容网关', 93 | 'Complex Gateway': '复杂网关', 94 | 'Event based Gateway': '事件网关', 95 | Transaction: '转运', 96 | 'Sub Process': '子流程', 97 | 'Event Sub Process': '事件子流程', 98 | 'Collapsed Pool': '折叠池', 99 | 'Expanded Pool': '展开池', 100 | 101 | // Errors 102 | 'no parent for {element} in {parent}': '在{parent}里,{element}没有父类', 103 | 'no shape type specified': '没有指定的形状类型', 104 | 'flow elements must be children of pools/participants': '流元素必须是池/参与者的子类', 105 | 'out of bounds release': 'out of bounds release', 106 | 'more than {count} child lanes': '子道大于{count} ', 107 | 'element required': '元素不能为空', 108 | 'diagram not part of bpmn:Definitions': '流程图不符合bpmn规范', 109 | 'no diagram to display': '没有可展示的流程图', 110 | 'no process or collaboration to display': '没有可展示的流程/协作', 111 | 'element {element} referenced by {referenced}#{property} not yet drawn': 112 | '由{referenced}#{property}引用的{element}元素仍未绘制', 113 | 'already rendered {element}': '{element} 已被渲染', 114 | 'failed to import {element}': '导入{element}失败', 115 | // 属性面板的参数 116 | Id: '编号', 117 | Name: '名称', 118 | General: '常规', 119 | Details: '详情', 120 | 'Message Name': '消息名称', 121 | Message: '消息', 122 | Initiator: '创建者', 123 | 'Asynchronous Continuations': '持续异步', 124 | 'Asynchronous Before': '异步前', 125 | 'Asynchronous After': '异步后', 126 | 'Job Configuration': '工作配置', 127 | Exclusive: '排除', 128 | 'Job Priority': '工作优先级', 129 | 'Retry Time Cycle': '重试时间周期', 130 | Documentation: '文档', 131 | 'Element Documentation': '元素文档', 132 | 'History Configuration': '历史配置', 133 | 'History Time To Live': '历史的生存时间', 134 | Forms: '表单', 135 | 'Form Key': '表单key', 136 | 'Form Fields': '表单字段', 137 | 'Business Key': '业务key', 138 | 'Form Field': '表单字段', 139 | ID: '编号', 140 | Type: '类型', 141 | Label: '名称', 142 | 'Default Value': '默认值', 143 | Validation: '校验', 144 | 'Add Constraint': '添加约束', 145 | Config: '配置', 146 | Properties: '属性', 147 | 'Add Property': '添加属性', 148 | Value: '值', 149 | Listeners: '监听器', 150 | 'Execution Listener': '执行监听器', 151 | 'Event Type': '事件类型', 152 | 'Listener Type': '监听器类型', 153 | 'Java Class': 'Java类', 154 | Expression: '表达式', 155 | 'Must provide a value': '必须提供一个值', 156 | 'Delegate Expression': '代理表达式', 157 | Script: '脚本', 158 | 'Script Format': '脚本格式', 159 | 'Script Type': '脚本类型', 160 | 'Inline Script': '内联脚本', 161 | 'External Script': '外部脚本', 162 | Resource: '资源', 163 | 'Field Injection': '字段注入', 164 | Extensions: '扩展', 165 | 'Input/Output': '输入/输出', 166 | 'Input Parameters': '输入参数', 167 | 'Output Parameters': '输出参数', 168 | Parameters: '参数', 169 | 'Output Parameter': '输出参数', 170 | 'Timer Definition Type': '定时器定义类型', 171 | 'Timer Definition': '定时器定义', 172 | Date: '日期', 173 | Duration: '持续', 174 | Cycle: '循环', 175 | Signal: '信号', 176 | 'Signal Name': '信号名称', 177 | Escalation: '升级', 178 | Error: '错误', 179 | 'Link Name': '链接名称', 180 | Condition: '条件名称', 181 | 'Variable Name': '变量名称', 182 | 'Variable Event': '变量事件', 183 | 'Specify more than one variable change event as a comma separated list.': 184 | '多个变量事件以逗号隔开', 185 | 'Wait for Completion': '等待完成', 186 | 'Activity Ref': '活动参考', 187 | 'Version Tag': '版本标签', 188 | Executable: '可执行文件', 189 | 'External Task Configuration': '扩展任务配置', 190 | 'Task Priority': '任务优先级', 191 | External: '外部', 192 | Connector: '连接器', 193 | 'Must configure Connector': '必须配置连接器', 194 | 'Connector Id': '连接器编号', 195 | Implementation: '实现方式', 196 | 'Field Injections': '字段注入', 197 | Fields: '字段', 198 | 'Result Variable': '结果变量', 199 | Topic: '主题', 200 | 'Configure Connector': '配置连接器', 201 | 'Input Parameter': '输入参数', 202 | Assignee: '代理人', 203 | 'Candidate Users': '候选用户', 204 | 'Candidate Groups': '候选组', 205 | 'Due Date': '到期时间', 206 | 'Follow Up Date': '跟踪日期', 207 | Priority: '优先级', 208 | // 'The follow up date as an EL expression (e.g. ${someDate} or an ISO date (e.g. 2015-06-26T09:54:00)': 209 | // '跟踪日期必须符合EL表达式,如: ${someDate} ,或者一个ISO标准日期,如:2015-06-26T09:54:00', 210 | // 'The due date as an EL expression (e.g. ${someDate} or an ISO date (e.g. 2015-06-26T09:54:00)': 211 | // '跟踪日期必须符合EL表达式,如: ${someDate} ,或者一个ISO标准日期,如:2015-06-26T09:54:00', 212 | Variables: '变量', 213 | 'This maps to the process definition key.': '流程定义键', 214 | 'Candidate Starter Configuration': '候选人配置', 215 | 'Candidate Starter Groups': '候选人组', 216 | 'Candidate Starter Users': '候选人', 217 | 'Specify more than one group as a comma separated list.': '指定多个组用逗号分隔', 218 | 'Specify more than one user as a comma separated list.': '指定多个用户用逗号分隔', 219 | 'Tasklist Configuration': '任务列表配置', 220 | 'This maps to the task definition key.': '任务定义键' 221 | } 222 | 223 | export default (template, replacements = {}) => { 224 | template = translations[template] || template 225 | return template.replace(/{([^}]+)}/g, (_, key) => { 226 | let str = replacements[key] 227 | if (translations[replacements[key]] != null && translations[replacements[key]] != 'undefined') { 228 | str = translations[replacements[key]] 229 | } 230 | return str || '{' + key + '}' 231 | }) 232 | } 233 | -------------------------------------------------------------------------------- /src/pages/design/components/CustomModeler/Custom/CustomContextPadProvider.ts: -------------------------------------------------------------------------------- 1 | // @ts-nocheck 2 | import { assign, forEach, isArray } from 'lodash' 3 | 4 | import { is } from 'bpmn-js/lib/util/ModelUtil' 5 | 6 | import { isExpanded, isEventSubProcess } from 'bpmn-js/lib/util/DiUtil' 7 | 8 | import { isAny } from 'bpmn-js/lib/features/modeling/util/ModelingUtil' 9 | 10 | import { getChildLanes } from 'bpmn-js/lib/features/modeling/util/LaneUtil' 11 | 12 | import { hasPrimaryModifier } from 'diagram-js/lib/util/Mouse' 13 | 14 | /** 15 | * A provider for BPMN 2.0 elements context pad 16 | */ 17 | export default function ContextPadProvider( 18 | config, 19 | injector, 20 | eventBus, 21 | contextPad, 22 | modeling, 23 | elementFactory, 24 | connect, 25 | create, 26 | popupMenu, 27 | canvas, 28 | rules, 29 | translate 30 | ) { 31 | config = config || {} 32 | 33 | contextPad.registerProvider(this) 34 | 35 | this._contextPad = contextPad 36 | 37 | this._modeling = modeling 38 | 39 | this._elementFactory = elementFactory 40 | this._connect = connect 41 | this._create = create 42 | this._popupMenu = popupMenu 43 | this._canvas = canvas 44 | this._rules = rules 45 | this._translate = translate 46 | 47 | if (config.autoPlace !== false) { 48 | this._autoPlace = injector.get('autoPlace', false) 49 | } 50 | 51 | eventBus.on('create.end', 250, function(event) { 52 | // eslint-disable-next-line one-var 53 | const context = event.context, 54 | shape = context.shape 55 | 56 | if (!hasPrimaryModifier(event) || !contextPad.isOpen(shape)) { 57 | return 58 | } 59 | 60 | var entries = contextPad.getEntries(shape) 61 | 62 | if (entries.replace) { 63 | entries.replace.action.click(event, shape) 64 | } 65 | }) 66 | } 67 | 68 | ContextPadProvider.$inject = [ 69 | 'config.contextPad', 70 | 'injector', 71 | 'eventBus', 72 | 'contextPad', 73 | 'modeling', 74 | 'elementFactory', 75 | 'connect', 76 | 'create', 77 | 'popupMenu', 78 | 'canvas', 79 | 'rules', 80 | 'translate' 81 | ] 82 | 83 | ContextPadProvider.prototype.getContextPadEntries = function(element) { 84 | // eslint-disable-next-line one-var 85 | const contextPad = this._contextPad, 86 | modeling = this._modeling, 87 | elementFactory = this._elementFactory, 88 | connect = this._connect, 89 | create = this._create, 90 | popupMenu = this._popupMenu, 91 | canvas = this._canvas, 92 | rules = this._rules, 93 | autoPlace = this._autoPlace, 94 | translate = this._translate 95 | 96 | const actions = {} 97 | 98 | if (element.type === 'label') { 99 | return actions 100 | } 101 | 102 | const businessObject = element.businessObject 103 | 104 | function startConnect(event, element) { 105 | connect.start(event, element) 106 | } 107 | 108 | function removeElement(e) { 109 | modeling.removeElements([element]) 110 | } 111 | 112 | function getReplaceMenuPosition(element) { 113 | const Y_OFFSET = 5 114 | 115 | // eslint-disable-next-line one-var 116 | const diagramContainer = canvas.getContainer(), 117 | pad = contextPad.getPad(element).html 118 | 119 | // eslint-disable-next-line one-var 120 | const diagramRect = diagramContainer.getBoundingClientRect(), 121 | padRect = pad.getBoundingClientRect() 122 | 123 | const top = padRect.top - diagramRect.top 124 | const left = padRect.left - diagramRect.left 125 | 126 | const pos = { 127 | x: left, 128 | y: top + padRect.height + Y_OFFSET 129 | } 130 | 131 | return pos 132 | } 133 | 134 | /** 135 | * Create an append action 136 | * 137 | * @param {String} type 138 | * @param {String} className 139 | * @param {String} [title] 140 | * @param {Object} [options] 141 | * 142 | * @return {Object} descriptor 143 | */ 144 | function appendAction(type, className?, title?, options?) { 145 | if (typeof title !== 'string') { 146 | options = title 147 | title = translate('Append {type}', { type: type.replace(/^bpmn:/, '') }) 148 | } 149 | 150 | function appendStart(event, element) { 151 | const shape = elementFactory.createShape(assign({ type: type }, options)) 152 | create.start(event, shape, { 153 | source: element 154 | }) 155 | } 156 | 157 | const append = autoPlace 158 | ? function(event, element) { 159 | const shape = elementFactory.createShape(assign({ type: type }, options)) 160 | 161 | autoPlace.append(element, shape) 162 | } 163 | : appendStart 164 | 165 | return { 166 | group: 'model', 167 | className: className, 168 | title: title, 169 | action: { 170 | dragstart: appendStart, 171 | click: append 172 | } 173 | } 174 | } 175 | 176 | function splitLaneHandler(count) { 177 | return function(event, element) { 178 | // actual split 179 | modeling.splitLane(element, count) 180 | 181 | // refresh context pad after split to 182 | // get rid of split icons 183 | contextPad.open(element, true) 184 | } 185 | } 186 | 187 | if (isAny(businessObject, ['bpmn:Lane', 'bpmn:Participant']) && isExpanded(businessObject)) { 188 | const childLanes = getChildLanes(element) 189 | 190 | assign(actions, { 191 | 'lane-insert-above': { 192 | group: 'lane-insert-above', 193 | className: 'bpmn-icon-lane-insert-above', 194 | title: translate('Add Lane above'), 195 | action: { 196 | click: function(event, element) { 197 | modeling.addLane(element, 'top') 198 | } 199 | } 200 | } 201 | }) 202 | 203 | if (childLanes.length < 2) { 204 | if (element.height >= 120) { 205 | assign(actions, { 206 | 'lane-divide-two': { 207 | group: 'lane-divide', 208 | className: 'bpmn-icon-lane-divide-two', 209 | title: translate('Divide into two Lanes'), 210 | action: { 211 | click: splitLaneHandler(2) 212 | } 213 | } 214 | }) 215 | } 216 | 217 | if (element.height >= 180) { 218 | assign(actions, { 219 | 'lane-divide-three': { 220 | group: 'lane-divide', 221 | className: 'bpmn-icon-lane-divide-three', 222 | title: translate('Divide into three Lanes'), 223 | action: { 224 | click: splitLaneHandler(3) 225 | } 226 | } 227 | }) 228 | } 229 | } 230 | 231 | assign(actions, { 232 | 'lane-insert-below': { 233 | group: 'lane-insert-below', 234 | className: 'bpmn-icon-lane-insert-below', 235 | title: translate('Add Lane below'), 236 | action: { 237 | click: function(event, element) { 238 | modeling.addLane(element, 'bottom') 239 | } 240 | } 241 | } 242 | }) 243 | } 244 | 245 | if (is(businessObject, 'bpmn:FlowNode')) { 246 | if (is(businessObject, 'bpmn:EventBasedGateway')) { 247 | assign(actions, { 248 | 'append.receive-task': appendAction('bpmn:ReceiveTask', 'bpmn-icon-receive-task'), 249 | 'append.message-intermediate-event': appendAction( 250 | 'bpmn:IntermediateCatchEvent', 251 | 'bpmn-icon-intermediate-event-catch-message', 252 | translate('Append MessageIntermediateCatchEvent'), 253 | { eventDefinitionType: 'bpmn:MessageEventDefinition' } 254 | ), 255 | 'append.timer-intermediate-event': appendAction( 256 | 'bpmn:IntermediateCatchEvent', 257 | 'bpmn-icon-intermediate-event-catch-timer', 258 | translate('Append TimerIntermediateCatchEvent'), 259 | { eventDefinitionType: 'bpmn:TimerEventDefinition' } 260 | ), 261 | 'append.condition-intermediate-event': appendAction( 262 | 'bpmn:IntermediateCatchEvent', 263 | 'bpmn-icon-intermediate-event-catch-condition', 264 | translate('Append ConditionIntermediateCatchEvent'), 265 | { eventDefinitionType: 'bpmn:ConditionalEventDefinition' } 266 | ), 267 | 'append.signal-intermediate-event': appendAction( 268 | 'bpmn:IntermediateCatchEvent', 269 | 'bpmn-icon-intermediate-event-catch-signal', 270 | translate('Append SignalIntermediateCatchEvent'), 271 | { eventDefinitionType: 'bpmn:SignalEventDefinition' } 272 | ) 273 | }) 274 | } else if ( 275 | isEventType(businessObject, 'bpmn:BoundaryEvent', 'bpmn:CompensateEventDefinition') 276 | ) { 277 | assign(actions, { 278 | 'append.compensation-activity': appendAction( 279 | 'bpmn:Task', 280 | 'bpmn-icon-task', 281 | translate('Append compensation activity'), 282 | { 283 | isForCompensation: true 284 | } 285 | ) 286 | }) 287 | } else if ( 288 | !is(businessObject, 'bpmn:EndEvent') && 289 | !businessObject.isForCompensation && 290 | !isEventType(businessObject, 'bpmn:IntermediateThrowEvent', 'bpmn:LinkEventDefinition') && 291 | !isEventSubProcess(businessObject) 292 | ) { 293 | assign(actions, { 294 | 'append.end-event': appendAction( 295 | 'bpmn:EndEvent', 296 | 'bpmn-icon-end-event-none', 297 | translate('Append EndEvent') 298 | ), 299 | 'append.gateway': appendAction( 300 | 'bpmn:ExclusiveGateway', 301 | 'bpmn-icon-gateway-none', 302 | translate('Append Gateway') 303 | ), 304 | 'append.user-task': appendAction( 305 | 'bpmn:UserTask', 306 | 'bpmn-icon-user-task', 307 | translate('Append UserTask') 308 | ), 309 | 'append.service-task': appendAction( 310 | 'bpmn:ServiceTask', 311 | 'bpmn-icon-service-task', 312 | translate('Append ServiceTask') 313 | ), 314 | 'append.intermediate-event': appendAction( 315 | 'bpmn:IntermediateThrowEvent', 316 | 'bpmn-icon-intermediate-event-none', 317 | translate('Append Intermediate/Boundary Event') 318 | ), 319 | 'append.timer-intermediate-event': appendAction( 320 | 'bpmn:IntermediateCatchEvent', 321 | 'bpmn-icon-intermediate-event-catch-timer', 322 | translate('Append TimerIntermediateCatchEvent'), 323 | { eventDefinitionType: 'bpmn:TimerEventDefinition' } 324 | ) 325 | }) 326 | } 327 | } 328 | 329 | if (!popupMenu.isEmpty(element, 'bpmn-replace')) { 330 | // Replace menu entry 331 | assign(actions, { 332 | replace: { 333 | group: 'edit', 334 | className: 'bpmn-icon-screw-wrench', 335 | title: translate('Change type'), 336 | action: { 337 | click: function(event, element) { 338 | var position = assign(getReplaceMenuPosition(element), { 339 | cursor: { x: event.x, y: event.y } 340 | }) 341 | 342 | popupMenu.open(element, 'bpmn-replace', position) 343 | } 344 | } 345 | } 346 | }) 347 | } 348 | 349 | if ( 350 | isAny(businessObject, [ 351 | 'bpmn:FlowNode', 352 | 'bpmn:InteractionNode', 353 | 'bpmn:DataObjectReference', 354 | 'bpmn:DataStoreReference' 355 | ]) 356 | ) { 357 | assign(actions, { 358 | 'append.text-annotation': appendAction('bpmn:TextAnnotation', 'bpmn-icon-text-annotation'), 359 | 360 | connect: { 361 | group: 'connect', 362 | className: 'bpmn-icon-connection-multi', 363 | title: translate( 364 | 'Connect using ' + 365 | (businessObject.isForCompensation ? '' : 'Sequence/MessageFlow or ') + 366 | 'Association' 367 | ), 368 | action: { 369 | click: startConnect, 370 | dragstart: startConnect 371 | } 372 | } 373 | }) 374 | } 375 | 376 | if (isAny(businessObject, ['bpmn:DataObjectReference', 'bpmn:DataStoreReference'])) { 377 | assign(actions, { 378 | connect: { 379 | group: 'connect', 380 | className: 'bpmn-icon-connection-multi', 381 | title: translate('Connect using DataInputAssociation'), 382 | action: { 383 | click: startConnect, 384 | dragstart: startConnect 385 | } 386 | } 387 | }) 388 | } 389 | 390 | // delete element entry, only show if allowed by rules 391 | let deleteAllowed = rules.allowed('elements.delete', { elements: [element] }) 392 | 393 | if (isArray(deleteAllowed)) { 394 | // was the element returned as a deletion candidate? 395 | deleteAllowed = deleteAllowed[0] === element 396 | } 397 | 398 | if (deleteAllowed) { 399 | assign(actions, { 400 | delete: { 401 | group: 'edit', 402 | className: 'bpmn-icon-trash', 403 | title: translate('Remove'), 404 | action: { 405 | click: removeElement 406 | } 407 | } 408 | }) 409 | } 410 | 411 | return actions 412 | } 413 | 414 | // helpers ///////// 415 | 416 | function isEventType(eventBo, type, definition) { 417 | const isType = eventBo.$instanceOf(type) 418 | let isDefinition = false 419 | 420 | const definitions = eventBo.eventDefinitions || [] 421 | forEach(definitions, function(def: any) { 422 | if (def.$type === definition) { 423 | isDefinition = true 424 | } 425 | }) 426 | 427 | return isType && isDefinition 428 | } 429 | -------------------------------------------------------------------------------- /src/pages/design/components/PropertiesPanel.tsx: -------------------------------------------------------------------------------- 1 | // @ts-nocheck 2 | import React, { useState, useEffect } from 'react' 3 | import { Collapse, Form, Input, Radio, Select } from 'antd' 4 | import { PlusOutlined } from '@ant-design/icons' 5 | import { getAllRoleList, getUserById } from '../service/design' 6 | import cmdHelper from 'bpmn-js-properties-panel/lib/helper/CmdHelper' 7 | import extensionElementsHelper from 'bpmn-js-properties-panel/lib/helper/ExtensionElementsHelper' 8 | import ImplementationTypeHelper from 'bpmn-js-properties-panel/lib/helper/ImplementationTypeHelper' 9 | import Contact from './contact' 10 | import { useForm } from 'antd/lib/form/util' 11 | 12 | interface IProps { 13 | modeler: any 14 | changeModelerObj: (params: any) => void 15 | } 16 | const { Panel } = Collapse 17 | const FormItem = Form.Item 18 | const { TextArea } = Input 19 | const { Option } = Select 20 | const formLayout = { 21 | labelCol: { span: 6 }, 22 | wrapperCol: { span: 18 }, 23 | } 24 | 25 | const TableList: React.FC = (props: any) => { 26 | const { modeler, changeModelerObj } = props 27 | 28 | const [baseForm] = Form.useForm() 29 | const [userForm] = Form.useForm() 30 | const [selectedAssignee, setSelectedAssignee] = useState('') // 代理人 31 | const [element, setElement] = useState(null as any) 32 | const [selectType, setSelectType] = useState('') 33 | const [selectedElements, setSelectedElements] = useState([]) 34 | const [rootElement, setRootElement] = useState(null) 35 | const [isUserTask, setIsUserTask] = useState(null) 36 | const [isServiceTask, setIsServiceTask] = useState(null) 37 | const [sequenceFlow, setSequenceFlow] = useState(null) 38 | const [isGateway, setIsGateway] = useState(null) 39 | const [isExclusiveSequenceFlow, setIsExclusiveSequenceFlow] = useState(null) 40 | const [isStart, setIsStart] = useState(null) 41 | const [isTask, setIsTask] = useState(null) 42 | const [isEvent, setIsEvent] = useState(null) 43 | const [userModalVisible, setUserModalVisible] = useState(false) 44 | const [roleOptions, setRoleOptions] = useState([]) 45 | const [contactValues, setContactValues] = useState({ 46 | name: '', 47 | maxSelectNum: 1, 48 | selectedIds: [], 49 | }) 50 | const [form, setForm] = useState({ 51 | id: null, 52 | name: null, 53 | versionTag: null, 54 | taskPriority: null, 55 | jobPriority: null, 56 | candidateStarterGroups: null, 57 | candidateStarterUsersNames: null, 58 | candidateStarterUsers: null, 59 | historyTimeToLive: null, 60 | // 流程发起人 61 | initiator: null, 62 | description: null, 63 | targetNamespace: null, 64 | async: null, 65 | exclusive: null, 66 | isForCompensation: null, 67 | skipExpression: null, 68 | }) 69 | const [listener, setListener] = useState({} as any) // 监听器 70 | const [taskListener, setTaskListener] = useState({} as any) // 任务监听器 71 | const [isTaskListener, setIsTaskListener] = useState(null) // 任务监听器 72 | const [saveButtonDatas, setSaveButtonDatas] = useState({} as any) // 任务监听器 73 | const [multiInstance, setMultiInstance] = useState({} as any) // 74 | const [saveProperties, setSaveProperties] = useState({} as any) // 75 | const [buttonsData, setButtonDatas] = useState([]) // 按钮 76 | const [listenerData, setListenerData] = useState([]) // 选中的监听器数组 77 | const [taskListenerData, setTaskListenerData] = useState([]) // 选中的监听器数组 78 | const [exclusiveSequence, setExclusiveSequence] = useState({ conditionExpression: undefined }) 79 | const [userTask, setUserTask] = useState({ 80 | assigneeType: 1, 81 | assigneeName: '', 82 | assignee: '', 83 | assigneeId: '', 84 | candidateUsersName: '', 85 | candidateUsers: '', 86 | candidateGroups: [], 87 | formKey: '', 88 | assigneeSelectedArr: [], 89 | candidateUsersSelectedArr: [], 90 | } as any) 91 | const [selectedMultiInstance, setSelectedMultiInstance] = useState({ 92 | isSequential: '', 93 | loopCardinality: '', 94 | collection: '', 95 | elementVariable: '', 96 | completionCondition: '', 97 | asyncBefore: false, 98 | asyncAfter: false, 99 | failedJobRetryTimeCycle: '', 100 | }) 101 | 102 | const setProcessUser = () => { 103 | let userIds = [] 104 | if (form.candidateStarterUsers) { 105 | userIds = form.candidateStarterUsers.split(',') 106 | } 107 | } 108 | 109 | const setButton = businessExtension => { 110 | setSaveButtonDatas(saveButtonDatas[form.id] || {}) 111 | const objValues = JSON.parse(JSON.stringify(saveButtonDatas[form.id] || {})) 112 | const Buttons = [] 113 | const TaskListener = [] 114 | const ExecutionListener = [] 115 | const obj: any = {} 116 | const listenerType = ['class', 'expression', 'delegateExpression'] 117 | businessExtension.filter(item => { 118 | if (item.$type === 'activiti:Button') { 119 | obj.eventType = item.event 120 | listenerType.some(key => { 121 | if (item[key]) { 122 | obj.value = item[key] 123 | obj.listenerType = key 124 | return 125 | } 126 | }) 127 | Buttons.push(item) 128 | } 129 | if (item.$type === 'activiti:TaskListener') { 130 | obj.eventType = item.event 131 | listenerType.some(key => { 132 | if (item[key]) { 133 | obj.value = item[key] 134 | obj.listenerType = key 135 | return 136 | } 137 | }) 138 | TaskListener.push(item) 139 | } 140 | if (item.$type === 'activiti:ExecutionListener') { 141 | obj.eventType = item.event 142 | listenerType.some(key => { 143 | if (item[key]) { 144 | obj.value = item[key] 145 | obj.listenerType = key 146 | return 147 | } 148 | }) 149 | ExecutionListener.push(item) 150 | } 151 | }) 152 | setButtonDatas(objValues.Button || Buttons) 153 | setListenerData(objValues.ExecutionListener || ExecutionListener) 154 | setTaskListenerData(objValues.TaskListener || TaskListener) 155 | } 156 | 157 | // 根据流程保存的信息的设置处理人 158 | const elsetUserTask = async(businessObject) => { 159 | // userForm.setFieldsValue({ ...userTask }) 160 | 161 | if (businessObject.assignee) { 162 | const res = await getUserById(businessObject.assignee) 163 | userTask.assigneeName = res.data.name 164 | userTask.assignee = businessObject.assignee 165 | userTask.assigneeSelectedArr = [{ id: businessObject.assignee }] 166 | } else { 167 | userTask.assigneeName = '' 168 | userTask.assignee = '' 169 | userTask.assigneeSelectedArr = [] 170 | } 171 | userTask.candidateUsersSelectedArr = [] 172 | userTask.candidateUsersName = '' 173 | if (businessObject.candidateUsers) { 174 | const newArr = businessObject.candidateUsers.split(',') 175 | newArr.forEach(async(id, i, arr) => { 176 | userTask.candidateUsersSelectedArr.push({ id: Number(id) }) 177 | const data = await getUserById(id) 178 | if (userTask.candidateUsersName.indexOf(data.data.name) < 0) { 179 | userTask.candidateUsersName += data.data.name 180 | if (i !== arr.length - 1) { 181 | userTask.candidateUsersName += ',' 182 | } 183 | } 184 | }) 185 | } 186 | userTask.candidateGroups = [] 187 | if (businessObject.candidateGroups) { 188 | userTask.candidateGroups = businessObject.candidateGroups 189 | } 190 | setUserTask({ 191 | ...userTask, 192 | assigneeType: 1, 193 | }) 194 | if (userForm) { 195 | userForm.setFieldsValue({...userTask}) 196 | const res = userForm.getFieldsValue() 197 | console.log(567, userTask, res) 198 | } 199 | } 200 | 201 | const setListenerFn = (elements, businessObject) => { 202 | // 执行监听器 203 | setListenerData(listener[elements.id] || []) 204 | if (listener[elements.id]?.length === 0) { 205 | const listeners = 206 | extensionElementsHelper.getExtensionElements( 207 | businessObject, 208 | 'activiti:ExecutionListener', 209 | ) || [] 210 | const mylistenerData = [...listenerData] 211 | for (let i = 0; i < listeners.length; i++) { 212 | const mylistener = listeners[i] 213 | const listenerType = ImplementationTypeHelper.getImplementationType(listener) 214 | mylistenerData.push({ 215 | id: Math.random(16), 216 | eventType: mylistener.event || '', 217 | listenerType, 218 | value: mylistener[listenerType], 219 | }) 220 | setListenerData(mylistenerData) 221 | } 222 | const ELlistener = { ...listener } 223 | ELlistener[element.id] = mylistenerData 224 | setListener(ELlistener) 225 | } 226 | } 227 | 228 | const setTaskListenerFn = (elements, businessObject) => { 229 | setTaskListenerData(taskListener[elements.id] || []) 230 | const mytaskListenerData = [...taskListenerData] 231 | if (taskListener[elements.id]?.length === 0) { 232 | const listeners = 233 | extensionElementsHelper.getExtensionElements(businessObject, 'activiti:TaskListener') || [] 234 | for (let i = 0; i < listeners.length; i++) { 235 | const mylistener = listeners[i] 236 | const listenerType = ImplementationTypeHelper.getImplementationType(mylistener) 237 | mytaskListenerData.push({ 238 | id: Math.random(16), 239 | eventType: mylistener.event || '', 240 | listenerType, 241 | value: mylistener[listenerType], 242 | }) 243 | } 244 | const eltaskListener = { ...taskListener } 245 | eltaskListener[elements.id] = mytaskListenerData 246 | setTaskListener(eltaskListener) 247 | } 248 | } 249 | 250 | // 保存每个节点对应的数据 251 | const saveKeys = (obj, objkeys = 'saveProperties') => { 252 | // 保存右侧属性 253 | const myobj = eval(objkeys) 254 | const ownKeys = myobj?.[form.id] 255 | if (ownKeys) { 256 | myobj[form.id] = { ...myobj[form.id], ...obj } 257 | } else { 258 | myobj[form.id] = obj 259 | } 260 | const fnStr = objkeys.substring(1) 261 | const fnFirstStr = objkeys.substring(0, 1).toUpperCase() 262 | console.log(`set${fnFirstStr}${fnStr}`) 263 | eval(`set${fnFirstStr}${fnStr}`)({ ...myobj }) 264 | } 265 | 266 | const updateProperties = properties => { 267 | const modeling = modeler.get('modeling') 268 | console.log(properties, rootElement) 269 | modeling.updateProperties(element || rootElement, properties) 270 | } 271 | 272 | // 保存处理人数据到流程上 273 | const changeField = (value, type, isUpdate) => { 274 | // const value = event.target.value 275 | // console.log(value, type) 276 | element[type] = value 277 | const properties = {} 278 | properties[type] = value 279 | saveKeys(properties) 280 | if (isUpdate) { 281 | updateProperties(properties) 282 | userTask.candidateGroups = value 283 | setUserTask({ ...userTask }) 284 | } 285 | } 286 | 287 | // 保存处理人 288 | const setAssignee = (res) => { 289 | // console.log(res) 290 | setUserModalVisible(false) 291 | userTask.assignee = res[0].id 292 | userTask.assigneeName = res[0].name 293 | userTask.assigneeSelectedArr = [ 294 | { 295 | id: res[0].id, 296 | }, 297 | ] 298 | updateProperties({ 299 | assignee: userTask.assignee, 300 | }) 301 | setUserTask({ ...userTask }) 302 | console.log(userTask) 303 | userForm.setFieldsValue({ 304 | ...userTask, 305 | }) 306 | saveKeys({ 307 | assigneeName: userTask.assigneeName, 308 | assigneeSelectedArr: userTask.assigneeSelectedArr, 309 | }) 310 | } 311 | 312 | // 保存处理人组 313 | const setCandidateUsers = (res) => { 314 | setUserModalVisible(false) 315 | const users = [] 316 | const usersName = [] 317 | const candidateUsersSelectedArr = [] 318 | for (let i = 0; i < res.length; i++) { 319 | candidateUsersSelectedArr.push({ id: res[i].id }) 320 | users.push(res[i].id) 321 | usersName.push(res[i].name) 322 | } 323 | userTask.candidateUsersSelectedArr = candidateUsersSelectedArr 324 | userTask.candidateUsers = users.join(',') 325 | userTask.candidateUsersName = usersName.join(',') 326 | setUserTask({ ...userTask }) 327 | userForm.setFieldsValue({ 328 | ...userTask, 329 | }) 330 | updateProperties({ 331 | candidateUsers: userTask.candidateUsers, 332 | }) 333 | saveKeys({ 334 | candidateUsersName: userTask.candidateUsersName, 335 | candidateUsersSelectedArr: userTask.candidateUsersSelectedArr, 336 | }) 337 | } 338 | 339 | // 调用处理人选择弹框 340 | const selectAgent = (maxSelectNum, name) => { 341 | setUserModalVisible(true) 342 | setContactValues({ 343 | name, 344 | maxSelectNum, 345 | selectedIds: name === 'assignee' 346 | ? userTask.assigneeSelectedArr 347 | : userTask.candidateUsersSelectedArr, 348 | }) 349 | } 350 | 351 | const handleMultiInstance = val => { 352 | saveKeys({ isSequential: val }, 'multiInstance') 353 | let loopCharacteristics 354 | if (val === '') { 355 | loopCharacteristics = undefined 356 | } else { 357 | const moddle = modeler.get('moddle') 358 | loopCharacteristics = moddle.create('bpmn:MultiInstanceLoopCharacteristics') 359 | if (val === 'Sequential') { 360 | loopCharacteristics.isSequential = true 361 | } 362 | } 363 | updateProperties({ 364 | loopCharacteristics, 365 | }) 366 | } 367 | 368 | const setMultiInstanceFn = businessObject => { 369 | setMultiInstance(multiInstance[form.id] || {}) 370 | const objValues = JSON.parse(JSON.stringify(multiInstance[form.id] || {})) 371 | const keys = [ 372 | 'isSequential', 373 | 'loopCardinality', 374 | 'collection', 375 | 'elementVariable', 376 | 'completionCondition', 377 | ] 378 | keys.forEach(v => { 379 | selectedMultiInstance[v] = objValues[v] 380 | }) 381 | const loopCharacteristics = businessObject.loopCharacteristics 382 | ? businessObject.loopCharacteristics 383 | : {} 384 | 385 | const myselectedMultiInstance = { ...selectedMultiInstance } 386 | 387 | myselectedMultiInstance.collection = objValues.collection || loopCharacteristics.collection 388 | myselectedMultiInstance.elementVariable = 389 | objValues.elementVariable || loopCharacteristics.elementVariable 390 | 391 | if (!myselectedMultiInstance.isSequential) { 392 | if (objValues.isSequential) { 393 | myselectedMultiInstance.isSequential = objValues.isSequential 394 | } else if (loopCharacteristics.loopCardinality) { 395 | myselectedMultiInstance.isSequential = loopCharacteristics.isSequential 396 | ? 'Sequential' 397 | : 'Parallel' 398 | if (!businessObject.loopCharacteristics) { 399 | handleMultiInstance(myselectedMultiInstance.isSequential) 400 | } 401 | } 402 | } 403 | if ( 404 | myselectedMultiInstance.loopCardinality === undefined && 405 | loopCharacteristics.loopCardinality 406 | ) { 407 | myselectedMultiInstance.loopCardinality = loopCharacteristics.loopCardinality.body 408 | } 409 | if ( 410 | myselectedMultiInstance.completionCondition === undefined && 411 | loopCharacteristics.completionCondition 412 | ) { 413 | myselectedMultiInstance.completionCondition = loopCharacteristics.completionCondition.body 414 | } 415 | setSelectedMultiInstance(myselectedMultiInstance) 416 | } 417 | 418 | const setDefaultProperties = elelement => { 419 | if (elelement) { 420 | console.log(99, elelement, isUserTask) 421 | const { businessObject } = elelement 422 | // setUserTask({ 423 | // ...businessObject, 424 | // ...businessObject?.$attrs, 425 | // }) 426 | setForm({ 427 | ...businessObject, 428 | ...businessObject?.$attrs, 429 | targetNamespace: businessObject?.$parent?.targetNamespace, 430 | name: businessObject?.name, 431 | }) 432 | baseForm.setFieldsValue({ 433 | ...businessObject, 434 | ...businessObject?.$attrs, 435 | targetNamespace: businessObject?.$parent?.targetNamespace, 436 | name: businessObject?.name, 437 | }) 438 | if (elelement.type === 'bpmn:Process') { 439 | setProcessUser() 440 | setIsUserTask(false) 441 | setIsStart(false) 442 | setIsTask(false) 443 | } else if (isUserTask || elelement.type === 'bpmn:UserTask') { 444 | // 设置流程人 445 | elsetUserTask(businessObject) 446 | // 设置多实例配置的值 447 | setMultiInstanceFn(businessObject) 448 | const businessExtension = businessObject.extensionElements 449 | ? businessObject.extensionElements.values 450 | : [] 451 | setButton(businessExtension) 452 | } 453 | if (elelement.type === 'bpmn:SequenceFlow' && businessObject.conditionExpression) { 454 | setExclusiveSequence({ 455 | ...exclusiveSequence, 456 | conditionExpression: businessObject.conditionExpression.body, 457 | }) 458 | } 459 | setListenerFn(elelement, businessObject) 460 | setTaskListenerFn(elelement, businessObject) 461 | if (businessObject?.documentation) { 462 | // this.form.description = businessObject.documentation[0].text 463 | } 464 | } 465 | } 466 | 467 | const executeCommand = command => { 468 | const commandStack = modeler.get('commandStack') 469 | console.log(command) 470 | commandStack.execute(command.cmd, command.context) 471 | } 472 | 473 | const updateDocumentation = value => { 474 | const bpmnFactory = modeler.get('bpmnFactory') 475 | if (value) { 476 | const newObjectList = [] 477 | newObjectList.push( 478 | bpmnFactory.create('bpmn:Documentation', { 479 | text: value, 480 | }), 481 | ) 482 | const elements = rootElement 483 | const command = cmdHelper.setList( 484 | elements, 485 | elements.businessObject, 486 | 'documentation', 487 | newObjectList, 488 | ) 489 | executeCommand(command) 490 | } 491 | } 492 | 493 | const handleChangeAssignee = e => { 494 | const { value } = e.target 495 | userTask.assigneeType = value 496 | let assignee = selectedAssignee 497 | if (value === 2) { 498 | assignee = '$INITIATOR' 499 | setSelectedAssignee(userTask.assignee) 500 | } 501 | setUserTask({ ...userTask, assignee }) 502 | updateProperties({ assignee }) 503 | saveKeys({ assigneeType: value }) 504 | } 505 | 506 | const elementChangeEvent = e => { 507 | if (!element) { 508 | return 509 | } 510 | if (e.element.id === element.id) { 511 | setElement(e.element) 512 | setDefaultProperties(e.element) 513 | } 514 | } 515 | const elementClickEvent = e => { 516 | const type = e.element?.type 517 | if (type === 'bpmn:Process') setRootElement(e.element) 518 | if (!element) { 519 | setDefaultProperties(e.element) 520 | } 521 | } 522 | 523 | // 获取所有角色列表 524 | const getUserList = async() => { 525 | const result = await getAllRoleList() 526 | if (result.data) { 527 | setRoleOptions(result.data) 528 | } else setRoleOptions([]) 529 | } 530 | const initFn = () => { 531 | modeler.on('selection.changed', e => { 532 | const { newSelection } = e 533 | setSelectedElements(newSelection) 534 | setElement(newSelection[0]) 535 | setRootElement(null) 536 | setDefaultProperties(newSelection[0]) 537 | // console.log(element, 'change') 538 | }) 539 | modeler.on('element.changed', e => { 540 | elementChangeEvent(e) 541 | }) 542 | modeler.on('element.click', e => { 543 | elementClickEvent(e) 544 | }) 545 | modeler.on('root.added', e => { 546 | elementClickEvent(e) 547 | }) 548 | } 549 | 550 | // 基本设置 551 | const renderContent = () => { 552 | return ( 553 | <> 554 | 559 | { 560 | updateProperties({ id: e.currentTarget.value }) 561 | }} /> 562 | 563 | 568 | { 569 | updateProperties({ name: e.currentTarget.value }) 570 | }} /> 571 | 572 | {rootElement ?
573 | 578 | { 579 | updateProperties({ targetNamespace: e.currentTarget.value }) 580 | }} /> 581 | 582 | 587 | { 588 | updateProperties({ taskPriority: e.currentTarget.value }) 589 | }} /> 590 | 591 | 596 | { 597 | updateProperties({ jobPriority: e.currentTarget.value }) 598 | }} /> 599 | 600 | 605 | { 606 | updateProperties({ historyTimeToLive: e.currentTarget.value }) 607 | }} /> 608 | 609 | 614 |