diff --git a/admin-pro-ui/.gitignore b/admin-pro-ui/.gitignore deleted file mode 100644 index f46b1022..00000000 --- a/admin-pro-ui/.gitignore +++ /dev/null @@ -1,28 +0,0 @@ -# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. - -/.idea - -# dependencies -/node_modules -/.pnp -.pnp.js - -# testing -/coverage - -# production -/build -/dist - -# misc -.DS_Store -.env.local -.env.development.local -.env.test.local -.env.production.local - -npm-debug.log* -yarn-debug.log* -yarn-error.log* -yarn.lock - diff --git a/admin-pro-ui/README.md b/admin-pro-ui/README.md deleted file mode 100644 index 7c2c8ab0..00000000 --- a/admin-pro-ui/README.md +++ /dev/null @@ -1,33 +0,0 @@ -# Admin-ui with Antd For Micro Frontends - -This is a simple React App with Webpack5 & Typescript. - -## Features -1. Support Webpack 5 ModuleFederationPlugin for Micro Frontends -2. Support Dynamic zip component loading -3. Support Dynamic Routing & Dynamic Menu -4. Support Axios for API calls -5. Support Antd & Pro-Components UI Library -6. Support Redux for State Management -7. Support Mock Server for API Mocking -8. Support Monaco Editor for Code Editor -9. Support Access ControlPanel for Menu & Page Components - -## Running -```shell -yarn - -yarn start -``` -## Build -```shell -yarn build -``` - -## Deploy -```shell -cd scripts -sh package.sh -sh deploy.sh -``` - diff --git a/admin-pro-ui/__mocks__/axios.ts b/admin-pro-ui/__mocks__/axios.ts deleted file mode 100644 index b3d0f3d8..00000000 --- a/admin-pro-ui/__mocks__/axios.ts +++ /dev/null @@ -1,37 +0,0 @@ -// __mocks__/axios.ts -const mockAxios = { - create: jest.fn(() => ({ - interceptors: { - request: { - use: jest.fn(), - eject: jest.fn() - }, - response: { - use: jest.fn(), - eject: jest.fn() - } - }, - get: jest.fn(), - post: jest.fn(), - put: jest.fn(), - delete: jest.fn(), - patch: jest.fn() - })), - interceptors: { - request: { - use: jest.fn(), - eject: jest.fn() - }, - response: { - use: jest.fn(), - eject: jest.fn() - } - }, - get: jest.fn(), - post: jest.fn(), - put: jest.fn(), - delete: jest.fn(), - patch: jest.fn() -}; - -export default mockAxios; diff --git a/admin-pro-ui/__mocks__/fileMock.js b/admin-pro-ui/__mocks__/fileMock.js deleted file mode 100644 index 86059f36..00000000 --- a/admin-pro-ui/__mocks__/fileMock.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = 'test-file-stub'; diff --git a/admin-pro-ui/__mocks__/monaco-editor.js b/admin-pro-ui/__mocks__/monaco-editor.js deleted file mode 100644 index 657ce693..00000000 --- a/admin-pro-ui/__mocks__/monaco-editor.js +++ /dev/null @@ -1,9 +0,0 @@ -module.exports = { - editor: { - create: jest.fn(() => ({ - dispose: jest.fn(), - getValue: jest.fn(() => ''), - setValue: jest.fn(), - })), - }, -}; diff --git a/admin-pro-ui/jest.config.ts b/admin-pro-ui/jest.config.ts deleted file mode 100644 index ad3ff041..00000000 --- a/admin-pro-ui/jest.config.ts +++ /dev/null @@ -1,41 +0,0 @@ -import type { Config } from 'jest'; - -const config: Config = { - preset: 'ts-jest', - testEnvironment: 'jsdom', - transform: { - '^.+\\.(ts|tsx)$': ['ts-jest', { - useESM: true, - }], - '^.+\\.(js|jsx|mjs)$': ['babel-jest', { - presets: [ - ['@babel/preset-env', { - targets: { - node: 'current', - }, - }], - '@babel/preset-react', - '@babel/preset-typescript' - ], - }], - }, - moduleNameMapper: { - '^monaco-editor$': '/__mocks__/monaco-editor.js', - "@logicflow": "/node_modules/@logicflow/core/dist/index.min.js", - '\\.(css|less|scss|sass)$': 'identity-obj-proxy', - '\\.(jpg|jpeg|png|gif|webp|svg)$': '/__mocks__/fileMock.js', - '^@/(.*)$': '/src/$1' - }, - setupFilesAfterEnv: ['/src/jest.setup.ts'], - testMatch: [ - "**/__test__/**/*.[jt]s?(x)", - "**/__tests__/**/*.[jt]s?(x)", - "**/?(*.)+(spec|test).[jt]s?(x)" - ], - moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'], - transformIgnorePatterns: [ - 'node_modules/(?!(lodash-es|@ant-design|@logicflow|other-esm-modules)/)' - ], -}; - -export default config; diff --git a/admin-pro-ui/mocks/index.js b/admin-pro-ui/mocks/index.js deleted file mode 100644 index b76c1b92..00000000 --- a/admin-pro-ui/mocks/index.js +++ /dev/null @@ -1,7 +0,0 @@ -const userMock = require('./user'); -const productMock = require('./product'); - -module.exports = (app, helper) => { - userMock(app); - productMock(app); -}; \ No newline at end of file diff --git a/admin-pro-ui/mocks/product.js b/admin-pro-ui/mocks/product.js deleted file mode 100644 index 92a4ddd6..00000000 --- a/admin-pro-ui/mocks/product.js +++ /dev/null @@ -1,16 +0,0 @@ -const Mock = require('mockjs'); - -module.exports = (app, helper) => { - app.get('/api/products', (req, res) => { - - const products = Mock.mock({ - 'list|100': [{ - 'id|+1': 1, - 'name': '@name', - 'price|100-1000': 1, - }] - }).list; - - res.json(products); - }); -}; \ No newline at end of file diff --git a/admin-pro-ui/mocks/user.js b/admin-pro-ui/mocks/user.js deleted file mode 100644 index d8ef8b72..00000000 --- a/admin-pro-ui/mocks/user.js +++ /dev/null @@ -1,28 +0,0 @@ -module.exports = (app, helper) => { - app.post('/user/login', (req, res) => { - const username = req.body.username; - - if(username==='admin'){ - res.json({ - success:true, - data:{ - 'username': username, - 'token':'test token', - 'avatar':'/logo.png', - 'authorities': ['ROLE_ADMIN','ROLE_DEVELOPER'], - } - }); - return; - } - - res.json({ - success:true, - data:{ - 'username': username, - 'token':'test token', - 'avatar':'/logo.png', - 'authorities': ['ROLE_USER'], - } - }); - }); -}; \ No newline at end of file diff --git a/admin-pro-ui/package.json b/admin-pro-ui/package.json deleted file mode 100644 index e3ae9c08..00000000 --- a/admin-pro-ui/package.json +++ /dev/null @@ -1,96 +0,0 @@ -{ - "name": "admin-pro-ui", - "version": "0.1.0", - "private": true, - "dependencies": { - "@ag-grid-community/locale": "^33.0.3", - "@ant-design/icons": "^5.4.0", - "@ant-design/pro-components": "^2.8.2", - "@babel/standalone": "^7.25.6", - "@dnd-kit/core": "^6.2.0", - "@dnd-kit/sortable": "^9.0.0", - "@handsontable/react-wrapper": "^15.0.0", - "@logicflow/core": "^2.0.5", - "@logicflow/extension": "^2.0.9", - "@reduxjs/toolkit": "^2.2.7", - "@types/babel__standalone": "^7.1.7", - "@types/node": "^16.18.108", - "@types/react": "^18.3.5", - "@types/react-dom": "^18.3.0", - "ag-grid-react": "^33.0.3", - "antd": "^5.20.6", - "axios": "^1.7.7", - "base64-js": "^1.5.1", - "handsontable": "^15.0.0", - "jszip": "^3.10.1", - "lodash": "^4.17.21", - "moment": "^2.30.1", - "monaco-editor": "^0.51.0", - "react": "^18.3.1", - "react-dom": "^18.3.1", - "react-markdown": "^10.0.0", - "react-redux": "^9.1.2", - "react-router": "^6.26.2", - "react-router-dom": "^6.26.2", - "remark-gfm": "^4.0.1", - "typescript": "^5.6.2", - "web-vitals": "^2.1.4" - }, - "scripts": { - "start": "webpack serve --config webpack.config.mock.js --open", - "dev": "webpack serve --config webpack.config.dev.js --open", - "build": "webpack --mode production --config webpack.config.prod.js", - "test": "jest", - "test:watch": "jest --watchAll" - }, - "eslintConfig": { - "extends": [ - "react-app" - ] - }, - "browserslist": { - "production": [ - ">0.2%", - "not dead", - "not op_mini all" - ], - "development": [ - "last 1 chrome version", - "last 1 firefox version", - "last 1 safari version" - ] - }, - "devDependencies": { - "@babel/preset-env": "^7.26.0", - "@babel/preset-react": "^7.26.3", - "@babel/preset-typescript": "^7.26.0", - "@testing-library/dom": "^10.4.0", - "@testing-library/jest-dom": "^6.6.3", - "@testing-library/react": "^16.1.0", - "@types/jest": "^29.5.14", - "@types/lodash": "^4.17.7", - "@types/lodash-es": "^4.17.12", - "babel-jest": "^29.7.0", - "clean-webpack-plugin": "^4.0.0", - "copy-webpack-plugin": "^12.0.2", - "css-loader": "^7.1.2", - "express": "^4.21.0", - "html-webpack-plugin": "^5.6.0", - "identity-obj-proxy": "^3.0.0", - "jest": "^29.7.0", - "jest-environment-jsdom": "^29.7.0", - "mockjs": "^1.1.0", - "monaco-editor-webpack-plugin": "^7.1.0", - "sass": "^1.78.0", - "sass-loader": "^16.0.1", - "style-loader": "^4.0.0", - "ts-jest": "^29.2.5", - "ts-loader": "^9.5.1", - "ts-node": "^10.9.2", - "webpack": "^5.94.0", - "webpack-cli": "^5.1.4", - "webpack-dev-server": "^5.1.0", - "webpack-merge": "^6.0.1", - "webpack-mock-server": "^1.0.21" - } -} diff --git a/admin-pro-ui/public/favicon.ico b/admin-pro-ui/public/favicon.ico deleted file mode 100644 index a11777cc..00000000 Binary files a/admin-pro-ui/public/favicon.ico and /dev/null differ diff --git a/admin-pro-ui/public/index.html b/admin-pro-ui/public/index.html deleted file mode 100644 index 12f184e0..00000000 --- a/admin-pro-ui/public/index.html +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - - - Admin-Pro UI - - - -
- - - diff --git a/admin-pro-ui/public/logo.png b/admin-pro-ui/public/logo.png deleted file mode 100644 index fc44b0a3..00000000 Binary files a/admin-pro-ui/public/logo.png and /dev/null differ diff --git a/admin-pro-ui/react-jest.md b/admin-pro-ui/react-jest.md deleted file mode 100644 index 84debe86..00000000 --- a/admin-pro-ui/react-jest.md +++ /dev/null @@ -1,190 +0,0 @@ -# React Unit Test for Jest - -1. dependencies on devDependencies -``` -@testing-library/dom -@testing-library/jest-dom -@testing-library/react -@types/jest -jest -ts-jest -jest-environment-jsdom -identity-obj-proxy -ts-node -``` -2. add jest.config.ts and add jest script in package.json - -``` -//rootDir/jest.config.ts -import type { Config } from 'jest'; - -const config: Config = { - preset: 'ts-jest', - testEnvironment: 'jsdom', - transform: { - '^.+\\.(ts|tsx)$': 'ts-jest', - '^.+\\.(js|jsx)$': 'babel-jest', - }, - moduleNameMapper: { - '\\.(css|less|scss|sass)$': 'identity-obj-proxy', - '\\.(jpg|jpeg|png|gif|webp|svg)$': '/__mocks__/fileMock.js', - '^@/(.*)$': '/src/$1' - }, - setupFilesAfterEnv: ['/src/jest.setup.ts'], - testRegex: '(/__tests__/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$', - moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'] -}; - -export default config; - - -``` - -on jest.config.ts will setting up the jest configuration for the project. -some important configuration is moduleNameMapper, this setting configuration for style,asset,and project path alias. -setupFilesAfterEnv is the file that will be executed before the test run, in this case jest.setup.ts -testRegex is regex for the test file, in this case the test file should be end with .test.tsx or .spec.tsx - - -on `package.json` add jest script -``` -"scripts":{ - "test": "jest", - "test:watch": "jest --watch" - ... other scripts -} -``` - -3. add jest.setup.ts and fileMock.js in src folder -``` -// rootDir/src/jest.setup.ts -import '@testing-library/jest-dom'; -``` - -``` - -// rootDir/__mocks__/fileMock.js -module.exports = 'test-file-stub'; - -``` - -4. create test file in src folder, example my component file in `rootDir/src/pages/welcome/index.tsx` then the test file should be in `rootDir/src/pages/welcome/__tests__/index.test.tsx` -``` - -import React from 'react'; -import Welcome from '../index'; -import {render} from "@testing-library/react"; -import store from "@/store/Redux"; -import {Provider} from "react-redux"; - -// test description for the component -describe('Welcome Component', () => { - // test case for the component - test('renders with default initial value', () => { - // render the component - render( - - - - ); - // get the element that will be tested - const countElement = document.querySelector('.App-header p'); - // assert the element is exist - expect(countElement).toBeInTheDocument(); - // assert the element text content - expect(countElement).toHaveTextContent('hi , Redux counter: 0, Roles:'); - // log the element text content - console.log(countElement?.textContent); - }); -}); - -``` - -5. test router component - -> specify the router path in the test file -``` -import { render, fireEvent } from '@testing-library/react'; -import { MemoryRouter, Routes, Route } from 'react-router-dom'; -import UserProfile from '../UserProfile'; - -describe('UserProfile Component', () => { - test('renders user profile with correct ID', () => { - - // use memory router and setting initial router path - const { getByTestId } = render( - - - } /> - - - ); - - expect(getByTestId('user-id')).toHaveTextContent('User ID: 123'); - }); -}); -``` -> or use `BrowserRouter` and `render` function -``` -import { render, fireEvent } from '@testing-library/react'; -import { createMemoryHistory } from 'history'; -import { Router } from 'react-router-dom'; -import App from '../App'; - -describe('App Navigation', () => { - test('full app navigation', () => { - const history = createMemoryHistory(); - const { getByText } = render( - - - - ); - fireEvent.click(getByText('Go to Profile')); - expect(history.location.pathname).toBe('/profile'); - }); -}); -``` - -6. test axios api calling -``` - -import { render, waitFor } from '@testing-library/react'; -import axios from 'axios'; -import { UserProfile } from '../UserProfile'; - -jest.mock('axios'); -const mockedAxios = axios as jest.Mocked; - -describe('UserProfile Component', () => { - // test case for the component - test('loads all user data correctly - URL based', async () => { - // mock the axios get function - mockedAxios.get.mockImplementation((url) => { - if (url === '/api/users/123') { - return Promise.resolve({ - data: { name: 'John Doe', email: 'john@example.com' } - }); - } - if (url === '/api/users/123/posts') { - return Promise.resolve({ - data: [{ id: 1, title: 'Post 1' }] - }); - } - if (url === '/api/users/123/followers') { - return Promise.resolve({ - data: [{ id: 1, name: 'Follower 1' }] - }); - } - return Promise.reject(new Error('Not found')); - }); - - const { getByTestId } = render(); - - await waitFor(() => { - expect(getByTestId('user-info')).toHaveTextContent('John Doe'); - expect(getByTestId('user-posts')).toHaveTextContent('Post 1'); - expect(getByTestId('user-followers')).toHaveTextContent('Follower 1'); - }); - }); - -``` diff --git a/admin-pro-ui/scripts/.gitignore b/admin-pro-ui/scripts/.gitignore deleted file mode 100644 index 3f20b3b8..00000000 --- a/admin-pro-ui/scripts/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/admin diff --git a/admin-pro-ui/scripts/deploy.sh b/admin-pro-ui/scripts/deploy.sh deleted file mode 100644 index b9802e2f..00000000 --- a/admin-pro-ui/scripts/deploy.sh +++ /dev/null @@ -1,8 +0,0 @@ -serverHost=server -serverAccount=root -serverPort=22 -serverPath=/opt/test/ - - -scp -o ConnectTimeout=30 -P $serverPort -r * $serverAccount@$serverHost:$serverPath -ssh -p $serverPort $serverAccount@$serverHost "cd $serverPath && sed -i 's/\r//g' *.sh && sh install.sh" diff --git a/admin-pro-ui/scripts/docker-compose.yaml b/admin-pro-ui/scripts/docker-compose.yaml deleted file mode 100644 index d4d678cc..00000000 --- a/admin-pro-ui/scripts/docker-compose.yaml +++ /dev/null @@ -1,12 +0,0 @@ -version: "3" - -services: - admin-nginx: - image: nginx:latest - volumes: - - "./admin:/usr/share/nginx/html" - environment: - TZ: "Asia/Shanghai" - restart: always - ports: - - "13000:80" \ No newline at end of file diff --git a/admin-pro-ui/scripts/install.sh b/admin-pro-ui/scripts/install.sh deleted file mode 100644 index 3032008a..00000000 --- a/admin-pro-ui/scripts/install.sh +++ /dev/null @@ -1,3 +0,0 @@ -docker-compose build --no-cache -docker-compose up -d -docker ps -a \ No newline at end of file diff --git a/admin-pro-ui/scripts/package.sh b/admin-pro-ui/scripts/package.sh deleted file mode 100644 index aebf879d..00000000 --- a/admin-pro-ui/scripts/package.sh +++ /dev/null @@ -1,8 +0,0 @@ -rm -rf admin - -cd .. -yarn -yarn run build - - -cp -r ./dist/ ./scripts/admin/ \ No newline at end of file diff --git a/admin-pro-ui/src/api/account.ts b/admin-pro-ui/src/api/account.ts deleted file mode 100644 index d2efb1ec..00000000 --- a/admin-pro-ui/src/api/account.ts +++ /dev/null @@ -1,32 +0,0 @@ -import {get, post} from "@/api/index"; - -export async function login(body: Account.LoginRequest) { - return post('/user/login', body); -} - -export async function captcha() { - return get('/open/captcha'); -} - - -export function clearUser() { - localStorage.removeItem('username'); - localStorage.removeItem('token'); - localStorage.removeItem('authorities'); - localStorage.removeItem('avatar'); -} - -export function initUser(user: { - username: string; - token: string; - authorities: string[]; - avatar: string; -}) { - const {username, token, authorities, avatar} = user; - localStorage.setItem('username', username); - localStorage.setItem('token', token); - if(authorities) { - localStorage.setItem('authorities', JSON.stringify(authorities)); - } - localStorage.setItem('avatar', avatar || "/logo.png"); -} diff --git a/admin-pro-ui/src/api/flow.ts b/admin-pro-ui/src/api/flow.ts deleted file mode 100644 index 5837490e..00000000 --- a/admin-pro-ui/src/api/flow.ts +++ /dev/null @@ -1,165 +0,0 @@ -import {get, page, post} from "@/api/index"; - - -// 流程设计 - -export async function list( - params: any, - sort: any, - filter: any, - match: { - key: string, - type: string - }[] -) { - return page('/api/query/flowWork/list', params, sort, filter, match); -} - - -export async function save(body: any) { - return post('/api/cmd/flowWork/save', body); -} - -export async function copy(id: any) { - return post('/api/cmd/flowWork/copy', {id}); -} - -export async function remove(id: any) { - return post('/api/cmd/flowWork/delete', {id}); -} - -export async function changeState(id: any) { - return post('/api/cmd/flowWork/changeState', {id}); -} - -export async function schema(body: any) { - return post('/api/cmd/flowWork/schema', body); -} - -// 流程控制 - -export async function startFlow(body:any) { - return post('/api/cmd/flowRecord/startFlow', body); -} - -export async function getFlowStep(body:any) { - return post('/api/cmd/flowRecord/getFlowStep', body); -} - -export async function removeFlow(body:any) { - return post('/api/cmd/flowRecord/remove', body); -} - - -export async function detail(id?:any,workCode?:any) { - return get('/api/query/flowRecord/detail', {id,workCode}); -} - -export async function saveFlow(body:any) { - return post('/api/cmd/flowRecord/save', body); -} - -export async function submitFlow(body:any) { - return post('/api/cmd/flowRecord/submitFlow', body); -} - -export async function trySubmitFlow(body:any) { - return post('/api/cmd/flowRecord/trySubmitFlow', body); -} - -export async function custom(body:any) { - return post('/api/cmd/flowRecord/custom', body); -} - -export async function recall(body:any) { - return post('/api/cmd/flowRecord/recall', body); -} - -export async function postponed(body:any) { - return post('/api/cmd/flowRecord/postponed', body); -} - -export async function transfer(body:any) { - return post('/api/cmd/flowRecord/transfer', body); -} - -export async function urge(body:any) { - return post('/api/cmd/flowRecord/urge', body); -} - - -// 待办中心控制 - -export async function flowRecordList(params: any, - sort: any, - filter: any, - match: { - key: string, - type: string - }[]) { - return page('/api/query/flowRecord/list', params, sort, filter, match); -} - - -export async function findAllByOperatorId(params: any, - sort: any, - filter: any, - match: { - key: string, - type: string - }[]) { - return page('/api/query/flowRecord/findAllByOperatorId', params, sort, filter, match); -} - - -export async function findTodoByOperatorId(params: any, - sort: any, - filter: any, - match: { - key: string, - type: string - }[]) { - return page('/api/query/flowRecord/findTodoByOperatorId', params, sort, filter, match); -} - -export async function findDoneByOperatorId(params: any, - sort: any, - filter: any, - match: { - key: string, - type: string - }[]) { - return page('/api/query/flowRecord/findDoneByOperatorId', params, sort, filter, match); -} - - -export async function findInitiatedByOperatorId(params: any, - sort: any, - filter: any, - match: { - key: string, - type: string - }[]) { - return page('/api/query/flowRecord/findInitiatedByOperatorId', params, sort, filter, match); -} - - -export async function findTimeoutTodoByOperatorId(params: any, - sort: any, - filter: any, - match: { - key: string, - type: string - }[]) { - return page('/api/query/flowRecord/findTimeoutTodoByOperatorId', params, sort, filter, match); -} - -export async function findPostponedTodoByOperatorId(params: any, - sort: any, - filter: any, - match: { - key: string, - type: string - }[]) { - return page('/api/query/flowRecord/findPostponedTodoByOperatorId', params, sort, filter, match); -} diff --git a/admin-pro-ui/src/api/index.ts b/admin-pro-ui/src/api/index.ts deleted file mode 100644 index 2488e68c..00000000 --- a/admin-pro-ui/src/api/index.ts +++ /dev/null @@ -1,124 +0,0 @@ -import axios from "axios"; -import {sleep, textToBase64} from "@/utils"; -import {message} from "antd"; - -const api = axios.create({ - timeout: 10000, - headers: { - "Content-Type": "application/json", - }, -}); - -api.interceptors.request.use((config: any) => { - const token = localStorage.getItem("token"); - if (token) { - config.headers = { - Authorization: `${token}`, - } as any; - } - return config; -}, (error: any) => { - return Promise.reject(error); -}); - -api.interceptors.response.use(async (response: any) => { - const headers = response.headers; - const token = headers['authorization']; - - const state = response.status; - if (state === 200) { - if (token) { - console.log('reset token', token); - localStorage.setItem("token", token) - } - - if(response.data) { - const success = response.data.success; - if (!success) { - const errMessage = response.data.errMessage; - const errCode = response.data.errCode; - if ("token.expire" === errCode || "token.error" === errCode) { - message.error('登录已过期,请退出再重新打开'); - await sleep(1500); - localStorage.clear(); - window.location.href = '/#login'; - } else { - if ("login.error" === errCode) { - return response; - } - message.error(errMessage) - } - } - }else { - message.error('抱歉,该账户无权限访问'); - } - } - return response; - }, - (error: any) => { - const response = error.response; - const state = response.data.status; - - if(state === 403){ - message.error('抱歉,该账户无权限访问').then(); - return { - data: { - success: false, - } - } - } - return Promise.reject(error); - } -) - - -export const get = async (url: string, params?: any) => { - try { - const response = await api.get(url, { - params - }); - return response.data; - }catch (e){ - return { - success: false, - } - } -} - -export const post = async (url: string, data: any) => { - try { - const response = await api.post(url, data); - return response.data; - }catch (e){ - return { - success: false, - } - } -} - -export const page = async (url: string, params: any, sort: any, filter: any, match: { - key: string, - type: string -}[]) => { - const response = await get(url, { - ...params, - sort: textToBase64(sort), - filter: textToBase64(filter), - params: textToBase64(match), - }); - - if(response.success){ - const list = response.data.total > 0 ? response.data.list : []; - return { - data: list, - success: response.success, - total: response.data.total - }; - }else{ - return { - data: [], - success: response.success, - total: 0 - } - } -} diff --git a/admin-pro-ui/src/api/jar.ts b/admin-pro-ui/src/api/jar.ts deleted file mode 100644 index 873517dd..00000000 --- a/admin-pro-ui/src/api/jar.ts +++ /dev/null @@ -1,9 +0,0 @@ -import {post} from "@/api/index"; - -export async function upload(body: any) { - return post('/api/jar/upload', body); -} - -export async function restart() { - return post('/api/jar/restart',{}); -} diff --git a/admin-pro-ui/src/api/leave.ts b/admin-pro-ui/src/api/leave.ts deleted file mode 100644 index 5c22bc7f..00000000 --- a/admin-pro-ui/src/api/leave.ts +++ /dev/null @@ -1,20 +0,0 @@ -import {page, post} from "@/api/index"; - -export async function list( - params: any, - sort: any, - filter: any, - match: { - key: string, - type: string - }[] -) { - return page('/api/query/leave/list', params, sort, filter, match); -} - - - -export async function startLeave(body: any) { - return post('/api/cmd/leave/startLeave', body); -} - diff --git a/admin-pro-ui/src/api/node.ts b/admin-pro-ui/src/api/node.ts deleted file mode 100644 index 9c62d9c6..00000000 --- a/admin-pro-ui/src/api/node.ts +++ /dev/null @@ -1,25 +0,0 @@ -import {page, post} from "@/api/index"; - -export async function list( - params: any, - sort: any, - filter: any, - match: { - key: string, - type: string - }[] -) { - return page('/api/node/list', params, sort, filter, match); -} - - -export async function save(body: any) { - return post('/api/node/save', body); -} - - -export async function del(body: { - id: string, -}) { - return post('/api/node/delete', body); -} diff --git a/admin-pro-ui/src/api/salary.ts b/admin-pro-ui/src/api/salary.ts deleted file mode 100644 index 928ae139..00000000 --- a/admin-pro-ui/src/api/salary.ts +++ /dev/null @@ -1,10 +0,0 @@ -export const users = async () => { - const data = []; - for(let i=0;i<500;i++){ - data.push({ - id:i, - name:`张三${i}` - }); - } - return data -} diff --git a/admin-pro-ui/src/api/typings.d.ts b/admin-pro-ui/src/api/typings.d.ts deleted file mode 100644 index 05b40d16..00000000 --- a/admin-pro-ui/src/api/typings.d.ts +++ /dev/null @@ -1,37 +0,0 @@ -// @ts-ignore -/* eslint-disable */ - -declare namespace API { - type CurrentUser = { - username?: string; - authorities?: string[]; - avatar?: string; - }; - - type Response = { - status: string; - success: boolean; - errCode: string; - errMessage: string; - data: T; - } - -} - - -declare namespace Account { - - type LoginResponse = { - token: string; - username: string; - authorities: string[]; - }; - - - type LoginRequest = { - username?: string; - password?: string; - type?: string; - }; -} - diff --git a/admin-pro-ui/src/api/user.ts b/admin-pro-ui/src/api/user.ts deleted file mode 100644 index 08d3ac4e..00000000 --- a/admin-pro-ui/src/api/user.ts +++ /dev/null @@ -1,40 +0,0 @@ -import {page, post,get} from "@/api/index"; - -export async function list( - params: any, - sort: any, - filter: any, - match: { - key: string, - type: string - }[] -) { - return page('/api/query/user/list', params, sort, filter, match); -} - -export async function users() { - return get('/api/query/user/list', {current:1,pageSize:999999}); -} - - -export async function save(body: any) { - return post('/api/cmd/user/save', body); -} - -export async function entrust(body: any) { - return post('/api/cmd/user/entrust', body); -} - -export async function removeEntrust(id: any) { - return post('/api/cmd/user/removeEntrust', {id}); -} - - -export async function changeManager(id: any) { - return post('/api/cmd/user/changeManager', {id}); -} - - -export async function remove(id: any) { - return post('/api/cmd/user/remove', {id}); -} diff --git a/admin-pro-ui/src/api/validate.ts b/admin-pro-ui/src/api/validate.ts deleted file mode 100644 index d40f32f0..00000000 --- a/admin-pro-ui/src/api/validate.ts +++ /dev/null @@ -1,103 +0,0 @@ -import {FormInstance} from "antd/es/form/hooks/useForm"; -import {NamePath} from "rc-field-form/es/interface"; - -// 流程表单API 提供get post的能力 -export interface FlowFormApi { - get: (url: string, params?: any) => Promise; - post: (url: string, data: any) => Promise; -} - -// 流程表单验证内容 -export class FlowFormValidateContent { - readonly value: any; - readonly form: FormInstance; - readonly api?: FlowFormApi - - constructor(value: any, form: FormInstance, api?: FlowFormApi) { - this.value = value; - this.form = form; - this.api = api; - } -} - -// 自定义验证 -export interface FlowFormCustomValidate { - name: NamePath; - validate: (content: FlowFormValidateContent) => Promise; -} - -// 流程表单API上下文 -export class FlowFormApiContext { - - private static readonly instance: FlowFormApiContext = new FlowFormApiContext(); - - private api: FlowFormApi | undefined; - - private constructor() { - this.api = undefined; - } - - public static getInstance() { - return FlowFormApiContext.instance; - } - - public setApi(api: FlowFormApi) { - this.api = api; - } - - public getApi() { - return this.api; - } -} - -// 自定义验证上下文 -export class FlowFormCustomValidateContext { - - private readonly map: Map; - - constructor() { - this.map = new Map(); - } - - public addValidate(validate: FlowFormCustomValidate) { - this.map.set(validate.name, validate); - } - - public addCustomFunctionCodeValidate(namePath:NamePath,validateFuncCode:string){ - const validateFunc = new Function('content', validateFuncCode); - this.addValidate({ - name: namePath, - validate: async (content) => { - return validateFunc(content); - } - }); - } - - public validate(form: FormInstance) { - this.map.values().forEach((validate) => { - const value = form.getFieldValue(validate.name); - const content = new FlowFormValidateContent(value, form, FlowFormApiContext.getInstance().getApi()); - validate.validate(content).then((res) => { - form.setFields( - [ - { - name: validate.name, - errors: res, - } - ] - ) - }).catch((error) => { - form.setFields( - [ - { - name: validate.name, - errors: [error], - } - ] - ) - }); - }); - } -} - - diff --git a/admin-pro-ui/src/assets/logo.svg b/admin-pro-ui/src/assets/logo.svg deleted file mode 100644 index 9dfc1c05..00000000 --- a/admin-pro-ui/src/assets/logo.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/admin-pro-ui/src/components/CodeEditor/index.tsx b/admin-pro-ui/src/components/CodeEditor/index.tsx deleted file mode 100644 index 6a146eac..00000000 --- a/admin-pro-ui/src/components/CodeEditor/index.tsx +++ /dev/null @@ -1,145 +0,0 @@ -import React, {Ref, useEffect, useImperativeHandle} from 'react'; -import * as monaco from 'monaco-editor'; - -export interface CodeEditorAction { - // 重置编辑器的值 - resetValue: (value: string) => void; - // 获取选中的值 - getSelectedValue: () => string; - // 获取编辑器的值 - getValue: () => string; - // 获取编辑器实例 - getEditor:() => monaco.editor.IStandaloneCodeEditor | null; -} - - -interface CodeEditorProps { - language?: string, - value?: string, - onChange?: (value: string) => void, - onSelectedRun?: (value: string) => void; - style?: React.CSSProperties; - readonly?: boolean; - theme?: string; - fontSize?: number; - actionRef?: Ref; -} - - -const CodeEditor: React.FC = (props) => { - const language = props.language || 'javascript'; - const theme = props.theme || "vs-dark"; - const fontSize = props.fontSize || 14; - const [editorId, setEditorId] = React.useState(""); - - const container = React.useRef(null); - - const style = props.style || { - height: "80px", - } - - const codeEditorAction = { - getEditor: ()=>{ - const editors = monaco.editor.getEditors(); - if(editors.length > 0) { - return editors.find(editor => editor.getId() === editorId); - } - return null; - }, - - resetValue: (value: string) => { - const editor = codeEditorAction.getEditor(); - if(editor) { - const position = editor.getPosition(); - editor.setValue(value); - if (position) { - editor.setPosition({ - lineNumber: position.lineNumber, - column: position.column - }); - } - } - }, - getSelectedValue: () => { - const editor = codeEditorAction.getEditor(); - if(editor) { - const selection = editor.getSelection(); - //@ts-ignore - return editor.getModel().getValueInRange(selection); - } - return ""; - }, - getValue: () => { - const editor = codeEditorAction.getEditor(); - if(editor) { - return editor.getValue(); - } - return ""; - } - } as CodeEditorAction; - - useImperativeHandle(props.actionRef, () => (codeEditorAction), [props.actionRef]); - - useEffect(() => { - if(props.value) { - codeEditorAction.resetValue(props.value); - } - }, [props.value]); - - useEffect(() => { - const model = monaco.editor.createModel(props.value || "", language); - const editor = monaco.editor.create( - //@ts-ignore - container?.current, - { - automaticLayout: true, - model: model, - fontSize: fontSize, - theme: theme, - readOnly: props.readonly, - }, - ); - - setEditorId(editor.getId()); - - const subscription = editor.onDidChangeModelContent((event) => { - props.onChange && props.onChange(editor.getValue()); - }); - - let runSelectedCodeActionDispose: monaco.IDisposable | null = null; - if (props.onSelectedRun) { - const runSelectedCodeAction = { - id: "run-code", - label: "Run Selected Code", - contextMenuGroupId: "navigation", - keybindings: [ - monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyR, - monaco.KeyMod.WinCtrl | monaco.KeyCode.KeyR, - ], - run: () => { - const selection = editor.getSelection(); - //@ts-ignore - const selectedText = editor.getModel().getValueInRange(selection); - props.onSelectedRun && props.onSelectedRun(selectedText); - }, - } - runSelectedCodeActionDispose = monaco.editor.addEditorAction(runSelectedCodeAction); - } - - return () => { - editor.dispose(); - subscription.dispose(); - model.dispose(); - - if (runSelectedCodeActionDispose !== null) { - runSelectedCodeActionDispose.dispose(); - } - }; - }, [props.readonly]); - - return ( -
- ); -}; - -export default CodeEditor; diff --git a/admin-pro-ui/src/components/flow/domain/FlowContext.ts b/admin-pro-ui/src/components/flow/domain/FlowContext.ts deleted file mode 100644 index f2fe720c..00000000 --- a/admin-pro-ui/src/components/flow/domain/FlowContext.ts +++ /dev/null @@ -1,25 +0,0 @@ -import FlowPanelContext from "@/components/flow/domain/FlowPanelContext"; - -class FlowContext{ - - private flowPanelContext: FlowPanelContext | null = null; - - private constructor() { - } - - private static instance: FlowContext = new FlowContext(); - - static getInstance(){ - return FlowContext.instance; - } - - setFlowPanelContext(flowPanelContext: FlowPanelContext){ - this.flowPanelContext = flowPanelContext; - } - - getFlowPanelContext(){ - return this.flowPanelContext; - } -} - -export default FlowContext; diff --git a/admin-pro-ui/src/components/flow/domain/FlowPanelContext.ts b/admin-pro-ui/src/components/flow/domain/FlowPanelContext.ts deleted file mode 100644 index 71af0dc9..00000000 --- a/admin-pro-ui/src/components/flow/domain/FlowPanelContext.ts +++ /dev/null @@ -1,528 +0,0 @@ -import React from "react"; -import {LogicFlow} from "@logicflow/core"; -import {CustomButtonType, NodeButtonProperties, NodeProperties, NodeType} from "@/components/flow/types"; -import {message} from "antd"; -import {isEmpty} from "lodash-es"; -import NodeData = LogicFlow.NodeData; -import RegisterConfig = LogicFlow.RegisterConfig; -import GraphConfigData = LogicFlow.GraphConfigData; -import GroovyScript from "@/components/flow/utils/script"; - -// 节点移动距离 -const TRANSLATION_DISTANCE = 40 - -// 逻辑面板上下文 -class FlowPanelContext { - - private readonly lfRef: React.RefObject; - - // 按钮事件选项 - private readonly buttonEventOptions = [ - { - label: "保存", - value: "SAVE" - }, - { - label: "发起", - value: "START" - }, - { - label: "提交", - value: "SUBMIT" - }, - { - label: "预提交", - value: "TRY_SUBMIT" - }, - { - label: "指定人员提交", - value: "SPECIFY_SUBMIT" - }, - { - label: "驳回", - value: "REJECT" - }, - { - label: "转办", - value: "TRANSFER" - }, - { - label: "撤销", - value: "RECALL" - }, - { - label: "延期", - value: "POSTPONED" - }, - { - label: "催办", - value: "URGE" - }, - { - label: "自定义接口", - value: "CUSTOM" - }, - { - label: "自定义事件", - value: "VIEW" - }, - { - label: "删除", - value: "REMOVE" - }, - ] as { - label: string; - value: CustomButtonType; - }[]; - - constructor(lfRef: React.RefObject) { - this.lfRef = lfRef; - } - - /** - * 获取按钮事件选项 - * @param value 事件值 - */ - convertButtonValue = (value: string) => { - return this.buttonEventOptions.find(item => item.value === value)?.label; - } - - /** - * 获取按钮事件选项 - */ - getButtonEventOptions() { - return this.buttonEventOptions; - } - - /** - * 生成uuid - */ - private generateUUID() { - return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { - const r = (Math.random() * 16) | 0; - const v = c === 'x' ? r : (r & 0x3) | 0x8; - return v.toString(16); - }); - } - - /** - * 注册节点 - * @param node - */ - register(node: RegisterConfig) { - this.lfRef.current?.register(node); - } - - /** - * 渲染数据 - * @param data - */ - render(data: GraphConfigData) { - this.lfRef.current?.render(data); - } - - /** - * 获取节点信息 - * @param nodeId 节点id - */ - getNode(nodeId: string) { - const data = this.getGraphData(); - if (data) { - //@ts-ignore - const nodes = data.nodes; - const getNode = (nodeId: string) => { - for (const node of nodes) { - if (node.id === nodeId) { - return node; - } - } - } - return getNode(nodeId); - } - return null; - } - - /** - * 获取节点按钮 - * @param nodeId 节点id - */ - getButtons(nodeId: string) { - const node = this.getNode(nodeId); - if (node) { - const buttons = node.properties.buttons || []; - buttons.sort((a: any, b: any) => { - return a.order - b.order; - }) - return buttons; - } - return [] - } - - - /** - * 删除节点按钮 - * @param nodeId 节点id - * @param buttonId 按钮id - */ - deleteButton(nodeId:string,buttonId:string){ - const data = this.getGraphData(); - if(data) { - //@ts-ignore - const nodes = data.nodes; - const getNode = (nodeId: String) => { - for (const node of nodes) { - if (node.id === nodeId) { - return node; - } - } - } - const node = getNode(nodeId); - const buttons = node.properties.buttons || []; - node.properties.buttons = buttons.filter((item: any) => item.id !== buttonId); - this.render(data as GraphConfigData); - } - } - - /** - * 更新节点按钮数据 - */ - updateButton(nodeId: string, button: NodeButtonProperties) { - const data = this.getGraphData(); - if (data) { - //@ts-ignore - const nodes = data.nodes; - const getNode = (nodeId: String) => { - for (const node of nodes) { - if (node.id === nodeId) { - return node; - } - } - } - const node = getNode(nodeId); - const buttons = node.properties.buttons || []; - let update = false; - for (const item of buttons) { - if (item.id == button.id) { - item.name = button.name; - item.style = button.style; - item.type = button.type; - item.order = button.order; - item.groovy = button.groovy; - item.eventKey = button.eventKey; - update = true; - } - } - if (!update) { - button.id = this.generateUUID(); - node.properties.buttons = [...buttons, button]; - } - this.render(data as GraphConfigData); - } - } - - - /** - * 添加节点 - * @param type 节点类型 - * @param properties 节点属性 - */ - addNode(type: NodeType, properties: NodeProperties) { - if (this.nodeVerify(type)) { - const uid = this.generateUUID(); - this.lfRef.current?.dnd.startDrag({ - id: uid, - type: type, - properties: { - ...properties, - id: uid - } - }) - } - } - - - /** - * 获取节点下的边 - * @param nodeId 节点id - */ - getEdges(nodeId: String) { - const data = this.getGraphData() as any; - const list = [] - if(data) { - const nodes = data.nodes; - const getNodeProperties = (nodeId: String) => { - for (const node of nodes) { - if (node.id === nodeId) { - return node.properties; - } - } - } - - let update = false; - - let order = 0; - if (data && data.edges) { - const edges = data.edges; - for (const index in edges) { - const edge = edges[index]; - if (edge.sourceNodeId === nodeId) { - order++; - if (!edge.properties.outTrigger) { - edge.properties = { - ...edge.properties, - outTrigger: GroovyScript.defaultOutTrigger, - order: order, - back: false - } - update = true; - } - - list.push({ - id: edge.id, - name: edge.properties.name, - source: getNodeProperties(edge.sourceNodeId), - target: getNodeProperties(edge.targetNodeId), - outTrigger: edge.properties.outTrigger, - back: edge.properties.back, - order: edge.properties.order, - edge - }); - } - } - } - - if (update) { - this.render(data); - } - } - - return list; - } - - - /** - * 修改边的名称 - * @param edgeId 边id - * @param name 名称 - */ - changeEdgeName(edgeId:string,name:string){ - const data = this.getGraphData() as any; - if (data && data.edges) { - const edges = data.edges; - for (const index in edges) { - const edge = edges[index]; - if (edge.id === edgeId) { - edge.properties.name = name; - this.render(data); - } - } - } - } - - - /** - * 修改边的排序 - * @param edgeId 边id - * @param order 排序 - */ - changeEdgeOrder(edgeId:string,order:number){ - const data = this.getGraphData() as any; - if (data && data.edges) { - const edges = data.edges; - for (const index in edges) { - const edge = edges[index]; - if (edge.id === edgeId) { - edge.properties.order = order; - this.render(data); - } - } - } - } - - /** - * 修改边的退回属性 - * @param edgeId 边id - * @param back 是否退回 - */ - changeEdgeBack(edgeId:string,back:boolean){ - const data = this.getGraphData() as any; - if (data && data.edges) { - const edges = data.edges; - for (const index in edges) { - const edge = edges[index]; - if (edge.id === edgeId) { - edge.properties.back = back; - this.render(data); - } - } - } - } - - - /** - * 修改边的触发器 - * @param edgeId 边id - * @param outTrigger 触发器 - */ - changeEdgeOutTrigger(edgeId:string,outTrigger:string){ - const data = this.getGraphData() as any; - if (data && data.edges) { - const edges = data.edges; - for (const index in edges) { - const edge = edges[index]; - if (edge.id === edgeId) { - edge.properties.outTrigger = outTrigger; - this.render(data); - } - } - } - } - - - /** - * 复制节点 控制位置的偏移 - * @param nodeData - * @param distance - * @private - */ - private translateNodeData(nodeData: NodeData, distance: number) { - nodeData.x += distance - nodeData.y += distance - - if (!isEmpty(nodeData.text)) { - nodeData.text.x += distance - nodeData.text.y += distance - } - - return nodeData - } - - - /** - * 从粘贴板中复制节点 - */ - copyNode = () => { - const flow = this.lfRef.current; - if (!flow) { - return; - } - const selected = flow.getSelectElements(true); - if (selected && (selected.nodes || selected.edges)) { - flow.clearSelectElements(); - if (selected.nodes) { - const nodes = selected.nodes; - for (const node of nodes) { - if (node.type === 'start-node') { - message.error('开始节点只能有一个').then(); - return false; - } - if (node.type === 'over-node') { - message.error('结束节点只能有一个').then(); - return false; - } - } - const addElements = flow.addElements( - selected, - TRANSLATION_DISTANCE - ); - if (!addElements) return true; - addElements.nodes.forEach((node) => flow.selectElementById(node.id, true)); - addElements.nodes.forEach((node) => { - this.translateNodeData(node, TRANSLATION_DISTANCE); - }); - } - } - return false; - } - - - /** - * 节点校验 - * @param type - */ - private nodeVerify = (type: NodeType) => { - // @ts-ignore - const nodes = this.getGraphData().nodes; - if (type === 'start-node') { - for (let i = 0; i < nodes.length; i++) { - if (nodes[i].type === type) { - message.error('开始节点只能有一个').then(); - return false; - } - } - } - if (type === 'over-node') { - for (let i = 0; i < nodes.length; i++) { - if (nodes[i].type === type) { - message.error('结束节点只能有一个').then(); - return false; - } - } - } - return true; - } - - - /** - * 缩放 - * @param flag true为放大 false为缩小 - */ - zoom = (flag: boolean) => { - this.lfRef.current?.zoom(flag); - } - - /** - * 重置缩放 - */ - resetZoom = () => { - this.lfRef.current?.resetZoom(); - this.lfRef.current?.resetTranslate(); - } - - /** - * 恢复 下一步 - */ - redo = () => { - this.lfRef.current?.redo(); - } - - /** - * 撤销 上一步 - */ - undo = () => { - this.lfRef.current?.undo(); - } - - /** - * 隐藏地图 - */ - hiddenMap = () => { - // @ts-ignore - this.lfRef.current?.extension.miniMap.hide(); - } - - /** - * 显示地图 - */ - showMap = () => { - const modelWidth = this.lfRef.current?.graphModel.width; - // @ts-ignore - this.lfRef.current?.extension.miniMap.show(modelWidth - 300, 200); - } - - /** - * 下载图片 - */ - download = () => { - this.lfRef.current?.getSnapshot(); - } - - /** - * 获取流程设计的数据 - */ - getGraphData() { - return this.lfRef.current?.getGraphData(); - } -} - -export default FlowPanelContext; diff --git a/admin-pro-ui/src/components/flow/index.scss b/admin-pro-ui/src/components/flow/index.scss deleted file mode 100644 index 1dff4c08..00000000 --- a/admin-pro-ui/src/components/flow/index.scss +++ /dev/null @@ -1,115 +0,0 @@ -/* 流程的背景色 */ -$flow-background-color: white; -/* 流程的边框颜色 */ -$flow-box-shadow-color: rgb(228, 224, 219); - -/* 抄送节点的颜色 */ -$flow-circulate-color: #61aa1f; -/* 流程节点的颜色 */ -$flow-node-color: #6855ef; -/* 结束节点的颜色 */ -$flow-over-color: #ea3f21; -/* 开始节点的颜色 */ -$flow-start-color: #b1ad30; - -/* 流程设计器的样式 */ -.flow-design { - border: 1px solid $flow-box-shadow-color; - transition: 0.3s; - padding: 15px 16px; - border-radius: 12px; - position: relative; - - .flow-view { - width: 100%; - height: 85vh; - } -} - - -/* 流程节点的样式 */ -.flow-node { - display: flex; - align-items: center; - max-height: 40px; - border-radius: 12px; - padding: 10px; - position: relative; - - .icon { - margin-left: 10px; - position: absolute; - font-size: 16px; - left: 0; - } - - .code { - margin-left: 20px; - font-size: 14px; - color: #4a5e63; - } - - .title { - margin-left: 5px; - font-size: 14px; - color: black; - } - - - .setting { - margin-right: 10px; - font-size: 14px; - position: absolute; - right: 0; - } - - - .state { - margin-right: 10px; - font-size: 14px; - position: absolute; - right: 0; - } -} - - -.circulate-node { - border: 1px solid $flow-circulate-color; - .icon { - color: $flow-circulate-color; - } - .setting { - color: $flow-circulate-color; - } -} - - -.node-node { - border: 1px solid $flow-node-color; - .icon { - color: $flow-node-color; - } - .setting { - color: $flow-node-color; - } -} - -.over-node { - border: 1px solid $flow-over-color; - .icon { - color: $flow-over-color; - } - .setting { - color: $flow-over-color; - } -} - -.start-node { - border: 1px solid $flow-start-color; - .icon { - color: $flow-start-color; - } - .setting { - color: $flow-start-color; - } -} diff --git a/admin-pro-ui/src/components/flow/index.tsx b/admin-pro-ui/src/components/flow/index.tsx deleted file mode 100644 index 3d0ac497..00000000 --- a/admin-pro-ui/src/components/flow/index.tsx +++ /dev/null @@ -1,125 +0,0 @@ -import React, {useEffect, useRef} from "react"; -import '@logicflow/core/es/index.css'; -import '@logicflow/extension/lib/style/index.css'; -import {LogicFlow} from "@logicflow/core"; -import {DndPanel, Menu, MiniMap, Snapshot} from "@logicflow/extension"; -import Start from "@/components/flow/nodes/Start"; -import Node from "@/components/flow/nodes/Node"; -import Over from "@/components/flow/nodes/Over"; -import Circulate from "@/components/flow/nodes/Circulate"; -import ControlPanel from "@/components/flow/panel/ControlPanel"; -import NodePanel from "@/components/flow/panel/NodePanel"; -import {EdgeType} from "@/components/flow/types"; - -import "./index.scss"; -import FlowPanelContext from "@/components/flow/domain/FlowPanelContext"; -import FlowContext from "@/components/flow/domain/FlowContext"; - -export interface FlowActionType { - getData: () => any; -} - -interface FlowProps { - data?: LogicFlow.GraphConfigData; - actionRef?: React.Ref; - edgeType?: EdgeType; -} - - -const Flow: React.FC = (props) => { - - // 流程图背景颜色 - const FLOW_BACKGROUND_COLOR = '#f3f5f8'; - // 流程图的边颜色 - const FLOW_EDGE_COLOR = '#8f94e3'; - // 流程图的边宽度 - const FLOW_EDGE_STROKE_WIDTH = 1; - - const container = useRef(null); - const lfRef = useRef(null); - const edgeType = props.edgeType || 'polyline'; - - const flowPanelContext = new FlowPanelContext(lfRef); - FlowContext.getInstance().setFlowPanelContext(flowPanelContext); - - if (props.actionRef) { - React.useImperativeHandle(props.actionRef, () => ({ - getData: () => { - return flowPanelContext.getGraphData(); - } - }), [props]); - } - - const data = props?.data || {}; - useEffect(() => { - const SilentConfig = { - isSilentMode: false, - stopScrollGraph: true, - stopMoveGraph: true, - stopZoomGraph: true, - edgeTextEdit: false, - }; - - //@ts-ignore - lfRef.current = new LogicFlow({ - //@ts-ignore - container: container.current, - ...SilentConfig, - background: { - backgroundColor: FLOW_BACKGROUND_COLOR - }, - plugins: [Menu, DndPanel, MiniMap, Snapshot], - grid: false, - keyboard: { - enabled: true, - shortcuts: [ - { - keys: ['ctrl + v', 'cmd + v'], - callback: (e) => { - flowPanelContext.copyNode(); - } - }, - - ] - }, - edgeType: edgeType, - }); - - lfRef.current.setTheme({ - bezier: { - stroke: FLOW_EDGE_COLOR, - strokeWidth: FLOW_EDGE_STROKE_WIDTH, - }, - polyline: { - stroke: FLOW_EDGE_COLOR, - strokeWidth: FLOW_EDGE_STROKE_WIDTH, - }, - line: { - stroke: FLOW_EDGE_COLOR, - strokeWidth: FLOW_EDGE_STROKE_WIDTH, - }, - }); - - flowPanelContext.register(Start); - flowPanelContext.register(Node); - flowPanelContext.register(Over); - flowPanelContext.register(Circulate); - flowPanelContext.render(data); - - lfRef.current.on('node:add', (data) => { - console.log('node:add', data); - }); - - }, []); - - return ( -
- - - -
-
- ) -}; - -export default Flow; diff --git a/admin-pro-ui/src/components/flow/nodes/Circulate/index.tsx b/admin-pro-ui/src/components/flow/nodes/Circulate/index.tsx deleted file mode 100644 index 7416be6c..00000000 --- a/admin-pro-ui/src/components/flow/nodes/Circulate/index.tsx +++ /dev/null @@ -1,140 +0,0 @@ -import {HtmlNode, HtmlNodeModel} from '@logicflow/core'; -import React from "react"; -import ReactDOM from "react-dom/client"; -import {InboxOutlined, SettingFilled} from "@ant-design/icons"; -import CirculateSettingPanel from "@/components/flow/nodes/panel/circulate"; -import StateTag from "@/components/flow/nodes/panel/StateTag"; -import {NodeState} from "@/components/flow/types"; - -type CirculateProperties = { - id: string; - name: string; - code: string; - type: string; - view: string; - operatorMatcher: string; - editable: boolean; - titleGenerator: string; - errTrigger: string; - approvalType: string; - timeout: number; - settingVisible?: boolean; - state?: NodeState; -} - -interface CirculateProps { - name: string; - code?: string; - update?: (values: any) => void; - settingVisible?: boolean; - properties?: CirculateProperties; -} - -export const CirculateView: React.FC = (props) => { - const [visible, setVisible] = React.useState(false); - - const state = props.properties?.state; - - return ( -
- -
- - {props.code && ( - <>({props.code}) - )} - - {props.name} -
- - {props.settingVisible && ( - { - setVisible(true); - }} - /> - )} - - {state && ( -
- -
- )} - - { - props.update && props.update(values); - }} - /> -
- ); -} - -class CirculateModel extends HtmlNodeModel { - setAttributes() { - const name = this.properties.name as string; - this.width = 200 + name.length * 10; - this.height = 45; - this.text.editable = false; - this.menu = []; - - this.sourceRules = [ - { - message: `不允许输出`, - validate: (sourceNode: any, targetNode: any, sourceAnchor, targetAnchor) => { - const edges = this.graphModel.getNodeOutgoingEdge(sourceNode.id); - if (edges.length >= 1) { - return false; - } else { - return true; - } - }, - }, - ]; - - this.anchorsOffset = [ - [this.width / 2, 0], - [0, this.height / 2], - [-this.width / 2, 0], - [0, -this.height / 2], - ]; - } - - -} - -class CirculateNode extends HtmlNode { - setHtml(rootEl: SVGForeignObjectElement) { - const {properties} = this.props.model as HtmlNodeModel; - const div = document.createElement('div'); - const settingVisible = properties.settingVisible !== false; - ReactDOM.createRoot(div).render( - { - this.props.model.setProperties(values); - }}/>, - ); - //需要清空 - rootEl.innerHTML = ''; - rootEl.appendChild(div); - super.setHtml(rootEl); - } -} - -export default { - type: 'circulate-node', - view: CirculateNode, - model: CirculateModel, -}; - diff --git a/admin-pro-ui/src/components/flow/nodes/Node/index.tsx b/admin-pro-ui/src/components/flow/nodes/Node/index.tsx deleted file mode 100644 index 14b01715..00000000 --- a/admin-pro-ui/src/components/flow/nodes/Node/index.tsx +++ /dev/null @@ -1,129 +0,0 @@ -import {HtmlNode, HtmlNodeModel} from '@logicflow/core'; -import React from "react"; -import ReactDOM from "react-dom/client"; -import {PlusCircleFilled, SettingFilled} from "@ant-design/icons"; -import NodeSettingPanel from "@/components/flow/nodes/panel/node"; -import StateTag from "@/components/flow/nodes/panel/StateTag"; -import {NodeState} from "@/components/flow/types"; - -type NodeProperties = { - id: string; - name: string; - code: string; - type: string; - view: string; - operatorMatcher: string; - editable: boolean; - titleGenerator: string; - errTrigger: string; - approvalType: string; - timeout: number; - settingVisible?: boolean; - state?: NodeState; -} - -interface NodeProps { - name: string; - code?: string; - update?: (values: any) => void; - settingVisible?: boolean; - properties?: NodeProperties; - state?: NodeState; -} - -export const NodeView: React.FC = (props) => { - const [visible, setVisible] = React.useState(false); - - const state = props.properties?.state; - - return ( -
- -
- - {props.code && ( - <>({props.code}) - )} - - {props.name} -
- - {props.settingVisible && ( - { - setVisible(true); - }} - /> - )} - - {state && ( -
- -
- )} - - { - props.update && props.update(values); - }} - /> -
- ); -} - -class NodeModel extends HtmlNodeModel { - setAttributes() { - const name = this.properties.name as string; - this.width = 200 + name.length * 10; - this.height = 45; - this.text.editable = false; - this.menu = []; - - this.anchorsOffset = [ - [this.width / 2, 0], - [0, this.height / 2], - [-this.width / 2, 0], - [0, -this.height / 2], - ]; - } - - -} - -class NodeNode extends HtmlNode { - setHtml(rootEl: SVGForeignObjectElement) { - const {properties} = this.props.model as HtmlNodeModel; - const div = document.createElement('div'); - - const settingVisible = properties.settingVisible !== false; - - ReactDOM.createRoot(div).render( - { - this.props.model.setProperties(values); - }}/>, - ); - //需要清空 - rootEl.innerHTML = ''; - rootEl.appendChild(div); - super.setHtml(rootEl); - } -} - -export default { - type: 'node-node', - view: NodeNode, - model: NodeModel, -}; - diff --git a/admin-pro-ui/src/components/flow/nodes/Over/index.tsx b/admin-pro-ui/src/components/flow/nodes/Over/index.tsx deleted file mode 100644 index 0ba1f1f6..00000000 --- a/admin-pro-ui/src/components/flow/nodes/Over/index.tsx +++ /dev/null @@ -1,142 +0,0 @@ -import {HtmlNode, HtmlNodeModel} from '@logicflow/core'; -import React from "react"; -import ReactDOM from "react-dom/client"; -import {CheckCircleFilled, SettingFilled} from "@ant-design/icons"; -import OverSettingPanel from "@/components/flow/nodes/panel/over"; -import StateTag from "@/components/flow/nodes/panel/StateTag"; -import {NodeState} from "@/components/flow/types"; - -type OverProperties = { - id:string; - name: string; - code: string; - type:string; - view: string; - operatorMatcher:string; - editable:boolean; - titleGenerator:string; - errTrigger:string; - approvalType:string; - timeout:number; - settingVisible?: boolean; - state?: NodeState; -} - -interface OverProps { - name: string; - code?: string; - update?: (values: any) => void; - settingVisible?: boolean; - properties?: OverProperties; -} - -export const OverView: React.FC = (props) => { - const [visible, setVisible] = React.useState(false); - - const state = props.properties?.state; - - return ( -
- -
- - {props.code && ( - <>({props.code}) - )} - - {props.name} -
- {props.settingVisible && ( - { - setVisible(true); - }} - /> - )} - - {state && ( -
- -
- )} - - { - props.update && props.update(values); - }} - /> -
- ); -} - -class OverModel extends HtmlNodeModel { - - setAttributes() { - const name = this.properties.name as string; - this.width = 200 + name.length * 10; - this.height = 45; - this.text.editable = false; - this.menu = []; - - this.sourceRules = [ - { - message: `不允许输出`, - validate: (sourceNode, targetNode: any, sourceAnchor, targetAnchor) => { - const edges = this.graphModel.getNodeIncomingEdge(targetNode.id); - if (edges.length >= 0) { - return false; - } else { - return true; - } - }, - }, - ]; - - this.anchorsOffset = [ - // [this.width / 2, 0], - // [0, this.height / 2], - // [-this.width / 2, 0], - [0, -this.height / 2], - ]; - } - - -} - -class OverNode extends HtmlNode { - setHtml(rootEl: SVGForeignObjectElement) { - const {properties} = this.props.model as HtmlNodeModel; - const div = document.createElement('div'); - - const settingVisible = properties.settingVisible !== false; - - ReactDOM.createRoot(div).render( - { - this.props.model.setProperties(values); - }}/>, - ); - //需要清空 - rootEl.innerHTML = ''; - rootEl.appendChild(div); - super.setHtml(rootEl); - } -} - -export default { - type: 'over-node', - view: OverNode, - model: OverModel, -}; - diff --git a/admin-pro-ui/src/components/flow/nodes/Start/index.tsx b/admin-pro-ui/src/components/flow/nodes/Start/index.tsx deleted file mode 100644 index d61aa615..00000000 --- a/admin-pro-ui/src/components/flow/nodes/Start/index.tsx +++ /dev/null @@ -1,126 +0,0 @@ -import {HtmlNode, HtmlNodeModel} from '@logicflow/core'; -import React from "react"; -import ReactDOM from "react-dom/client"; -import {PlayCircleFilled, SettingFilled} from "@ant-design/icons"; -import StartSettingPanel from "@/components/flow/nodes/panel/start"; -import StateTag from "@/components/flow/nodes/panel/StateTag"; -import {NodeState} from "@/components/flow/types"; - -type StartProperties ={ - id:string; - name: string; - code: string; - type:string; - view: string; - operatorMatcher:string; - editable:boolean; - titleGenerator:string; - errTrigger:string; - approvalType:string; - timeout:number; - settingVisible?: boolean; - state?: NodeState; -} - -interface StartProps { - name: string; - code?: string; - settingVisible?: boolean; - update?: (values: any) => void; - properties?: StartProperties; -} - -export const StartView: React.FC = (props) => { - const [visible, setVisible] = React.useState(false); - - const state = props.properties?.state; - - return ( -
- -
- - {props.code && ( - <>({props.code}) - )} - - {props.name} -
- {props.settingVisible && ( - { - setVisible(true); - }} - /> - )} - - {state && ( -
- -
- )} - - { - props.update && props.update(values); - }} - /> -
- ); -} - -class StartModel extends HtmlNodeModel { - setAttributes() { - const name = this.properties.name as string; - this.width = 200 + name.length * 10; - this.height = 45; - this.text.editable = false; - this.menu = []; - - this.anchorsOffset = [ - // [this.width / 2, 0], - [0, this.height / 2], - // [-this.width / 2, 0], - // [0, -this.height / 2], - ]; - } -} - - -class StartNode extends HtmlNode { - - setHtml(rootEl: SVGForeignObjectElement) { - const {properties} = this.props.model as HtmlNodeModel; - const div = document.createElement('div'); - const settingVisible = properties.settingVisible !== false; - - ReactDOM.createRoot(div).render( - { - this.props.model.setProperties(values); - }}/> - ); - //需要清空 - rootEl.innerHTML = ''; - rootEl.appendChild(div); - super.setHtml(rootEl); - } -} - -export default { - type: 'start-node', - view: StartNode, - model: StartModel, -}; - diff --git a/admin-pro-ui/src/components/flow/nodes/panel/ButtonPanel.tsx b/admin-pro-ui/src/components/flow/nodes/panel/ButtonPanel.tsx deleted file mode 100644 index d027b993..00000000 --- a/admin-pro-ui/src/components/flow/nodes/panel/ButtonPanel.tsx +++ /dev/null @@ -1,233 +0,0 @@ -import React from "react"; -import {ActionType, ModalForm, ProColumns, ProForm, ProFormText, ProTable} from "@ant-design/pro-components"; -import {Button, ColorPicker, Popconfirm, Space} from "antd"; -import ScriptModal from "@/components/flow/nodes/panel/ScriptModal"; -import {EyeOutlined} from "@ant-design/icons"; -import FlowContext from "@/components/flow/domain/FlowContext"; -import FormInput from "@/components/form/input"; -import ValidateUtils from "@/components/form/utils"; -import FormSelect from "@/components/form/select"; -import FormColor from "@/components/form/color"; - -interface ButtonPanelProps { - id: string; -} - -const ButtonPanel: React.FC = (props) => { - - const actionRef = React.useRef(); - - const [form] = ProForm.useForm(); - const [groovyForm] = ProForm.useForm(); - - - const [visible, setVisible] = React.useState(false); - - const [scriptVisible, setScriptVisible] = React.useState(false); - - const [type, setType] = React.useState(); - - const flowContext = FlowContext.getInstance(); - - const columns = [ - { - title: 'id', - dataIndex: 'id', - key: 'id', - hidden: true - }, - { - title: '按钮名称', - dataIndex: 'name', - key: 'name', - }, - { - title: '事件类型', - dataIndex: 'type', - key: 'type', - render: (value: string) => { - return flowContext.getFlowPanelContext()?.convertButtonValue(value); - } - }, - { - title: '按钮颜色', - dataIndex: 'style', - key: 'style', - render: (_: any, record: any) => { - return ; - } - }, - { - title: '排序', - dataIndex: 'order', - key: 'order', - }, - { - title: "操作", - valueType: "option", - render: (_: any, record: any) => { - return [ - { - groovyForm.resetFields(); - form.setFieldsValue(record); - setType(record.type); - setVisible(true); - }} - > - 修改 - , - { - flowContext.getFlowPanelContext()?.deleteButton(props.id, record.id); - actionRef.current?.reload(); - }}> - 删除 - - ] - } - } - ] as ProColumns[]; - - return ( - <> - { - const buttons = flowContext?.getFlowPanelContext()?.getButtons(props.id) || []; - return { - data: buttons, - total: buttons.length, - } - }} - toolBarRender={() => { - return [ - - ] - }} - /> - - { - flowContext.getFlowPanelContext()?.updateButton(props.id, values as any); - setVisible(false); - actionRef.current?.reload(); - }} - modalProps={{ - onCancel: () => { - setVisible(false); - }, - onClose:()=>{ - setVisible(false); - }, - destroyOnClose:true - }} - > - - - ) -} - -export default ButtonPanel; diff --git a/admin-pro-ui/src/components/flow/nodes/panel/EdgePanel.tsx b/admin-pro-ui/src/components/flow/nodes/panel/EdgePanel.tsx deleted file mode 100644 index 72cfae8b..00000000 --- a/admin-pro-ui/src/components/flow/nodes/panel/EdgePanel.tsx +++ /dev/null @@ -1,194 +0,0 @@ -import React from "react"; -import {ActionType, ProForm, ProTable} from "@ant-design/pro-components"; -import {Input, InputNumber, Popconfirm, Space} from "antd"; -import {CheckOutlined, EditOutlined, SettingOutlined} from "@ant-design/icons"; -import ScriptModal from "@/components/flow/nodes/panel/ScriptModal"; -import FlowContext from "@/components/flow/domain/FlowContext"; - -interface EdgePanelProps { - id?: string; - type: string; -} - -const EdgePanel: React.FC = (props) => { - - const [visible, setVisible] = React.useState(false); - - const [name, setName] = React.useState(""); - const [order, setOrder] = React.useState(0); - - const [groovyForm] = ProForm.useForm(); - const actionRef = React.useRef(); - - const flowContext = FlowContext.getInstance(); - - const handlerChangeName = (id: any) => { - flowContext.getFlowPanelContext()?.changeEdgeName(id, name); - actionRef.current?.reload(); - } - - const handlerChangeOrder = (id: any) => { - flowContext.getFlowPanelContext()?.changeEdgeOrder(id, order); - actionRef.current?.reload(); - } - - const handlerChangeBack = (id: any, back: boolean) => { - flowContext.getFlowPanelContext()?.changeEdgeBack(id, back); - actionRef.current?.reload(); - } - - const handlerChangeOutTrigger = (id: any, outTrigger: string) => { - flowContext.getFlowPanelContext()?.changeEdgeOutTrigger(id, outTrigger); - actionRef.current?.reload(); - } - - const columns = [ - { - title: 'id', - dataIndex: 'id', - key: 'id', - hidden: true - }, - { - title: '关系名称', - dataIndex: 'name', - key: 'name', - render: (text: string, record: any) => { - return ( - - {record.name ? record.name : "未命名"} - - { - setName(e.target.value); - }}/> - )} - onConfirm={() => { - handlerChangeName(record.id); - }} - > - - - - ) - } - }, - { - title: '关系类型', - dataIndex: 'type', - key: 'relation', - render: (text: string, record: any) => { - return ( - <>{record.source.name} {"->"} {record.target.name} - ) - } - }, - { - title: '出口设置', - dataIndex: 'outTrigger', - key: 'outTrigger', - render: (text: string, record: any) => { - return ( - - { - groovyForm.setFieldValue("script", record.outTrigger); - groovyForm.setFieldValue("type", record.id); - setVisible(true); - }}/> - {record.outTrigger ? () : null} - - ) - } - }, - { - title: '是否退回', - dataIndex: 'back', - hidden: props.type === 'start', - key: 'back', - render: (text: string, record: any) => { - return ( - - - { - handlerChangeBack(record.id, !text); - }} - > - - - - {text ? '是' : '否'} - - - - ) - } - }, - { - title: '排序', - dataIndex: 'order', - key: 'order', - render: (text: string, record: any) => { - return ( - - { - setOrder(e); - }}/>)} - onConfirm={() => { - handlerChangeOrder(record.id); - }} - > - - - - {text} - - ) - } - }, - ] as any[]; - - - return ( - <> - { - const data = props.id ? flowContext.getFlowPanelContext()?.getEdges(props.id) as any[] : []; - return { - data: data.sort((a: any, b: any) => { - return a.order - b.order; - }), - success: true - } - }} - /> - - { - handlerChangeOutTrigger(values.type, values.script); - }} - form={groovyForm} - setVisible={setVisible} - visible={visible}/> - - ) -} -export default EdgePanel; diff --git a/admin-pro-ui/src/components/flow/nodes/panel/NodePanel.tsx b/admin-pro-ui/src/components/flow/nodes/panel/NodePanel.tsx deleted file mode 100644 index 90cf2779..00000000 --- a/admin-pro-ui/src/components/flow/nodes/panel/NodePanel.tsx +++ /dev/null @@ -1,285 +0,0 @@ -import React from "react"; -import {Button, Divider, Form, Space} from "antd"; -import {EyeOutlined, SettingOutlined} from "@ant-design/icons"; -import GroovyScript from "@/components/flow/utils/script"; -import ScriptModal from "@/components/flow/nodes/panel/ScriptModal"; -import {getComponent} from "@/framework/ComponentBus"; -import ValidateUtils from "@/components/form/utils"; -import FormSelect from "@/components/form/select"; -import FormSwitch from "@/components/form/switch"; -import {ProForm} from "@ant-design/pro-components"; -import FormInput from "@/components/form/input"; -import {FormInstance} from "antd/es/form/hooks/useForm"; -import {UserSelectFormProps, UserSelectFormViewKey} from "@/components/flow/types"; - -interface NodePanelProps { - id?: string, - data?: any, - onFinish: (values: any) => void, - form: FormInstance, - type: string, -} - -const NodePanel: React.FC = (props) => { - - const [groovyForm] = ProForm.useForm(); - - const [visible, setVisible] = React.useState(false); - - const [userSelectVisible, setUserSelectVisible] = React.useState(false); - - const [operatorMatcherType, setOperatorMatcherType] = React.useState(props.data?.operatorMatcherType); - - // 用户选人视图 - const UserSelectView = getComponent(UserSelectFormViewKey) as React.ComponentType; - - - return ( - <> -
{ - props.onFinish(values); - }} - > - - 基本信息 - - - - - - - 节点配置 - - -
- ); - }; - callback(Test); -`; - const [code, setCode] = useState(defaultCode); // 直接在代码中返回 Test 组件实例 - - const executeCode = (codeStr: string, mode: string) => { - loadComponent(codeStr, [ - Button, - Input, - Select - ]).then((Component) => { - - setRemoteTestComponent(() => Component); - - if (mode === 'menu') { - addMenu({ - path: '/test', - name: '测试页面', - icon: 'BugFilled', - routes: [ - { - path: '/test/test1', - element: ( - - - - ), - name: '测试页面1', - } - ] - }); - } - }).catch((error) => { - console.error(error); - }); - }; - - - return ( - - - - - - - - - )} - > - - - - - - - - {RemoteTestComponent && ( - }> - - - )} - - - - ); -}; - -export default Test1; diff --git a/admin-ui/src/pages/dynamic/test2.tsx b/admin-ui/src/pages/dynamic/test2.tsx deleted file mode 100644 index d7890bfb..00000000 --- a/admin-ui/src/pages/dynamic/test2.tsx +++ /dev/null @@ -1,205 +0,0 @@ -import React, {Suspense, useEffect, useState} from "react"; -import {Button, Space, Spin} from "antd"; -import {ModalForm, PageContainer, ProForm, ProFormText} from "@ant-design/pro-components"; -import ProFormUploader from "@/components/Form/ProFormUploader"; -import {loadRemoteComponent, loadZipJsFileScript} from "@/utils/dynamicLoader"; -import {useRoutesContext} from "@/framework/Routes/RoutesProvider"; -import Role from "@/pages/role"; - - -const Test2 = () => { - const [RemoteTestComponent, setRemoteTestComponent] = useState | null>(null); - - const {addMenu} = useRoutesContext(); - - const [visible, setVisible] = useState(false); - const [mode, setMode] = useState('zip' as 'zip' | 'menu'); - - const [form] = ProForm.useForm(); - - useEffect(() => { - if(visible){ - form.setFieldsValue({ - scope: "MircoApp", - module: "./Header", - }) - } - }, [visible]); - - const loadComponent = (values: any) => { - return new Promise((resolve, reject) => { - const base64 = values.component; - const scope = values.scope; - const module = values.module; - loadZipJsFileScript(base64).then(() => { - loadRemoteComponent(scope, module).then((ComponentModule: any) => { - const Component = ComponentModule.default || ComponentModule; - resolve(Component); - }).catch(e => { - reject(e); - }); - }); - }); - } - - const handlerLoadComponent = async (values: any) => { - loadComponent(values).then((Component:any) => { - if(Component) { - if (mode === 'menu') { - addMenu({ - path: '/test', - name:'测试页面', - icon: 'BugFilled', - routes:[ - { - path: '/test/test1', - element: ( - - - - ), - name:'测试页面1', - } - ] - }); - }else{ - setRemoteTestComponent(() => Component); - } - } - setVisible(false); - }); - } - - return ( - -
-

动态加载组件

- - {RemoteTestComponent && ( - }> - { - alert('click'); - }} - /> - - )} - - - - - - - - - - - { - setVisible(false); - }, - destroyOnClose: true - }} - onFinish={handlerLoadComponent} - > - -
-
- ) -} - -export default Test2; diff --git a/admin-ui/src/pages/flow/leave/LeaveForm.tsx b/admin-ui/src/pages/flow/leave/LeaveForm.tsx index 718b28fe..8f1cfbc4 100644 --- a/admin-ui/src/pages/flow/leave/LeaveForm.tsx +++ b/admin-ui/src/pages/flow/leave/LeaveForm.tsx @@ -1,132 +1,51 @@ import React, {useEffect} from "react"; -import {ProForm, ProFormDigit, ProFormText, ProFormTextArea} from "@ant-design/pro-components"; -import {EVENT_CLOSE_RESULT_VIEW, EVENT_RELOAD_DATA, FlowFormViewProps} from "@/components/Flow/flow/types"; -import {Button} from "antd"; -import {useSelector} from "react-redux"; -import {FlowReduxState} from "@/components/Flow/store/FlowSlice"; - +import {FlowFormViewProps} from "@codingapi/ui-framework"; +import {Form,FormInput,FormTextArea} from "@codingapi/form-pc"; +import {ValidateUtils} from "@codingapi/ui-framework"; const LeaveForm: React.FC = (props) => { - // 审批意见输入框展示状态 - const opinionEditorVisible = useSelector((state: FlowReduxState) => state.flow.opinionEditorVisible); - - useEffect(() => { - if (props.visible) { - console.log('init props.visible '); - props.form.resetFields(); - props.form.setFieldsValue(props.data); - - // 关闭意见输入框,仅当在开始节点关闭 - if (props.flowData?.getNodeCode() === 'start') { - props.opinionEditorVisible && props.opinionEditorVisible(false); - } else { - props.opinionEditorVisible && props.opinionEditorVisible(true); - } - } - }, [props.visible]); - - const eventKey = props.eventKey; - - const [visible, setVisible] = React.useState(false); - useEffect(() => { - if (eventKey === 'test') { - console.log("点击了自定义事件", eventKey); - setVisible(true); - } - - // 当流程审批反馈结果关闭时,重新加载数据 - if (eventKey == EVENT_CLOSE_RESULT_VIEW && props.flowData?.getNodeCode() === 'start') { - // 重新加载数据 - console.log("重新加载数据"); - props.handlerClick && props.handlerClick({type: "RELOAD"}); + if (props.dataVersion && props.data) { + console.log('data',props.data); + props.form?.setFieldsValue({ + ...props.data + }); } - - if (eventKey == EVENT_RELOAD_DATA) { - props.form.resetFields(); - props.form.setFieldsValue(props.data); - } - }, [eventKey]); - - - useEffect(() => { - setTimeout(() => { - console.log('flowData recordId', props.flowData?.getRecordId()); - }, 100) - }, [props.flowData]); - + }, [props.dataVersion]); return ( - - + ) } diff --git a/admin-ui/src/pages/flow/leave/index.tsx b/admin-ui/src/pages/flow/leave/index.tsx index bfc61a8d..bd1470fb 100644 --- a/admin-ui/src/pages/flow/leave/index.tsx +++ b/admin-ui/src/pages/flow/leave/index.tsx @@ -1,11 +1,9 @@ import React from "react"; -import Page from "@/components/Layout/Page"; import {ActionType, PageContainer, ProTable} from "@ant-design/pro-components"; import {list} from "@/api/leave"; import {Button} from "antd"; -import FlowView from "@/components/Flow/flow"; import LeaveForm from "@/pages/flow/leave/LeaveForm"; - +import {FlowModelView} from "@codingapi/flow-pc"; const LeavePage = () => { @@ -37,37 +35,35 @@ const LeavePage = () => { return ( - - [ - - ]} - columns={columns} - search={false} - rowKey={"id"} - request={async (params, sort, filter) => { - return list(params, sort, filter, []); - }} - /> + [ + + ]} + columns={columns} + search={false} + rowKey={"id"} + request={async (params, sort, filter) => { + return list(params, sort, filter, []); + }} + /> - + - ) } diff --git a/admin-ui/src/pages/flow/record/index.tsx b/admin-ui/src/pages/flow/record/index.tsx index ac873cbe..fada0816 100644 --- a/admin-ui/src/pages/flow/record/index.tsx +++ b/admin-ui/src/pages/flow/record/index.tsx @@ -12,13 +12,13 @@ import { import moment from "moment"; import {message, Tabs} from "antd"; import "./index.scss"; -import FlowView from "@/components/Flow/flow"; import LeaveForm from "@/pages/flow/leave/LeaveForm"; +import {FlowModelView} from "@codingapi/flow-pc"; const FlowRecordPage = () => { const [flowViewVisible, setFlowViewVisible] = React.useState(false); - const [currentId, setCurrentId] = React.useState(0); + const [currentId, setCurrentId] = React.useState(''); const [reviewVisible, setReviewVisible] = React.useState(false); @@ -31,7 +31,7 @@ const FlowRecordPage = () => { const allTodoActionRef = React.useRef(); - const handlerUrgeFlow = (recordId:any) => { + const handlerUrgeFlow = (recordId: any) => { const body = { recordId, } @@ -61,9 +61,9 @@ const FlowRecordPage = () => { { title: '标题', dataIndex: 'title', - render:(value:any,record:any)=>{ + render: (value: any, record: any) => { return ( -
+
); } }, @@ -171,7 +171,7 @@ const FlowRecordPage = () => { ] as any[]; - const reloadTable = ()=>{ + const reloadTable = () => { if (key === 'todo') { todoActionRef.current?.reload(); } @@ -213,7 +213,7 @@ const FlowRecordPage = () => { search={false} columns={columns} rowClassName={(record) => { - return record.read?"record-read":"record-unread"; + return record.read ? "record-read" : "record-unread"; }} request={async (params, sort, filter) => { return findTodoByOperatorId(params, sort, filter, []); @@ -230,7 +230,7 @@ const FlowRecordPage = () => { search={false} columns={columns} rowClassName={(record) => { - return record.read?"record-read":"record-unread"; + return record.read ? "record-read" : "record-unread"; }} request={async (params, sort, filter) => { return findDoneByOperatorId(params, sort, filter, []); @@ -247,7 +247,7 @@ const FlowRecordPage = () => { search={false} columns={columns} rowClassName={(record) => { - return record.read?"record-read":"record-unread"; + return record.read ? "record-read" : "record-unread"; }} request={async (params, sort, filter) => { return findInitiatedByOperatorId(params, sort, filter, []); @@ -264,7 +264,7 @@ const FlowRecordPage = () => { search={false} columns={columns} rowClassName={(record) => { - return record.read?"record-read":"record-unread"; + return record.read ? "record-read" : "record-unread"; }} request={async (params, sort, filter) => { return findTimeoutTodoByOperatorId(params, sort, filter, []); @@ -282,7 +282,7 @@ const FlowRecordPage = () => { search={false} columns={columns} rowClassName={(record) => { - return record.read?"record-read":"record-unread"; + return record.read ? "record-read" : "record-unread"; }} request={async (params, sort, filter) => { return findPostponedTodoByOperatorId(params, sort, filter, []); @@ -299,7 +299,7 @@ const FlowRecordPage = () => { search={false} columns={columns} rowClassName={(record) => { - return record.read?"record-read":"record-unread"; + return record.read ? "record-read" : "record-unread"; }} request={async (params, sort, filter) => { return findAllByOperatorId(params, sort, filter, []); @@ -310,17 +310,16 @@ const FlowRecordPage = () => { ]} /> - - ) } diff --git a/admin-ui/src/pages/flow/user/index.tsx b/admin-ui/src/pages/flow/user/index.tsx index a25b0ab9..d52717ec 100644 --- a/admin-ui/src/pages/flow/user/index.tsx +++ b/admin-ui/src/pages/flow/user/index.tsx @@ -1,19 +1,11 @@ import React, {useRef} from "react"; -import Page from "@/components/Layout/Page"; -import { - ActionType, - ModalForm, - PageContainer, - ProForm, - ProFormSwitch, - ProFormText, - ProTable -} from "@ant-design/pro-components"; +import {ActionType, PageContainer, ProTable} from "@ant-design/pro-components"; import {changeManager, entrust, list, remove, removeEntrust, save} from "@/api/user"; -import {Button, message, Popconfirm, Space} from "antd"; +import {Button, message, Modal, Popconfirm, Space} from "antd"; import {DeleteOutlined, SettingOutlined} from "@ant-design/icons"; import UserSelect from "@/pages/flow/user/select"; - +import {Form,FormInput,FormSwitch} from "@codingapi/form-pc"; +import {ValidateUtils} from "@codingapi/ui-framework"; const UserPage = () => { @@ -23,7 +15,7 @@ const UserPage = () => { const [user, setUser] = React.useState({}); const actionRef = useRef(); - const [form] = ProForm.useForm(); + const form = Form.useForm(); const handleSave = (values: any) => { save(values).then(res => { @@ -168,7 +160,6 @@ const UserPage = () => { return ( - [ @@ -188,67 +179,58 @@ const UserPage = () => { }} /> - { - setVisible(false); - }, - onClose: () => { - setVisible(false); - }, - destroyOnClose: true + onClose={()=>{ + setVisible(false); + }} + onCancel={()=>{ + setVisible(false); }} - onFinish={async (values) => { - handleSave(values); + onOk={async ()=>{ + await form.submit(); }} > - +
{ + handleSave(values); + }} + > +
) } diff --git a/admin-ui/src/pages/flow/work/__test__/index.test.tsx b/admin-ui/src/pages/flow/work/__test__/index.test.tsx deleted file mode 100644 index 30686478..00000000 --- a/admin-ui/src/pages/flow/work/__test__/index.test.tsx +++ /dev/null @@ -1,63 +0,0 @@ -import React from 'react'; -import {act, fireEvent, render, screen, waitFor} from "@testing-library/react"; -import FlowPage from "@/pages/flow/work"; - -jest.mock('axios'); - -// test description for the component -describe('Welcome Component', () => { - - test('renders with default initial value', async () => { - - // render the component - render( - - ); - - const flowTable = screen.getByTestId("flow-table"); - expect(flowTable).toBeInTheDocument(); - - const flowAddBtn = screen.getByTestId("flow-add-btn"); - expect(flowAddBtn).toBeInTheDocument(); - - - await act(async () => { - fireEvent.click(flowAddBtn); - }); - - await waitFor(() => { - const flowEditor = screen.getByTestId("flow-editor"); - expect(flowEditor).toBeInTheDocument(); - }); - - const inputTitle = screen.getByLabelText('标题'); - const inputCode = screen.getByLabelText('编码'); - const inputDescription = screen.getByLabelText('描述'); - const inputPostponedMax = screen.getByLabelText('最大延期次数'); - const inputSkipIfSameApprover = screen.getByLabelText('是否跳过相同审批人'); - const submitButton = screen.getByTestId('flow-editor-submit'); - expect(submitButton).toBeInTheDocument(); - - - await act(async () => { - fireEvent.change(inputTitle, {target: {value: 'test'}}); - expect(inputTitle).toHaveValue('test'); - fireEvent.change(inputCode, {target: {value: 'test'}}); - expect(inputCode).toHaveValue('test'); - fireEvent.change(inputDescription, {target: {value: 'test'}}); - expect(inputDescription).toHaveValue('test'); - fireEvent.change(inputPostponedMax, {target: {value: '1'}}); - expect(inputPostponedMax).toHaveValue('1'); - fireEvent.change(inputSkipIfSameApprover, {target: {value: 'true'}}); - expect(inputSkipIfSameApprover).toHaveValue('true'); - fireEvent.click(submitButton); - }); - - await waitFor(() => { - // todo 不敢展示,却必须得展示才能正常 - const submitButton = screen.getByTestId('flow-editor-submit'); - expect(submitButton).toBeInTheDocument(); - }); - - }); -}); diff --git a/admin-ui/src/pages/flow/work/index.tsx b/admin-ui/src/pages/flow/work/index.tsx index 69d45222..4c0719b2 100644 --- a/admin-ui/src/pages/flow/work/index.tsx +++ b/admin-ui/src/pages/flow/work/index.tsx @@ -1,23 +1,17 @@ import React from "react"; -import Flow, {FlowActionType} from "@/components/Flow"; -import { - ActionType, - ModalForm, - PageContainer, - ProForm, - ProFormDigit, ProFormSwitch, - ProFormText, ProFormTextArea, - ProTable -} from "@ant-design/pro-components"; +import {Flow,FlowActionType} from "@codingapi/flow-pc"; +import {ActionType, PageContainer, ProTable} from "@ant-design/pro-components"; import {changeState, copy, list, remove, save, schema} from "@/api/flow"; -import {Button, Drawer, message, Popconfirm, Space} from "antd"; +import {Button, Drawer, message, Modal, Popconfirm, Space} from "antd"; +import {Form,FormInput,FormTextArea,FormSwitch} from "@codingapi/form-pc"; +import {ValidateUtils} from "@codingapi/ui-framework"; const FlowPage = () => { const [visible, setVisible] = React.useState(false); const [editorVisible, setEditorVisible] = React.useState(false); const flowActionType = React.useRef(null); - const [form] = ProForm.useForm(); + const form = Form.useForm(); const actionRef = React.useRef(); const [current, setCurrent] = React.useState(null); @@ -181,7 +175,7 @@ const FlowPage = () => { data-testid={"flow-add-btn"} type={"primary"} onClick={() => { - form.resetFields(); + form.reset(); setEditorVisible(true); }} >新增 @@ -197,77 +191,68 @@ const FlowPage = () => { }} /> - setEditorVisible(false), - onCancel: () => setEditorVisible(false), + destroyOnClose={true} + onClose={()=>{ + setEditorVisible(false) + }} + onCancel={()=>{ + setEditorVisible(false) }} - submitter={{ - submitButtonProps:{ - "data-testid":"flow-editor-submit", - }, + onOk={async ()=>{ + await form.submit(); }} - onFinish={handlerSave} > - + { - const [form] = Form.useForm(); +const FooterButtons: React.FC<{ formInstance: FormInstance }> = ({formInstance}) => { + const data = { + user: { + name: '张三', + age: 18, + password: '123456', + code: '123', + checkbox: '1,2', + radio: '1', + rate: 3, + slider: 50, + switch: true, + textarea: '这是一段文本', + date: '2021-08-01', + cascader: '1,1-1,1-1-1', + select: '1,2', + avatar: 'c84fb304c180f61bb7db40efef7f85b7', + color: '#000000', + ideCode: 'console.log("hello world")' + } + } return ( - -
-
{ - console.log(values); - }} - > - { - // 自定义格式化值的方式 - return value.join(','); - }} - getValueProps={(value)=>{ - // 自定义设置值的方式,这里是将字符串转为数组 - if(value) { - const arr = value.split(','); - return { - value: arr - }; +
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + +
+ ) +} + +const FormPage = () => { + const leftFormInstance = Form.useForm(); + const rightFormInstance = Form.useForm(); + + const fields = [ + { + type: 'input', + props: { + required: true, + name: ['user', 'name'], + label: '姓名', + placeholder: '请输入姓名', + validateFunction: async (content) => { + const value = content.value; + if (value) { + return [] + } + return ['姓名不能为空'] + } + } + }, + { + type: 'stepper', + props: { + required: true, + name: ['user', 'age'], + label: '年龄', + placeholder: '请输入年龄', + } + }, + { + type: 'password', + props: { + required: true, + name: ['user', 'password'], + label: '银行卡密码', + placeholder: '请输入银行卡密码', + validateFunction: async (content) => { + const value = content.value; + if (value) { + return [] + } + return ['银行卡密码不能为空'] + } + } + }, + { + type: 'captcha', + props: { + required: true, + name: ['user', 'code'], + label: '银行卡验证码', + placeholder: '请输入银行卡验证码', + onCaptchaRefresh: async () => { + console.log('refresh captcha') + return { + url: '/captcha.jpeg', + code: '123' + } + } + } + }, + { + type: 'checkbox', + props: { + required: true, + name: ['user', 'checkbox'], + label: '复选框', + options: [ + {label: '选项1', value: '1'}, + {label: '选项2', value: '2'}, + {label: '选项3', value: '3'}, + ] + } + }, + { + type: 'radio', + props: { + required: true, + name: ['user', 'radio'], + label: '单选框', + options: [ + {label: '选项1', value: '1'}, + {label: '选项2', value: '2'}, + {label: '选项3', value: '3'}, + ] + } + }, + { + type: 'rate', + props: { + required: true, + name: ['user', 'rate'], + label: '评分', + } + }, + { + type: 'slider', + props: { + required: true, + name: ['user', 'slider'], + label: '滑块', + sliderPopover: true + } + }, + { + type: 'switch', + props: { + required: true, + name: ['user', 'switch'], + label: '开关', + } + }, + { + type: 'textarea', + props: { + required: true, + name: ['user', 'textarea'], + label: '文本域', + } + }, + { + type: 'date', + props: { + required: true, + name: ['user', 'date'], + label: '日期', + } + }, + { + type: 'cascader', + props: { + required: true, + name: ['user', 'cascader'], + label: '级联选择', + options: [ + { + label: '选项1', + value: '1', + children: [ + { + label: '选项1-1', + value: '1-1', + children: [ + { + label: '选项1-1-1', + value: '1-1-1', + }, + { + label: '选项1-1-2', + value: '1-1-2', + }, + ] + }, + { + label: '选项1-2', + value: '1-2', + }, + ] + }, + { + label: '选项2', + value: '2', + children: [ + { + label: '选项2-1', + value: '2-1', + }, + { + label: '选项2-2', + value: '2-2', + }, + ] + }, + ] + } + }, + { + type: 'select', + props: { + required: true, + name: ['user', 'select'], + label: '选择器', + selectMultiple: true, + options: [ + {label: '选项1', value: '1'}, + {label: '选项2', value: '2'}, + {label: '选项3', value: '3'}, + ] + } + }, + { + type: 'uploader', + props: { + required: true, + name: ['user', 'avatar'], + label: '头像', + } + }, + { + type: 'color', + props: { + required: true, + name: ['user', 'color'], + label: '颜色', + } + }, + { + type: 'code', + props: { + required: true, + name: ['user', 'ideCode'], + label: '代码', + } + }, + ] as FormField[]; + + return ( + + + + + )} > - {/* - 对于下属组件,需要提供以下属性: - value: 1 - onChange: (value: any) => void - 通过这两个属性就可以实现双向绑定 - */} - - // } - }, - { - title: '科目1', - dataIndex: 'kemu1', - valueType: 'digit', - editable: true, - // render: (text, record) => { - // return { - // const data = { - // ...record, - // kemu1: parseFloat(e.target.value) - // } - // table2UpdateService.onChange(data); - // }} - // /> - // } - }, - { - title: '科目2', - dataIndex: 'kemu2', - valueType: 'digit', - editable: true, - // render: (text, record) => { - // return { - // const data = { - // ...record, - // kemu2: parseFloat(e.target.value ) - // } - // console.log(data) - // table2UpdateService.onChange(data); - // }} - // /> - // } - }, - { - title: '小计', - dataIndex: 'sum', - valueType: 'text', - editable: true, - // render: (text, record) => { - // return - // } - } - ] as ProColumns[]; - - - for(let i=0;i<30;i++){ - columns.push( - { - title: '科目21'+i, - dataIndex: 'kemu2'+i, - //@ts-ignore - valueType: 'digit'+i, - // render: (text, record) => { - // return { - // const data = { - // ...record, - // kemu2: parseFloat(e.target.value ) - // } - // console.log(data) - // table2UpdateService.onChange(data); - // }} - // /> - // } - }, - ) - } - - return ( - <> - {/* {*/} - {/* console.log('reload table2')*/} - {/* return {*/} - {/* data: table2,*/} - {/* success: true*/} - {/* }*/} - {/* }}*/} - {/*/>*/} - - - ) -} - -export default SalaryTable2; diff --git a/admin-ui/src/pages/salary/data/table1.ts b/admin-ui/src/pages/salary/data/table1.ts deleted file mode 100644 index cbfc2ee7..00000000 --- a/admin-ui/src/pages/salary/data/table1.ts +++ /dev/null @@ -1,58 +0,0 @@ -import {Table1, Table2, User} from "@/pages/salary/types"; -import {SalaryStore} from "@/pages/salary/store/salary"; - -export class Table1UpdateService { - - private readonly data: Table1[]; - private readonly update: (data: Table1[]) => void; - private readonly reload: () => void; - - - constructor(store: SalaryStore, update: (data: Table1[]) => void, reload: () => void) { - this.data = store.table1; - this.update = update; - this.reload = reload; - - } - - - public initData(users: User[]) { - const t1 = new Date().getTime(); - const data = users.map((user) => { - return { - id: user.id, - name: user.name, - jxgz: 0, - sum: 0 - } - }); - this.updateData(data); - const t2 = new Date().getTime(); - console.log("initData", t2 - t1); - } - - - public reloadData(table2: Table2[]) { - const list = this.data.map((item) => { - const row = table2.find((row) => row.id === item.id); - if (row) { - if (item.id === row.id) { - return { - ...item, - jxgz: row.sum, - sum: row.sum - } - } - } - return item; - }); - this.updateData(list); - } - - - private updateData(data: Table1[]) { - this.update(data); - this.reload(); - } - -} diff --git a/admin-ui/src/pages/salary/data/table2.ts b/admin-ui/src/pages/salary/data/table2.ts deleted file mode 100644 index 2bf91c12..00000000 --- a/admin-ui/src/pages/salary/data/table2.ts +++ /dev/null @@ -1,57 +0,0 @@ -import {Table2, User} from "@/pages/salary/types"; - -export class Table2UpdateService { - - private data: Table2[]; - private readonly update: (data: Table2[]) => void; - private readonly reload: () => void; - private readonly updateVersion: (version: number) => void; - - constructor(table2: Table2[], update: (data: Table2[]) => void, reload: () => void, updateVersion: (version: number) => void) { - this.data = table2; - this.update = update; - this.reload = reload; - this.updateVersion = updateVersion; - } - - - public initData(users: User[]) { - const data = users.map((user) => { - return { - id: user.id, - name: user.name, - kemu1: 0, - kemu2: 0, - sum: 0 - } - }); - this.updateData(data); - } - - - public onChange(row: Table2) { - const t1 = new Date().getMilliseconds(); - const sum = row.kemu1 + row.kemu2; - const list = this.data.map((item) => { - if (item.id === row.id) { - return { - ...item, - ...row, - sum - } - } - return item; - }); - console.log(sum); - this.updateData(list); - const t2 = new Date().getMilliseconds(); - console.log("onChange", t2 - t1); - } - - - private updateData(data: Table2[]) { - this.update(data); - this.reload(); - this.updateVersion(Math.random()); - } -} diff --git a/admin-ui/src/pages/salary/index.tsx b/admin-ui/src/pages/salary/index.tsx deleted file mode 100644 index b17754d2..00000000 --- a/admin-ui/src/pages/salary/index.tsx +++ /dev/null @@ -1,82 +0,0 @@ -import React, {useEffect, useState} from "react"; -import {Provider, useDispatch} from "react-redux"; -import {salaryStore, updateUsers} from "@/pages/salary/store/salary"; -import {PageContainer} from "@ant-design/pro-components"; -import {Tabs} from "antd"; -import SalaryTable1 from "@/pages/salary/compoments/SalaryTable1"; -import SalaryTable2 from "@/pages/salary/compoments/SalaryTable2"; -import {users} from "@/api/salary"; - - -const $Salary = () => { - - const items = [ - { - key: "1", - label: "总科目", - children: - }, - ]; - - for (let i = 2; i < 500; i++) { - items.push( - { - key: i + '', - label: "绩效工资", - children: ( - - ) - }) - } - - const [activeKey, setActiveKey] = useState("1"); - - const dispatch = useDispatch(); - - useEffect(() => { - users().then(res => { - dispatch(updateUsers(res)); - }) - }, []); - - return ( - - { - setActiveKey(value); - }} - > - {items.map(item => { - return ( - - ) - })} - - - {items.map(item => { - if (item.key === activeKey) { - return item.children; - } - })} - - - - ) -} - -const Salary = () => { - return ( - <> - - <$Salary/> - - - ) -} - - -export default Salary; diff --git a/admin-ui/src/pages/salary/store/salary.ts b/admin-ui/src/pages/salary/store/salary.ts deleted file mode 100644 index ddd54a23..00000000 --- a/admin-ui/src/pages/salary/store/salary.ts +++ /dev/null @@ -1,64 +0,0 @@ -import {configureStore, createSlice, PayloadAction} from '@reduxjs/toolkit'; -import {Table1, Table2, User} from "@/pages/salary/types"; - -export interface SalaryStore { - users: User[]; - table1: Table1[]; - table2: Table2[]; - table2Version:number; -} - -export type SalaryStoreAction = { - - updateUsers: (state: SalaryStore, action: PayloadAction) => void; - - updateTable1: (state: SalaryStore, action: PayloadAction) => void; - - updateTable2: (state: SalaryStore, action: PayloadAction) => void; - - updateTable2Version: (state: SalaryStore, action: PayloadAction) => void; -} - -export const SalarySlice = createSlice({ - name: 'salary', - initialState: { - users: [], - table1: [], - table2: [], - table2Version:0 - }, - reducers: { - - updateUsers: (state, action) => { - state.users = action.payload; - }, - - updateTable1: (state, action) => { - state.table1 = action.payload; - }, - - updateTable2: (state, action) => { - state.table2 = action.payload; - }, - - updateTable2Version: (state, action) => { - state.table2Version = action.payload; - } - - }, -}); - -export const { - updateUsers, - updateTable1, - updateTable2, - updateTable2Version -} = SalarySlice.actions; - -export const salaryStore = configureStore({ - reducer: { - salary: SalarySlice.reducer - }, -}); - -export type SalaryState = ReturnType; diff --git a/admin-ui/src/pages/salary/types.ts b/admin-ui/src/pages/salary/types.ts deleted file mode 100644 index d4fd1ad4..00000000 --- a/admin-ui/src/pages/salary/types.ts +++ /dev/null @@ -1,22 +0,0 @@ - -export interface User{ - id:number, - name:string -} - - -export interface Table1{ - id:number, - name:string, - jxgz:number, - sum:number -} - - -export interface Table2{ - id:number, - name:string, - kemu1:number, - kemu2:number, - sum:number -} diff --git a/admin-ui/src/pages/user/index.tsx b/admin-ui/src/pages/user/index.tsx deleted file mode 100644 index a7533db5..00000000 --- a/admin-ui/src/pages/user/index.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import {list} from '@/api/user'; -import type {ActionType} from '@ant-design/pro-components'; -import {PageContainer, ProTable} from '@ant-design/pro-components'; -import React, {useRef} from 'react'; - -const UserPage: React.FC = () => { - - const actionRef = useRef(null); - - const columns = [ - { - title: "编号", - dataIndex: 'id', - key: 'id', - sorter: true, - search: false, - }, - { - title: "名称", - dataIndex: 'name', - key: 'name', - }, - ]; - - return ( - - { - return await list(params, sort, filter, [ - { - key: 'name', - type: 'LIKE' - } - ]); - }} - /> - - ); -}; - -export default UserPage; diff --git a/admin-ui/src/pages/welcome/__test__/index.test.tsx b/admin-ui/src/pages/welcome/__test__/index.test.tsx deleted file mode 100644 index 4fbc8f3d..00000000 --- a/admin-ui/src/pages/welcome/__test__/index.test.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import React from 'react'; -import Welcome from '../index'; -import {render} from "@testing-library/react"; -import store from "@/store/Redux"; -import {Provider} from "react-redux"; -import {Calculate} from "../data"; - -// test description for the component -describe('Welcome Component', () => { - // test case for the component - test('renders with default initial value', () => { - // render the component - render( - - - - ); - // get the element that will be tested - const countElement = document.querySelector('.App-header p'); - // assert the element is exist - expect(countElement).toBeInTheDocument(); - // assert the element text content - expect(countElement).toHaveTextContent('hi , Redux counter: 0, Roles:'); - // log the element text content - console.log(countElement?.textContent); - }); - - - test('method add test', () => { - const sum = Calculate.add(1,1); - expect(sum).toBe(2); - }); -}); diff --git a/admin-ui/src/pages/welcome/data.ts b/admin-ui/src/pages/welcome/data.ts deleted file mode 100644 index 8128121b..00000000 --- a/admin-ui/src/pages/welcome/data.ts +++ /dev/null @@ -1,18 +0,0 @@ -export class Calculate{ - - static add = (a: number, b: number) => { - return a + b; - } - - static sub = (a: number, b: number) => { - return a - b; - } - - static mul = (a: number, b: number) => { - return a * b; - } - - static div = (a: number, b: number) => { - return a / b; - } -} diff --git a/admin-ui/src/pages/welcome/index.tsx b/admin-ui/src/pages/welcome/index.tsx index 80579a34..5bf4fca0 100644 --- a/admin-ui/src/pages/welcome/index.tsx +++ b/admin-ui/src/pages/welcome/index.tsx @@ -1,61 +1,21 @@ import React from 'react'; +import {PageContainer} from "@ant-design/pro-components"; import './index.scss'; -import Page from "@/components/Layout/Page"; -import {Button, Form, Input} from "antd"; -import {FlowFormApiContext, FlowFormCustomValidateContext} from "@/api/validate"; -import * as api from "@/api" -FlowFormApiContext.getInstance().setApi({ - get: (url: string, params?: any) => { - return api.get(url, params); - }, - post: (url: string, data: any) => { - return api.post(url, data); - } -}); -const Index = () => { - - const [form] = Form.useForm(); - - const context = new FlowFormCustomValidateContext(); - - const validateFuncCode = ` - if (content.value) { - return []; - } else { - return ["姓名不存在"]; - } - `; - context.addCustomFunctionCodeValidate(["user", "name"], validateFuncCode); +const WelcomePage = () => { return ( - - - - - - - - - - - - - - + + +
    Admin-UI 支持的功能有
+
    1. 自定义流程
+
    2. 表单渲染
+
    3. 管理权限
+
    4. 动态菜单
+
    5. 动态加载组件
+
); } -export default Index; +export default WelcomePage; diff --git a/admin-ui/src/portal/index.tsx b/admin-ui/src/portal/index.tsx deleted file mode 100644 index 4e063986..00000000 --- a/admin-ui/src/portal/index.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import React from "react"; - -const Portal = ()=>{ - return ( - <> - - - ) -} -export default Portal; diff --git a/admin-pro-ui/src/utils/captcha.ts b/admin-ui/src/utils/captcha.ts similarity index 100% rename from admin-pro-ui/src/utils/captcha.ts rename to admin-ui/src/utils/captcha.ts diff --git a/admin-pro-ui/src/utils/oss.ts b/admin-ui/src/utils/oss.ts similarity index 100% rename from admin-pro-ui/src/utils/oss.ts rename to admin-ui/src/utils/oss.ts diff --git a/admin-pro-ui/todo.md b/admin-ui/todo.md similarity index 100% rename from admin-pro-ui/todo.md rename to admin-ui/todo.md diff --git a/admin-ui/webpack.common.js b/admin-ui/webpack.common.js index 061d285a..34dac02d 100644 --- a/admin-ui/webpack.common.js +++ b/admin-ui/webpack.common.js @@ -15,7 +15,7 @@ module.exports = { }, ], devServer: { - port: 3000, + port: 8000, }, resolve: { extensions: ['.ts', '.tsx', '.js'], diff --git a/admin-ui/webpack.config.dev.js b/admin-ui/webpack.config.dev.js index 60b77fdb..2ac3919e 100644 --- a/admin-ui/webpack.config.dev.js +++ b/admin-ui/webpack.config.dev.js @@ -4,7 +4,7 @@ const common = require('./webpack.common.js'); module.exports = merge(common, { mode: 'development', devServer: { - port: 3000, + port: 8000, proxy: [ { diff --git a/admin-ui/webpack.config.mock.js b/admin-ui/webpack.config.mock.js index d53c66ae..1998dc09 100644 --- a/admin-ui/webpack.config.mock.js +++ b/admin-ui/webpack.config.mock.js @@ -7,7 +7,7 @@ const express = require('express'); module.exports = merge(common, { mode: 'development', devServer: { - port: 3000, + port: 8000, setupMiddlewares: (middlewares, devServer) => { if (!devServer) { diff --git a/admin-ui/webpack.config.prod.js b/admin-ui/webpack.config.prod.js index 90868278..3e2d47c5 100644 --- a/admin-ui/webpack.config.prod.js +++ b/admin-ui/webpack.config.prod.js @@ -4,6 +4,6 @@ const common = require('./webpack.common.js'); module.exports = merge(common,{ mode: 'production', devServer: { - port: 13000, + port: 8000, }, }); diff --git a/example/example-app/example-app-cmd-domain/src/main/java/com/codingapi/example/app/cmd/domain/router/UserRouter.java b/example/example-app/example-app-cmd-domain/src/main/java/com/codingapi/example/app/cmd/domain/router/UserRouter.java index 1b4fccd0..88083da0 100644 --- a/example/example-app/example-app-cmd-domain/src/main/java/com/codingapi/example/app/cmd/domain/router/UserRouter.java +++ b/example/example-app/example-app-cmd-domain/src/main/java/com/codingapi/example/app/cmd/domain/router/UserRouter.java @@ -13,9 +13,9 @@ public class UserRouter { public void createOrUpdate(UserCmd.UpdateRequest request) { if (request.hasId()) { - userService.update(request.getId(), request.toMetric()); + userService.update(request.getId(), request.toMetric(),request.isFlowManager()); } else { - userService.create(request.toMetric()); + userService.create(request.toMetric(),request.isFlowManager()); } } diff --git a/example/example-domain/example-domain-user/src/main/java/com/codingapi/example/domain/user/service/UserService.java b/example/example-domain/example-domain-user/src/main/java/com/codingapi/example/domain/user/service/UserService.java index b67cdb48..2c286594 100644 --- a/example/example-domain/example-domain-user/src/main/java/com/codingapi/example/domain/user/service/UserService.java +++ b/example/example-domain/example-domain-user/src/main/java/com/codingapi/example/domain/user/service/UserService.java @@ -22,16 +22,18 @@ public void initAdmin() { } } - public void create(UserMetric userMetric) { + public void create(UserMetric userMetric,boolean isFlowManager) { User user = new User(); + user.setFlowManager(isFlowManager); user.setUserMetric(userMetric); user.encodePassword(passwordEncoder); userRepository.save(user); } - public void update(long id, UserMetric metric) { + public void update(long id, UserMetric metric,boolean isFlowManager) { User user = userRepository.getUserById(id); + user.setFlowManager(isFlowManager); user.setUserMetric(metric); user.encodePassword(passwordEncoder); userRepository.save(user); diff --git a/example/example-interface/src/main/java/com/codingapi/example/api/domain/UserDomainCmdController.java b/example/example-interface/src/main/java/com/codingapi/example/api/domain/UserDomainCmdController.java index 7863f88f..15ecfe1d 100644 --- a/example/example-interface/src/main/java/com/codingapi/example/api/domain/UserDomainCmdController.java +++ b/example/example-interface/src/main/java/com/codingapi/example/api/domain/UserDomainCmdController.java @@ -11,7 +11,7 @@ import org.springframework.web.bind.annotation.RestController; @RestController -@RequestMapping("/api/cmd/domain/user") +@RequestMapping("/api/cmd/user") @AllArgsConstructor public class UserDomainCmdController { diff --git a/mobile-ui/.gitignore b/mobile-ui/.gitignore index f46b1022..5d207c84 100644 --- a/mobile-ui/.gitignore +++ b/mobile-ui/.gitignore @@ -25,4 +25,5 @@ npm-debug.log* yarn-debug.log* yarn-error.log* yarn.lock +package-lock.json diff --git a/mobile-ui/package.json b/mobile-ui/package.json index 44acb0bd..899a3ac0 100644 --- a/mobile-ui/package.json +++ b/mobile-ui/package.json @@ -4,6 +4,9 @@ "private": true, "dependencies": { "@babel/standalone": "^7.25.6", + "@codingapi/flow-mobile": "^0.0.3", + "@codingapi/form-mobile": "^0.0.3", + "@codingapi/ui-framework": "^0.0.12", "@logicflow/core": "^2.0.10", "@logicflow/extension": "^2.0.14", "@reduxjs/toolkit": "^2.2.7", @@ -17,6 +20,7 @@ "base64-js": "^1.5.1", "cross-env": "^7.0.3", "dayjs": "^1.11.13", + "jszip": "^3.10.1", "lodash": "^4.17.21", "moment": "^2.30.1", "monaco-editor": "^0.51.0", diff --git a/mobile-ui/public/captcha.jpeg b/mobile-ui/public/captcha.jpeg new file mode 100644 index 00000000..74930885 Binary files /dev/null and b/mobile-ui/public/captcha.jpeg differ diff --git a/mobile-ui/src/components/descriptions/index.scss b/mobile-ui/src/components/descriptions/index.scss deleted file mode 100644 index bd0d0289..00000000 --- a/mobile-ui/src/components/descriptions/index.scss +++ /dev/null @@ -1,34 +0,0 @@ -@use "@/config/variables" as *; - -.descriptions-list { - padding: 3px; - background-color: white; - - .descriptions-list-item { - background-color: white; - padding: 15px; - font-size: $content-font-size; - display: flex; - align-items: center; - justify-content: space-between; - border-bottom: 1px solid $body-background-color; - - .descriptions-list-item-label { - font-weight: bold; - } - - .descriptions-list-item-value { - } - } - - .form-header-title { - width: 100%; - height: 20px; - border-left: 4px solid $theme-primary-color; - padding-left: 4px; - font-size: 14px; - margin-bottom: 10px; - } -} - - diff --git a/mobile-ui/src/components/descriptions/index.tsx b/mobile-ui/src/components/descriptions/index.tsx deleted file mode 100644 index cdb95701..00000000 --- a/mobile-ui/src/components/descriptions/index.tsx +++ /dev/null @@ -1,98 +0,0 @@ -import React, {useEffect, useImperativeHandle} from "react"; -import {FormField} from "@/components/form/types"; -import "./index.scss"; - -export interface DescriptionsAction { - // 重新刷新数据 - reload: () => void; -} - -// 详情展示属性 -interface DescriptionsProps { - // 展示的字段 - columns?: FormField[]; - // 请求数据 - request?: () => Promise; - // 操作对象 - actionRef?: React.Ref; - // 页脚 - footer?: React.ReactNode; - // 页头 - header?: React.ReactNode; - // 数据转换 - dataConvert?: (field: FormField, data: any) => Promise; -} - -// 详情展示 -const Descriptions: React.FC = (props) => { - - const [data, setData] = React.useState(null); - - const reload = () => { - if (props.request) { - props.request().then((data) => { - if(props.dataConvert) { - const promise = [] as Promise[]; - props.columns?.map(item => { - promise.push(new Promise((resolve, reject) => { - props.dataConvert?.(item, data).then(value => { - data[item.props.name] = value; - resolve(value); - }).catch(reject); - })); - }); - Promise.all(promise).then(() => { - setData(data); - }); - }else { - setData(data); - } - }) - } - } - - useImperativeHandle(props.actionRef, () => ({ - reload: () => { - reload() - } - }), [props.actionRef]); - - useEffect(() => { - reload(); - - return () => { - setData(null); - } - }, []); - - return ( -
- {props.header} - {data && props.columns && props.columns - .filter(item => !item.props.hidden) - .map((item) => { - const key = item.props.name; - const value = data[key]; - const valueType = typeof value === 'object' ? 'object' : 'string'; - return ( -
-
- {valueType === 'string' && ( -
- )} - {valueType === 'object' && ( -
- {value} -
- )} -
- ) - })} - {props.footer} -
- ) -} - -export default Descriptions; diff --git a/mobile-ui/src/components/flow/plugins/PostponedFormView.tsx b/mobile-ui/src/components/flow/PostponedFormView.tsx similarity index 90% rename from mobile-ui/src/components/flow/plugins/PostponedFormView.tsx rename to mobile-ui/src/components/flow/PostponedFormView.tsx index f3707631..7c903cf2 100644 --- a/mobile-ui/src/components/flow/plugins/PostponedFormView.tsx +++ b/mobile-ui/src/components/flow/PostponedFormView.tsx @@ -1,9 +1,9 @@ import React from "react"; -import {PostponedFormProps} from "@/components/flow/types"; +import {PostponedFormProps} from "@codingapi/ui-framework"; import Popup from "@/components/popup"; import {DatePickerView} from "antd-mobile"; import dayjs from "dayjs"; -import {dateLabelRenderer} from "@/components/form/date"; +import {dateLabelRenderer} from "@codingapi/form-mobile"; const PostponedFormView: React.FC = (props) => { diff --git a/mobile-ui/src/components/flow/plugins/UserSelectFormView.tsx b/mobile-ui/src/components/flow/UserSelectFormView.tsx similarity index 90% rename from mobile-ui/src/components/flow/plugins/UserSelectFormView.tsx rename to mobile-ui/src/components/flow/UserSelectFormView.tsx index 9c6dfdd1..919e42fa 100644 --- a/mobile-ui/src/components/flow/plugins/UserSelectFormView.tsx +++ b/mobile-ui/src/components/flow/UserSelectFormView.tsx @@ -1,8 +1,7 @@ import React, {useEffect} from "react"; -import {UserSelectFormProps} from "@/components/flow/types"; +import {UserSelectFormProps} from "@codingapi/ui-framework"; import Popup from "@/components/popup"; -import Form from "@/components/form"; -import FormInput from "@/components/form/input"; +import {Form,FormInput} from "@codingapi/form-mobile"; const UserSelectFormView: React.FC = (props) => { diff --git a/mobile-ui/src/components/flow/components/FlowChart.tsx b/mobile-ui/src/components/flow/components/FlowChart.tsx deleted file mode 100644 index 8b5e2b6a..00000000 --- a/mobile-ui/src/components/flow/components/FlowChart.tsx +++ /dev/null @@ -1,93 +0,0 @@ -import React, {useContext, useEffect} from "react"; -import '@logicflow/core/es/index.css'; -import '@logicflow/extension/lib/style/index.css'; -import {LogicFlow, Options} from "@logicflow/core"; -import {DndPanel, Menu, MiniMap, Snapshot} from "@logicflow/extension"; -import {FlowViewReactContext} from "@/components/flow/view"; -import Start from "@/components/flow/nodes/Start"; -import Over from "@/components/flow/nodes/Over"; -import Circulate from "@/components/flow/nodes/Circulate"; -import Node from "@/components/flow/nodes/Node"; -import EdgeType = Options.EdgeType; - -interface FlowChartProps { - edgeType?: EdgeType; -} - -const FlowChart: React.FC = (props) => { - - const flowViewReactContext = useContext(FlowViewReactContext); - const flowRecordContext = flowViewReactContext?.flowRecordContext; - const flowSchema = flowRecordContext?.getFlowSchema(); - - const [url, setUrl] = React.useState(''); - - const edgeType = props.edgeType || 'polyline'; - const container = React.useRef(null); - const lfRef = React.useRef(null); - - useEffect(() => { - const SilentConfig = { - isSilentMode: true, - stopScrollGraph: false, - stopMoveGraph: false, - stopZoomGraph: false, - edgeTextEdit: false, - }; - - //@ts-ignore - lfRef.current = new LogicFlow({ - //@ts-ignore - container: container.current, - ...SilentConfig, - background: { - backgroundColor: '#f3f5f8' - }, - width: 0, - height: 0, - plugins: [Menu, DndPanel, MiniMap, Snapshot], - grid: false, - edgeType: edgeType, - }); - - lfRef.current.setTheme({ - bezier: { - stroke: '#8f94e3', - strokeWidth: 1, - }, - polyline: { - stroke: '#8f94e3', - strokeWidth: 1, - }, - line: { - stroke: '#8f94e3', - strokeWidth: 1, - }, - }); - lfRef.current.register(Start); - lfRef.current.register(Node); - lfRef.current.register(Over); - lfRef.current.register(Circulate); - lfRef.current.render(flowSchema); - - setTimeout(() => { - lfRef.current?.getSnapshotBlob().then((blob: any) => { - setUrl(URL.createObjectURL(blob.data)); - }); - }, 100) - - }, [flowViewReactContext]); - - return ( -
-
-
- {url && ( - - )} -
-
- ) -} - -export default FlowChart; diff --git a/mobile-ui/src/components/flow/components/FlowContent.tsx b/mobile-ui/src/components/flow/components/FlowContent.tsx deleted file mode 100644 index 0949d166..00000000 --- a/mobile-ui/src/components/flow/components/FlowContent.tsx +++ /dev/null @@ -1,67 +0,0 @@ -import React, {useContext, useEffect} from "react"; -import {Divider, Tabs} from "antd-mobile"; -import {FlowFormViewProps} from "@/components/flow/types"; -import {FlowViewReactContext} from "@/components/flow/view"; -import FlowHistory from "@/components/flow/components/FlowHistory"; -import FlowFormOpinion from "@/components/flow/components/FlowFormOpinion"; -import {useSelector} from "react-redux"; -import {FlowReduxState} from "@/components/flow/store/FlowSlice"; -import FlowChart from "@/components/flow/components/FlowChart"; -import FlowHistoryLine from "@/components/flow/components/FlowHistoryLine"; -import FlowOpinion from "@/components/flow/components/FlowOpinion"; - -const FlowContent= () => { - const flowViewReactContext = useContext(FlowViewReactContext); - - const flowRecordContext = flowViewReactContext?.flowRecordContext; - const formInstance = flowViewReactContext?.formInstance; - - const FlowFormView = flowRecordContext?.getFlowFormView() as React.ComponentType; - - const formParams = flowRecordContext?.getFlowFormParams(); - - const opinionVisible = useSelector((state: FlowReduxState) => state.flow.opinionVisible); - const dataVersion = useSelector((state: FlowReduxState) => state.flow.dataVersion); - const contentHiddenVisible = useSelector((state: FlowReduxState) => state.flow.contentHiddenVisible); - - useEffect(() => { - if(!flowRecordContext?.isEditable()){ - setTimeout(()=>{ - formInstance?.disableAll(); - },100); - } - }, []); - - const style = contentHiddenVisible ? {"display":"none"} : {}; - return ( -
- - - {formInstance && ( - - )} - - {opinionVisible && ( - - )} - - - - 审批记录 - - - - - 流转历史 - - - -
- ) -} - -export default FlowContent; diff --git a/mobile-ui/src/components/flow/components/FlowFooter.tsx b/mobile-ui/src/components/flow/components/FlowFooter.tsx deleted file mode 100644 index eea062e5..00000000 --- a/mobile-ui/src/components/flow/components/FlowFooter.tsx +++ /dev/null @@ -1,118 +0,0 @@ -import React, {useContext} from "react"; -import {ActionSheet, Button} from "antd-mobile"; -import {FlowViewReactContext} from "@/components/flow/view"; -import {useSelector} from "react-redux"; -import {FlowReduxState} from "@/components/flow/store/FlowSlice"; -import {useNavigate} from "react-router"; - -interface FlowFooterProps { - maxButtonCount?: number; -} - -const FlowFooter: React.FC = (props) => { - const flowViewReactContext = useContext(FlowViewReactContext); - - const flowRecordContext = flowViewReactContext?.flowRecordContext; - - const flowButtonClickContext = flowViewReactContext?.flowButtonClickContext; - - const buttons = flowRecordContext?.getFlowButtons()||[]; - const maxButtonCount = props.maxButtonCount || 4; - const requestLoading = useSelector((state: FlowReduxState) => state.flow.requestLoading); - const contentHiddenVisible = useSelector((state: FlowReduxState) => state.flow.contentHiddenVisible); - const [visible, setVisible] = React.useState(false); - const navigate = useNavigate(); - - const style = contentHiddenVisible ? {"display":"none"} : {}; - - if(flowRecordContext?.isEditable()){ - return ( -
- {buttons && buttons.length <= maxButtonCount && buttons.map((item) => { - const style = item.style && JSON.parse(item.style) || {}; - return ( - - ) - })} - {buttons && buttons.length > maxButtonCount && ( - <> - {buttons && buttons.slice(0, maxButtonCount - 1).map(item => { - const style = item.style && JSON.parse(item.style) || {}; - return ( - - ) - })} - - - - { - return { - text: item.name, - key: item.id, - onClick: () => { - flowButtonClickContext?.handlerClick(item); - setVisible(false); - } - } - })} - onClose={() => setVisible(false)} - /> - - )} - - -
- ) - }else { - return ( -
- -
- ) - } -} - -export default FlowFooter; diff --git a/mobile-ui/src/components/flow/components/FlowForm404.tsx b/mobile-ui/src/components/flow/components/FlowForm404.tsx deleted file mode 100644 index 31257336..00000000 --- a/mobile-ui/src/components/flow/components/FlowForm404.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import React from "react"; -import {Button, Result} from "antd-mobile"; -import {useNavigate} from "react-router"; - - -const FlowForm404 = ()=>{ - - const navigate = useNavigate(); - - return ( - - -
- )} - /> - ) -} - -export default FlowForm404; diff --git a/mobile-ui/src/components/flow/components/FlowFormOpinion.tsx b/mobile-ui/src/components/flow/components/FlowFormOpinion.tsx deleted file mode 100644 index fe300015..00000000 --- a/mobile-ui/src/components/flow/components/FlowFormOpinion.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import React, {useContext, useEffect} from "react"; -import Form from "@/components/form"; -import {FlowViewReactContext} from "@/components/flow/view"; - - -const FlowFormOpinion = ()=>{ - - const flowViewReactContext = useContext(FlowViewReactContext); - const opinionInstance = flowViewReactContext?.opinionInstance; - const flowRecordContext = flowViewReactContext?.flowRecordContext; - - useEffect(() => { - opinionInstance?.setFieldValue("advice", flowRecordContext?.getOpinionAdvice()); - }, []); - - return ( - <> -
{ - return [ - { - type:'textarea', - props:{ - name:"advice", - label:"审批意见", - textAreaRows:2, - required:true, - validateFunction:async (content)=>{ - const value = content.value; - if(value){ - return []; - } - return ["请输入审批意见"]; - } - } - } - ] - }} - > -
- - ) -} - -export default FlowFormOpinion; diff --git a/mobile-ui/src/components/flow/components/FlowHistory.tsx b/mobile-ui/src/components/flow/components/FlowHistory.tsx deleted file mode 100644 index 25faa305..00000000 --- a/mobile-ui/src/components/flow/components/FlowHistory.tsx +++ /dev/null @@ -1,167 +0,0 @@ -import React, {useContext} from "react"; -import {FlowViewReactContext} from "@/components/flow/view"; -import Descriptions from "@/components/descriptions"; -import {FormField} from "@/components/form/types"; -import moment from "moment"; -import {Tag} from "antd-mobile"; - -const fields = [ - { - type: 'input', - props: { - name: 'title', - label: '', - } - }, - { - type: 'input', - props: { - name: 'createOperatorName', - label: '发起人', - } - }, - { - type: 'input', - props: { - name: 'createOperatorDate', - label: '发起时间', - } - }, - { - type: 'input', - props: { - name: 'flowStatus', - label: '状态', - } - }, - { - type: 'input', - props: { - name: 'recodeType', - label: '流程状态', - } - }, - { - type: 'input', - props: { - name: 'postponedCount', - label: '是否延期', - } - }, - { - type: 'input', - props: { - name: 'interfere', - label: '是否干预', - } - }, - { - type: 'input', - props: { - name: 'read', - label: '是否已读', - } - }, - { - type: 'input', - props: { - name: 'timeoutTime', - label: '超时时间', - } - }, - { - type: 'input', - props: { - name: 'nodeName', - label: '节点名称', - } - } -] as FormField[]; - -const flowStatusConvert = (data: any) => { - if (data.flowStatus === 'RUNNING') { - return '进行中'; - } - if (data.flowStatus === 'FINISH') { - return '已结束'; - } - return ''; -} - -const recodeTypeConvert = (data: any) => { - if (data.flowType === 'TODO') { - return 待办; - } - if (data.flowType === 'DONE') { - return 已办; - } - if (data.flowType === 'TRANSFER') { - return 已转办; - } - return ''; -} - -const postponedCountConvert = (data: any) => { - if (data.postponedCount > 0) { - return '延期'; - } - return '未延期'; -} - -const interfereConvert = (data: any) => { - if (data.interfere) { - return '干预'; - } - return '未干预'; -} - -const readConvert = (data: any) => { - if (data.read) { - return '已读'; - } - return '未读'; -} - -const timeoutTimeConvert = (data: any) => { - if (data.timeoutTime == 0) { - return '未设置'; - } - return moment(data.timeoutTime).format("YYYY-MM-DD HH:mm:ss"); -} - - -const FlowHistory = () => { - const flowViewReactContext = useContext(FlowViewReactContext); - const flowRecordContext = flowViewReactContext?.flowRecordContext; - - const currentFlowRecord = flowRecordContext?.getCurrentFlowRecord(); - - if(currentFlowRecord) { - return ( -
-
- { - return { - ...currentFlowRecord, - createOperatorName: currentFlowRecord.createOperator?.name, - createOperatorDate: moment(currentFlowRecord.createTime).format('YYYY-MM-DD HH:mm:ss'), - flowStatus: flowStatusConvert(currentFlowRecord), - recodeType: recodeTypeConvert(currentFlowRecord), - postponedCount: postponedCountConvert(currentFlowRecord), - interfere: interfereConvert(currentFlowRecord), - read: readConvert(currentFlowRecord), - timeoutTime: timeoutTimeConvert(currentFlowRecord), - nodeName: flowRecordContext?.getNode(currentFlowRecord.nodeCode)?.name, - }; - }} - /> -
-
- ) - } - return <> -} - -export default FlowHistory; diff --git a/mobile-ui/src/components/flow/components/FlowHistoryLine.tsx b/mobile-ui/src/components/flow/components/FlowHistoryLine.tsx deleted file mode 100644 index 6ffeb24c..00000000 --- a/mobile-ui/src/components/flow/components/FlowHistoryLine.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import React, {useContext} from "react"; -import {FlowViewReactContext} from "@/components/flow/view"; -import moment from "moment"; -import {Tag} from "antd-mobile"; - -const recodeTypeConvert = (data: any) => { - if (data.flowType === 'TODO') { - return 待办; - } - if (data.flowType === 'DONE') { - return 已办; - } - if (data.flowType === 'TRANSFER') { - return 已转办; - } - return ''; -} - - -const FlowHistoryLine = () => { - const flowViewReactContext = useContext(FlowViewReactContext); - const flowRecordContext = flowViewReactContext?.flowRecordContext; - - const historyRecords = flowRecordContext?.getHistoryRecords(); - - return ( -
-
- {historyRecords && historyRecords.map((historyRecord: any, index: number) => { - const nodeName = flowRecordContext?.getNode(historyRecord.nodeCode)?.name; - const recodeType = recodeTypeConvert(historyRecord); - const createName = historyRecord.createOperator?.name; - const createTime = moment(historyRecord.createTime).format('YYYY-MM-DD HH:mm:ss'); - const flowAdvice = historyRecord.opinion?.advice || '暂无意见'; - return ( -
-
- {historyRecords.length > (index + 1) &&
} -
-
-
{nodeName}
-
状态:{recodeType}
-
创建人:{createName}
-
创建时间:{createTime}
-
审批意见:{flowAdvice}
-
-
- ) - })} -
-
- ) -} - -export default FlowHistoryLine; diff --git a/mobile-ui/src/components/flow/components/FlowOpinion.tsx b/mobile-ui/src/components/flow/components/FlowOpinion.tsx deleted file mode 100644 index 4ff19b83..00000000 --- a/mobile-ui/src/components/flow/components/FlowOpinion.tsx +++ /dev/null @@ -1,54 +0,0 @@ -import React, {useContext} from "react"; -import {FlowViewReactContext} from "@/components/flow/view"; -import moment from "moment"; - - -const optionStateConvert = (data: any) => { - if (data.opinion.result === 1) { - return '转办' - } - if (data.opinion.result === 2) { - return '通过' - } - if (data.opinion.result === 3) { - return '驳回' - } - return '暂存' -} - -const FlowOpinion = () => { - - const flowViewReactContext = useContext(FlowViewReactContext); - const flowRecordContext = flowViewReactContext?.flowRecordContext; - - const historyOpinions = flowRecordContext?.getHistoryOpinions(); - - return ( - <> - {historyOpinions && historyOpinions.map((item: any) => { - const flowOperatorName = item.operator.name; - const optionState = optionStateConvert(item); - const createTime = moment(item.createTime).format("YYYY-MM-DD HH:mm:ss"); - const advice = item.opinion.advice; - return ( -
-
- 审批人: - {flowOperatorName} {optionState} -
-
- 审批时间: - {createTime} -
-
- 审批意见: - {advice} -
-
- ) - })} - - ) -} - -export default FlowOpinion; diff --git a/mobile-ui/src/components/flow/components/FlowPage.tsx b/mobile-ui/src/components/flow/components/FlowPage.tsx deleted file mode 100644 index acf4c9c5..00000000 --- a/mobile-ui/src/components/flow/components/FlowPage.tsx +++ /dev/null @@ -1,134 +0,0 @@ -import React, {useEffect} from "react"; -import { - FlowFormViewProps, - FlowViewProps, - PostponedFormProps, - PostponedFormViewKey, - UserSelectFormProps, - UserSelectFormViewKey -} from "@/components/flow/types"; -import {useDispatch, useSelector} from "react-redux"; -import {FlowReduxState, updateState} from "@/components/flow/store/FlowSlice"; -import {FlowRecordContext} from "@/components/flow/domain/FlowRecordContext"; -import Form from "@/components/form"; -import {FlowStateContext} from "@/components/flow/domain/FlowStateContext"; -import {FlowEventContext} from "@/components/flow/domain/FlowEventContext"; -import FlowResult from "@/components/flow/components/FlowResult"; -import FlowContent from "@/components/flow/components/FlowContent"; -import FlowFooter from "@/components/flow/components/FlowFooter"; -import {FlowViewReactContext} from "@/components/flow/view"; -import FlowForm404 from "@/components/flow/components/FlowForm404"; -import {getComponent} from "@/framework/ComponentBus"; -import {FlowTriggerContext} from "@/components/flow/domain/FlowTriggerContext"; -import {FlowButtonClickContext} from "@/components/flow/domain/FlowButtonClickContext"; - - -interface FlowPageProps extends FlowViewProps { - // 流程详情数据 - flowData: any; -} - -const FlowPage: React.FC = (props) => { - - const dispatch = useDispatch(); - - const currentState = useSelector((state: FlowReduxState) => state.flow); - const flowRecordContext = new FlowRecordContext(props, props.flowData); - const formInstance = Form.useForm(); - const opinionInstance = Form.useForm(); - - const flowStateContext = new FlowStateContext(currentState, (state: any) => { - dispatch(updateState({ - ...state - })); - }); - const flowTriggerContext = new FlowTriggerContext(); - const flowEvenContext = new FlowEventContext(flowRecordContext, flowTriggerContext, formInstance, opinionInstance, flowStateContext); - const flowButtonClickContext = new FlowButtonClickContext(flowEvenContext, flowStateContext); - const FlowFormView = flowRecordContext.getFlowFormView() as React.ComponentType; - - // 延期表单视图 - const PostponedFormView = getComponent(PostponedFormViewKey) as React.ComponentType; - // 选人表单视图 - const UserSelectFormView = getComponent(UserSelectFormViewKey) as React.ComponentType; - - const version = useSelector((state: FlowReduxState) => state.flow.version); - - // 设置流程编号 - useEffect(() => { - if (props.id) { - flowStateContext.setRecordId(props.id); - } - }, [version]); - - if (FlowFormView) { - return ( - -
- {currentState.result && ( - - )} - - -
- - {PostponedFormView && ( - { - flowStateContext.setPostponedVisible(visible); - }} - onFinish={(timeOut) => { - flowEvenContext.postponedFlow(timeOut, (res) => { - flowStateContext.setResult({ - title: '延期成功', - state: 'success', - closeable: true, - items: [ - { - label: '延期时间', - value: `${timeOut}小时` - } - ] - }) - }); - }} - /> - )} - - {UserSelectFormView && currentState.userSelectMode && ( - { - flowStateContext.setUserSelectVisible(visible); - }} - onFinish={(users) => { - // 选择的人 - flowEvenContext.userSelectCallback(users, currentState.userSelectMode); - }} - multiple={currentState.userSelectMode.multiple} - specifyUserIds={currentState.userSelectMode.specifyUserIds} - currentUserIds={currentState.userSelectMode.currentUserIds} - userSelectType={currentState.userSelectMode.userSelectType} - /> - )} - -
- ) - } else { - return ( - - ) - } -} - -export default FlowPage; diff --git a/mobile-ui/src/components/flow/components/FlowRecord.tsx b/mobile-ui/src/components/flow/components/FlowRecord.tsx deleted file mode 100644 index 4362e1c8..00000000 --- a/mobile-ui/src/components/flow/components/FlowRecord.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import React, {useContext} from "react"; -import {FlowViewReactContext} from "@/components/flow/view"; - - -const FlowRecord = () => { - const flowViewReactContext = useContext(FlowViewReactContext); - const flowRecordContext = flowViewReactContext?.flowRecordContext; - - const historyRecords = flowRecordContext?.getHistoryRecords(); - - return ( -
-
流程历史记录
- -
- {historyRecords && historyRecords.map((item: any) => { - return ( -
-
{item.title}
-
- ) - })} -
- -
- ) -} - -export default FlowRecord; diff --git a/mobile-ui/src/components/flow/components/FlowResult.tsx b/mobile-ui/src/components/flow/components/FlowResult.tsx deleted file mode 100644 index c0554e52..00000000 --- a/mobile-ui/src/components/flow/components/FlowResult.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import React, {useContext} from "react"; -import {Button, Result} from "antd-mobile"; -import {useSelector} from "react-redux"; -import {FlowReduxState} from "@/components/flow/store/FlowSlice"; -import {useNavigate} from "react-router"; -import {FlowViewReactContext} from "@/components/flow/view"; - - -const FlowResult = () => { - - const result = useSelector((state: FlowReduxState) => state.flow.result); - const navigate = useNavigate(); - - const flowViewReactContext = useContext(FlowViewReactContext); - - return ( - - {result && result.items && result.items.map((item) => { - return ( -
-
{item.label}:
-
{item.value}
-
- ) - })} - -
- -
-
- )} - /> - ) -} - -export default FlowResult; diff --git a/mobile-ui/src/components/flow/domain/FlowButtonClickContext.ts b/mobile-ui/src/components/flow/domain/FlowButtonClickContext.ts deleted file mode 100644 index 4a0f8a4d..00000000 --- a/mobile-ui/src/components/flow/domain/FlowButtonClickContext.ts +++ /dev/null @@ -1,316 +0,0 @@ -import {FlowButton} from "@/components/flow/types"; -import {Toast} from "antd-mobile"; -import {FlowSubmitResultParser, FlowTrySubmitResultParser} from "@/components/flow/domain/FlowResultParser"; -import {FlowEventContext} from "@/components/flow/domain/FlowEventContext"; -import {FlowStateContext} from "./FlowStateContext"; - -// 流程按钮事件点击触发器 -export class FlowButtonClickContext { - - private readonly flowEventContext: FlowEventContext; - private readonly flowStateContext: FlowStateContext; - - constructor(flowEventContext: FlowEventContext, - flowStateContext: FlowStateContext) { - this.flowEventContext = flowEventContext; - this.flowStateContext = flowStateContext; - } - - - /** - * 触发刷新动作 - */ - handlerReload(){ - this.flowEventContext?.reloadFlow(); - } - - /** - * 触发保存动作 - */ - handlerSave(){ - if (this.flowStateContext?.hasRecordId()) { - this.flowEventContext?.saveFlow(() => { - Toast.show('流程保存成功'); - }) - } else { - this.flowEventContext?.startFlow(() => { - this.flowEventContext?.saveFlow(() => { - Toast.show('流程保存成功'); - }) - }); - } - } - - /** - * 触发发起动作 - */ - handlerStart(){ - if (this.flowStateContext?.hasRecordId()) { - Toast.show('流程已发起,无需重复发起'); - } else { - this.flowEventContext?.startFlow((res) => { - Toast.show('流程发起成功.'); - }) - } - } - - /** - * 指定人提交流程 - * @param approvalState 审批状态 同意为true 驳回为false - * @param operatorIds 流程操作人员id - */ - handlerSpecifyOperatorSubmit(approvalState:boolean,operatorIds:number[]){ - this.flowEventContext?.submitFlow(true, (res) => { - const flowSubmitResultParser = new FlowSubmitResultParser(res.data); - this.flowStateContext.setResult(flowSubmitResultParser.parser()); - }, operatorIds); - } - - /** - * 触发指定人提交动作 - */ - handlerSpecifySubmit(){ - const trySpecifySubmitHandler = ()=>{ - this.flowEventContext?.trySubmitFlow((res) => { - const operators = res.data.operators; - const userIds = operators.map((item: any) => { - return item.userId; - }); - this.flowStateContext?.setUserSelectMode({ - userSelectType: 'nextNodeUser', - multiple: true, - specifyUserIds: userIds, - }); - }); - } - if (this.flowStateContext?.hasRecordId()) { - trySpecifySubmitHandler(); - }else { - this.flowEventContext?.startFlow(() => { - trySpecifySubmitHandler(); - }); - } - } - - /** - * 触发提交动作 - */ - handlerSubmit(){ - const submitHandler = ()=>{ - this.flowEventContext?.submitFlow(true, (res) => { - const flowSubmitResultParser = new FlowSubmitResultParser(res.data); - this.flowStateContext?.setResult(flowSubmitResultParser.parser()); - }) - } - if (this.flowStateContext?.hasRecordId()) { - submitHandler(); - } else { - this.flowEventContext?.startFlow(() => { - submitHandler(); - }); - } - } - - /** - * 触发驳回动作 - */ - handlerReject(){ - if (this.flowStateContext?.hasRecordId()) { - this.flowEventContext?.submitFlow(false, (res) => { - const flowSubmitResultParser = new FlowSubmitResultParser(res.data); - this.flowStateContext?.setResult(flowSubmitResultParser.parser()); - }) - } else { - Toast.show('流程尚未发起,无法操作'); - } - } - - /** - * 触发尝试提交动作 - */ - handlerTrySubmit(){ - const trySubmitHandler = ()=>{ - this.flowEventContext?.trySubmitFlow((res) => { - const flowTrySubmitResultParser = new FlowTrySubmitResultParser(res.data); - this.flowStateContext?.setResult(flowTrySubmitResultParser.parser()); - }); - } - if (this.flowStateContext?.hasRecordId()) { - trySubmitHandler(); - }else { - this.flowEventContext?.startFlow(() => { - trySubmitHandler(); - }); - } - } - - /** - * 触发撤回动作 - */ - handlerRecall(){ - this.flowEventContext?.recallFlow(() => { - this.flowStateContext?.setResult({ - state: 'success', - closeable: true, - title: '流程撤回成功', - }); - }); - } - - /** - * 触发删除动作 - */ - handlerRemove(){ - if (this.flowStateContext?.hasRecordId()) { - this.flowEventContext?.removeFlow(() => { - this.flowStateContext?.setResult({ - state: 'success', - closeable: true, - title: '流程删除成功', - }); - }); - } else { - Toast.show('流程尚未发起,无法删除'); - } - } - - /** - * 触发催办动作 - */ - handlerUrge(){ - this.flowEventContext?.urgeFlow(() => { - this.flowStateContext?.setResult({ - state: 'success', - closeable: true, - title: '催办提醒已发送', - }); - }); - } - - /** - * 触发延期动作 - */ - handlerPostponed(){ - if (this.flowStateContext?.hasRecordId()) { - this.flowStateContext?.setPostponedVisible(true); - }else { - Toast.show('流程尚未发起,无法操作'); - } - } - - /** - * 触发转办动作 - */ - handlerTransfer(){ - if (this.flowStateContext?.hasRecordId()) { - this.flowStateContext?.setUserSelectMode({ - userSelectType: 'transfer', - multiple: false, - }); - }else { - Toast.show('流程尚未发起,无法操作'); - } - } - - /** - * 触发自定义接口动作 - * @param buttonId 自定义按钮的id - */ - handlerCustom(buttonId:string){ - const customHandler = ()=>{ - this.flowEventContext?.customFlow(buttonId, (res) => { - const customMessage = res.data; - this.flowStateContext?.setResult({ - state: customMessage.resultState.toLowerCase(), - ...customMessage - }); - }); - } - if (this.flowStateContext?.hasRecordId()) { - customHandler(); - } else { - this.flowEventContext?.startFlow((res) => { - customHandler(); - }); - } - } - - /** - * 触发自定义前端动作 - * @param eventKey 自定义按钮的事件key - */ - handlerView(eventKey:string){ - const viewHandler = ()=>{ - this.flowEventContext?.triggerEvent(eventKey); - } - - if (this.flowStateContext?.hasRecordId()) { - viewHandler(); - }else { - this.flowEventContext?.startFlow((res) => { - viewHandler(); - }); - } - } - - /** - * 处理按钮点击事件 - * @param button - */ - handlerClick(button: FlowButton) { - if (button.type === "RELOAD") { - this.handlerReload(); - } - - if (button.type === 'SAVE') { - this.handlerSave(); - } - - if (button.type === "START") { - this.handlerStart(); - } - if (button.type === 'SPECIFY_SUBMIT') { - this.handlerSpecifySubmit(); - } - - if (button.type === 'SUBMIT') { - this.handlerSubmit(); - } - - if (button.type === 'REJECT') { - this.handlerReject(); - } - - if (button.type === 'TRY_SUBMIT') { - this.handlerTrySubmit(); - } - - if (button.type === 'RECALL') { - this.handlerRecall(); - } - - if (button.type === 'REMOVE') { - this.handlerRemove(); - } - - if (button.type === 'URGE') { - this.handlerUrge() - } - - if (button.type === 'POSTPONED') { - this.handlerPostponed(); - } - - if (button.type === 'TRANSFER') { - this.handlerTransfer(); - } - - if (button.type === "CUSTOM") { - this.handlerCustom(button.id); - } - - if (button.type === 'VIEW') { - this.handlerView(button.eventKey); - } - } -} diff --git a/mobile-ui/src/components/flow/domain/FlowEventContext.ts b/mobile-ui/src/components/flow/domain/FlowEventContext.ts deleted file mode 100644 index 2abeda57..00000000 --- a/mobile-ui/src/components/flow/domain/FlowEventContext.ts +++ /dev/null @@ -1,350 +0,0 @@ -import React from "react"; -import {FormAction} from "@/components/form"; -import {FlowRecordContext} from "@/components/flow/domain/FlowRecordContext"; -import {FlowStateContext} from "@/components/flow/domain/FlowStateContext"; -import * as flowApi from "@/api/flow"; -import {FlowUser} from "@/components/flow/types"; -import {FlowSubmitResultParser} from "@/components/flow/domain/FlowResultParser"; -import {UserSelectMode} from "@/components/flow/store/FlowSlice"; -import {FlowTriggerContext} from "@/components/flow/domain/FlowTriggerContext"; -import FormInstance from "@/components/form/domain/FormInstance"; - -/** - * 流程的事件控制上下文对象 - */ -export class FlowEventContext { - - private readonly flowRecordContext: FlowRecordContext; - private readonly flowTriggerContext: FlowTriggerContext; - private readonly flowInstance: FormInstance; - private readonly opinionInstance: FormInstance; - private readonly flowStateContext: FlowStateContext; - - constructor(flowViewContext: FlowRecordContext, - flowTriggerContext:FlowTriggerContext, - flowInstance: FormInstance, - opinionInstance: FormInstance, - flowStateContext: FlowStateContext) { - this.flowRecordContext = flowViewContext; - this.flowTriggerContext = flowTriggerContext; - this.flowInstance = flowInstance; - this.opinionInstance = opinionInstance; - this.flowStateContext = flowStateContext; - } - - private getRequestBody = () => { - const formData = this.opinionInstance.getFieldsValue(); - const flowData = this.flowRecordContext.getFlowFormParams(); - const workCode = this.flowRecordContext.getWorkCode(); - const recordId = this.flowStateContext.getRecordId(); - const advice = this.opinionInstance.getFieldsValue(); - - return { - recordId, - workCode, - ...advice, - formData: { - ...flowData, - ...formData, - } - } - } - - - private validateForm = async () => { - const formState = await this.flowInstance.validate(); - const opinionState = await this.opinionInstance.validate(); - return formState && opinionState; - } - - /** - * 发起流程 - * @param callback 回调函数 - */ - startFlow = (callback?: (res: any) => void) => { - const body = this.getRequestBody(); - this.flowStateContext.setRequestLoading(true); - flowApi.startFlow(body) - .then(res => { - if (res.success) { - const newRecordId = res.data.records[0].id; - this.flowStateContext.setRecordId(newRecordId); - - if (callback) { - callback(res); - } - } - }) - .finally(() => { - this.flowStateContext.setRequestLoading(false); - }) - } - - - /** - * 提交流程 - * @param approvalState 是否审批通过 - * @param callback 回调函数 - * @param operatorIds 指定审批人 - */ - submitFlow = (approvalState: boolean, callback?: (res: any) => void, operatorIds?: number[]) => { - this.validateForm().then((validateState) => { - if (validateState) { - const body = { - ...this.getRequestBody(), - success: approvalState, - operatorIds: operatorIds, - } - this.flowStateContext.setRequestLoading(true); - flowApi.submitFlow(body) - .then(res => { - if (res.success) { - if (callback) { - callback(res); - } - } - }) - .finally(() => { - this.flowStateContext.setRequestLoading(false); - }) - } - }) - } - - /** - * 删除流程 - * @param callback 回调函数 - */ - removeFlow = (callback?: (res: any) => void) => { - this.flowStateContext.setRequestLoading(true); - const body = { - recordId: this.flowStateContext.getRecordId() - }; - flowApi.removeFlow(body).then(res => { - if (res.success) { - if (callback) { - callback(res); - } - } - }).finally(() => { - this.flowStateContext.setRequestLoading(false); - }) - } - - /** - * 保存流程 - * @param callback 回调函数 - */ - saveFlow = (callback?: (res: any) => void) => { - this.flowStateContext.setRequestLoading(true); - const body = this.getRequestBody(); - flowApi.saveFlow(body).then(res => { - if (res.success) { - if (callback) { - callback(res); - } - } - }).finally(() => { - this.flowStateContext.setRequestLoading(false); - }) - } - - /** - * 延期流程 - * @param timeOut 延期时间 - * @param callback 回调函数 - */ - postponedFlow(timeOut: number, callback?: (res: any) => void) { - this.flowStateContext.setRequestLoading(true); - const body = { - recordId: this.flowStateContext.getRecordId(), - timeOut - }; - flowApi.postponed(body).then(res => { - if (res.success) { - if (callback) { - callback(res); - } - } - }).finally(() => { - this.flowStateContext.setRequestLoading(false); - }) - } - - /** - * 自定义流程 - * @param buttonId 自定义按钮Id - * @param callback 回调函数 - */ - customFlow(buttonId: string, callback?: (res: any) => void) { - this.validateForm().then((validateState) => { - console.log('validateState', validateState); - if (validateState) { - const body = { - ...this.getRequestBody(), - buttonId: buttonId, - } - this.flowStateContext.setRequestLoading(true); - flowApi.custom(body) - .then(res => { - if (res.success) { - if (callback) { - callback(res); - } - } - }) - .finally(() => { - this.flowStateContext.setRequestLoading(false); - }) - } - }) - } - - /** - * 转办流程 - * @param user 转办用户 - * @param callback 回调函数 - */ - transferFlow(user: FlowUser, callback?: (res: any) => void) { - this.validateForm().then((validateState) => { - if (validateState) { - const body = { - ...this.getRequestBody(), - targetUserId: user.id - } - this.flowStateContext.setRequestLoading(true); - flowApi.transfer(body) - .then(res => { - if (res.success) { - if (callback) { - callback(res); - } - } - }) - .finally(() => { - this.flowStateContext.setRequestLoading(false); - }) - } - }) - } - - /** - * 催办流程 - * @param callback - */ - urgeFlow(callback?: (res: any) => void) { - this.flowStateContext.setRequestLoading(true); - const body = { - recordId: this.flowStateContext.getRecordId() - }; - flowApi.urge(body).then(res => { - if (res.success) { - if (callback) { - callback(res); - } - } - }).finally(() => { - this.flowStateContext.setRequestLoading(false); - }) - } - - /** - * 撤回流程 - */ - recallFlow(callback?: (res: any) => void) { - this.flowStateContext.setRequestLoading(true); - const body = { - recordId: this.flowStateContext.getRecordId() - }; - flowApi.recall(body).then(res => { - if (res.success) { - if (callback) { - callback(res); - } - } - }).finally(() => { - this.flowStateContext.setRequestLoading(false); - }) - } - - /** - * 预提交流程 - * @param callback - */ - trySubmitFlow(callback?: (res: any) => void) { - this.validateForm().then((validateState) => { - if (validateState) { - const body = { - ...this.getRequestBody(), - success: true, - } - this.flowStateContext.setRequestLoading(true); - flowApi.trySubmitFlow(body) - .then(res => { - if (res.success) { - if (callback) { - callback(res); - } - } - }) - .finally(() => { - this.flowStateContext.setRequestLoading(false); - }) - } - }) - } - - - /** - * 重新加载流程数据 - */ - reloadFlow() { - this.flowStateContext.randomVersion(); - } - - - /** - * 触发流程事件 - */ - triggerEvent(eventKey:string){ - this.flowTriggerContext.triggerEvent(eventKey); - } - - /** - * 用户选择回调 - * @param users 选择用户 - * @param userSelectMode 用户选择模式 - */ - userSelectCallback(users: FlowUser[], userSelectMode: UserSelectMode | null) { - if (users.length > 0) { - if (userSelectMode) { - if (userSelectMode.userSelectType === 'transfer') { - const targetUser = users[0]; - this.transferFlow(targetUser, (res) => { - const message = `已经成功转办给${targetUser.name}`; - this.flowStateContext.setResult({ - state: 'success', - closeable: true, - title: message, - }); - }); - } - if (userSelectMode.userSelectType === 'nextNodeUser') { - const userIds = users.map((item: any) => { - return item.id; - }); - this.submitFlow(true, (res) => { - const flowSubmitResultParser = new FlowSubmitResultParser(res.data); - this.flowStateContext.setResult(flowSubmitResultParser.parser()); - }, userIds); - } - } - } - - this.flowStateContext.setUserSelectVisible(false); - } - -} - - - diff --git a/mobile-ui/src/components/flow/domain/FlowRecordContext.ts b/mobile-ui/src/components/flow/domain/FlowRecordContext.ts deleted file mode 100644 index cc2a9354..00000000 --- a/mobile-ui/src/components/flow/domain/FlowRecordContext.ts +++ /dev/null @@ -1,205 +0,0 @@ -import {FlowButton, FlowFormView, FlowViewProps} from "@/components/flow/types"; - -/** - * 流程的详情控制上下文对象 - */ -export class FlowRecordContext { - - private readonly props: FlowViewProps; - private readonly data: any; - - constructor(props: FlowViewProps, data?: any) { - this.props = props; - this.data = data; - } - - // 获取流程的工作编码 - getWorkCode() { - return this.data.flowWork.code; - } - - // 获取流程的Form视图 - getFlowFormView() { - const view = this.props.view; - if (typeof view === 'object') { - const nodeView = this.data.flowNode.view; - return (view as FlowFormView)[nodeView]; - } - return view; - } - - - // 获取节点的数据 - getNode = (code: string) => { - if (this.data) { - const nodes = this.data.flowWork.nodes; - for (const node of nodes) { - if (node.code === code) { - return node; - } - } - } - return null; - } - - // 获取当前节点的表单数据 (内部使用) - private getNodeState = (code: string) => { - const historyRecords = this.data.historyRecords || []; - if (code==='over' && this.isFinished()) { - return "done"; - } - for (const record of historyRecords) { - if (record.nodeCode === code) { - if (record.flowType === 'TODO') { - return "current"; - } - return "done"; - } - } - if(this.isFinished()){ - return "undone"; - } - return "wait"; - } - - - // 获取历史记录 - getHistoryRecords = () => { - return this.data.historyRecords; - } - - // 获取当前的详情的记录数据 - getCurrentFlowRecord = () => { - return this.data.flowRecord; - } - - // 获取当前节点的流程图 - getFlowSchema = () => { - if (this.data.flowWork.schema) { - const schema = JSON.parse(this.data.flowWork.schema); - - for (const node of schema.nodes) { - node.properties.settingVisible = false; - node.properties.state = this.getNodeState(node.properties.code); - } - return schema; - } - return null; - } - - // 获取审批意见 - getOpinionAdvice = () => { - if(this.data.flowRecord){ - if(this.data.flowRecord.opinion){ - return this.data.flowRecord.opinion.advice; - } - } - return null; - } - - // 获取历史审批意见 - getHistoryOpinions() { - if(this.data.opinions){ - return this.data.opinions.filter((item:any)=>{ - if(!item.opinion){ - return false; - } - return item.opinion.result!==0; - }); - } - return []; - } - - //获取流程的form数据 - getFlowFormParams() { - return { - ...this.data.bindData, - ...this.props.formParams - } - } - - // 获取流程的操作按钮 - getFlowButtons() { - const buttons = this.data.flowNode.buttons as FlowButton[] || []; - return buttons.sort((item1: any, item2: any) => { - return item1.order - item2.order; - }) - } - - - // 是否可以审批 - isApproval = () => { - return this.data.canHandle; - } - - // 是否是开始节点 - isStartNode = ()=>{ - if (this.data) { - return this.data.flowNode.startNode; - } - return false; - } - - // 获取当前节点的code - getNodeCode = () => { - if (this.data) { - return this.data.flowNode.code; - } - return null; - } - - // 获取当前节点是否可编辑 - isEditable = () => { - if(this.isFinished()){ - return false; - } - if(this.isDone()){ - return false; - } - return this.getFlowNodeEditable() - } - - // 获取当前节点是否可编辑 - getFlowNodeEditable = () => { - if (this.data) { - const node = this.data.flowNode; - if (node) { - return node.editable; - } - } - return false - } - - // 是否已审批 - isDone() { - if (this.data.flowRecord) { - return this.data.flowRecord.flowStatus === 'FINISH' || this.data.flowRecord.flowType === 'DONE'; - } - return false; - } - - - // 是否完成 - isFinished() { - if (this.data.flowRecord) { - return this.data.flowRecord.flowStatus === 'FINISH'; - } - return false; - } - - - // 是否是退回状态 - isReject(){ - const historyRecords = this.data.historyRecords || []; - const currentRecord = this.data.flowRecord; - if(currentRecord && historyRecords.length>0){ - const preId = currentRecord.preId; - const preRecord = historyRecords.find((item:any)=>item.id===preId); - if(preRecord){ - return preRecord.flowSourceDirection === 'REJECT'; - } - } - return false; - } - -} diff --git a/mobile-ui/src/components/flow/domain/FlowResultParser.ts b/mobile-ui/src/components/flow/domain/FlowResultParser.ts deleted file mode 100644 index 686e7c67..00000000 --- a/mobile-ui/src/components/flow/domain/FlowResultParser.ts +++ /dev/null @@ -1,92 +0,0 @@ -import {FlowResultMessage} from "@/components/flow/types"; - -export class FlowWorkData { - - protected readonly data: any; - - constructor(data: any) { - this.data = data; - } - - getNode = (code: string) => { - if (this.data) { - const nodes = this.data.flowWork.nodes; - for (const node of nodes) { - if (node.code === code) { - return node; - } - } - } - return null; - } - - getWorkCode() { - return this.data.flowWork.code; - } -} - - -export class FlowSubmitResultParser extends FlowWorkData { - - constructor(data: any) { - super(data); - } - - parser() { - const records = this.data.records; - const resultItems = []; - for (const record of records) { - const node = this.getNode(record.nodeCode); - resultItems.push({ - label: '下级审批节点', - value: node.name - }); - - resultItems.push({ - label: '下级审批人', - value: record.currentOperator.name - }); - } - return { - title: '流程审批完成', - closeable: true, - state: 'success', - items: resultItems - } as FlowResultMessage; - } -} - - -export class FlowTrySubmitResultParser { - - private readonly data: any; - - constructor(data: any) { - this.data = data; - } - - parser() { - const operators = this.data.operators; - const usernames = operators.map((item: any) => { - return item.name; - }); - const flowResult = { - title: '下级节点提示', - closeable: false, - state: 'success', - items: [ - { - label: '下级审批节点', - value: this.data.flowNode.name - }, - { - label: '下级审批人', - value: usernames.join(',') - } - ] - } - - return flowResult as FlowResultMessage; - } - -} diff --git a/mobile-ui/src/components/flow/domain/FlowStateContext.tsx b/mobile-ui/src/components/flow/domain/FlowStateContext.tsx deleted file mode 100644 index 5f2368c7..00000000 --- a/mobile-ui/src/components/flow/domain/FlowStateContext.tsx +++ /dev/null @@ -1,95 +0,0 @@ -import {FlowStore, UserSelectMode} from "@/components/flow/store/FlowSlice"; -import {FlowResultMessage} from "@/components/flow/types"; - -/** - * 流程的状态数据上下文对象 - */ -export class FlowStateContext { - - private currentState: FlowStore; - private readonly updateFlowStore: (currentState: any) => any; - - constructor(currentState: FlowStore, updateFlowStore: (state: any) => any) { - this.currentState = JSON.parse(JSON.stringify(currentState)); - this.updateFlowStore = updateFlowStore; - } - - private updateState() { - this.updateFlowStore({ - ...this.currentState - }) - } - - setRequestLoading = (requestLoading: boolean) => { - this.currentState = { - ...this.currentState, - requestLoading - } - this.updateState(); - } - - setRecordId = (recordId: string) => { - this.currentState = { - ...this.currentState, - recordId - } - this.updateState(); - } - - setResult = (result: FlowResultMessage) => { - this.currentState = { - ...this.currentState, - result - } - this.updateState(); - } - - clearResult = () => { - this.currentState = { - ...this.currentState, - result: null - } - this.updateState(); - } - - hasRecordId = () => { - return this.currentState.recordId; - } - - getRecordId = () => { - return this.currentState.recordId; - } - - setPostponedVisible(visible: boolean) { - this.currentState = { - ...this.currentState, - postponedVisible: visible - } - this.updateState(); - } - - setUserSelectVisible(visible: boolean) { - this.currentState = { - ...this.currentState, - userSelectVisible: visible - } - this.updateState(); - } - - setUserSelectMode(userSelectMode: UserSelectMode) { - this.currentState = { - ...this.currentState, - userSelectVisible: true, - userSelectMode: userSelectMode - } - this.updateState(); - } - - randomVersion() { - this.currentState = { - ...this.currentState, - version: Math.random() - } - this.updateState(); - } -} diff --git a/mobile-ui/src/components/flow/domain/FlowTriggerContext.ts b/mobile-ui/src/components/flow/domain/FlowTriggerContext.ts deleted file mode 100644 index 7e673682..00000000 --- a/mobile-ui/src/components/flow/domain/FlowTriggerContext.ts +++ /dev/null @@ -1,32 +0,0 @@ -export interface EventTrigger { - trigger: (eventKey: string) => void -} - -// 流程事件触发器上下文对象 -export class FlowTriggerContext { - - private list: EventTrigger[] = []; - - constructor() { - } - - // 添加触发器 - addTrigger(trigger:(eventKey:string)=>void) { - this.list.push({ - trigger - }); - } - - // 触发事件 - triggerEvent(eventKey: string) { - this.list.forEach(trigger => { - trigger.trigger(eventKey); - }) - } - - clear() { - this.list = []; - } - -} - diff --git a/mobile-ui/src/components/flow/nodes/Circulate/index.scss b/mobile-ui/src/components/flow/nodes/Circulate/index.scss deleted file mode 100644 index 0e6b78fd..00000000 --- a/mobile-ui/src/components/flow/nodes/Circulate/index.scss +++ /dev/null @@ -1,49 +0,0 @@ - -$color: #61aa1f; - -.circulate-node { - display: flex; - align-items: center; - max-height: 40px; - border: 1px solid $color; - border-radius: 12px; - padding: 10px; - position: relative; - - .icon { - margin-left: 10px; - position: absolute; - color: $color; - font-size: 16px; - left: 0; - } - - .code { - margin-left: 20px; - font-size: 14px; - color: #4a5e63; - } - - .title { - margin-left: 5px; - font-size: 14px; - color: black; - } - - - .setting { - margin-right: 10px; - font-size: 14px; - position: absolute; - color: $color; - right: 0; - } - - - .state { - margin-right: 10px; - font-size: 14px; - position: absolute; - right: 0; - } -} diff --git a/mobile-ui/src/components/flow/nodes/Circulate/index.tsx b/mobile-ui/src/components/flow/nodes/Circulate/index.tsx deleted file mode 100644 index ed4e8ed9..00000000 --- a/mobile-ui/src/components/flow/nodes/Circulate/index.tsx +++ /dev/null @@ -1,131 +0,0 @@ -import {HtmlNode, HtmlNodeModel} from '@logicflow/core'; -import React from "react"; -import ReactDOM from "react-dom/client"; -import "./index.scss"; -import {NodeState} from "@/components/flow/nodes/states"; -import StateLabel from "@/components/flow/nodes/StateLabel"; -import {PlayOutline, SetOutline} from "antd-mobile-icons"; - -type CirculateProperties = { - id: string; - name: string; - code: string; - type: string; - view: string; - operatorMatcher: string; - editable: boolean; - titleGenerator: string; - errTrigger: string; - approvalType: string; - timeout: number; - settingVisible?: boolean; - state?: NodeState; -} - -interface CirculateProps { - name: string; - code?: string; - update?: (values: any) => void; - settingVisible?: boolean; - properties?: CirculateProperties; -} - -export const CirculateView: React.FC = (props) => { - const [visible, setVisible] = React.useState(false); - - const state = props.properties?.state; - - return ( -
- -
- - {props.code && ( - <>({props.code}) - )} - - {props.name} -
- - {props.settingVisible && ( - { - setVisible(true); - }} - /> - )} - - {state && ( -
- -
- )} -
- ); -} - -class CirculateModel extends HtmlNodeModel { - setAttributes() { - const name = this.properties.name as string; - this.width = 200 + name.length * 10; - this.height = 45; - this.text.editable = false; - this.menu = []; - - this.sourceRules = [ - { - message: `不允许输出`, - validate: (sourceNode: any, targetNode: any, sourceAnchor, targetAnchor) => { - const edges = this.graphModel.getNodeOutgoingEdge(sourceNode.id); - if (edges.length >= 1) { - return false; - } else { - return true; - } - }, - }, - ]; - - this.anchorsOffset = [ - [this.width / 2, 0], - [0, this.height / 2], - [-this.width / 2, 0], - [0, -this.height / 2], - ]; - } - - -} - -class CirculateNode extends HtmlNode { - setHtml(rootEl: SVGForeignObjectElement) { - const {properties} = this.props.model as HtmlNodeModel; - const div = document.createElement('div'); - const settingVisible = properties.settingVisible !== false; - ReactDOM.createRoot(div).render( - { - this.props.model.setProperties(values); - }}/>, - ); - //需要清空 - rootEl.innerHTML = ''; - rootEl.appendChild(div); - super.setHtml(rootEl); - } -} - -export default { - type: 'circulate-node', - view: CirculateNode, - model: CirculateModel, -}; - diff --git a/mobile-ui/src/components/flow/nodes/Node/index.scss b/mobile-ui/src/components/flow/nodes/Node/index.scss deleted file mode 100644 index 58b6ecb5..00000000 --- a/mobile-ui/src/components/flow/nodes/Node/index.scss +++ /dev/null @@ -1,49 +0,0 @@ - -$color: #6855ef; - -.node-node { - display: flex; - align-items: center; - max-height: 40px; - border: 1px solid $color; - border-radius: 12px; - padding: 10px; - position: relative; - - .icon { - margin-left: 10px; - position: absolute; - color: $color; - font-size: 16px; - left: 0; - } - - .code { - margin-left: 20px; - font-size: 14px; - color: #4a5e63; - } - - .title { - margin-left: 5px; - font-size: 14px; - color: black; - } - - - .setting { - margin-right: 10px; - font-size: 14px; - position: absolute; - color: $color; - right: 0; - } - - .state { - margin-right: 10px; - font-size: 14px; - position: absolute; - right: 0; - } - -} diff --git a/mobile-ui/src/components/flow/nodes/Node/index.tsx b/mobile-ui/src/components/flow/nodes/Node/index.tsx deleted file mode 100644 index fe45b1fe..00000000 --- a/mobile-ui/src/components/flow/nodes/Node/index.tsx +++ /dev/null @@ -1,121 +0,0 @@ -import {HtmlNode, HtmlNodeModel} from '@logicflow/core'; -import React from "react"; -import ReactDOM from "react-dom/client"; -import "./index.scss"; -import {NodeState} from "@/components/flow/nodes/states"; -import StateLabel from "@/components/flow/nodes/StateLabel"; -import {PlayOutline, SetOutline} from "antd-mobile-icons"; - -type NodeProperties = { - id: string; - name: string; - code: string; - type: string; - view: string; - operatorMatcher: string; - editable: boolean; - titleGenerator: string; - errTrigger: string; - approvalType: string; - timeout: number; - settingVisible?: boolean; - state?: NodeState; -} - -interface NodeProps { - name: string; - code?: string; - update?: (values: any) => void; - settingVisible?: boolean; - properties?: NodeProperties; - state?: NodeState; -} - -export const NodeView: React.FC = (props) => { - const [visible, setVisible] = React.useState(false); - - const state = props.properties?.state; - - return ( -
- -
- - {props.code && ( - <>({props.code}) - )} - - {props.name} -
- - {props.settingVisible && ( - { - setVisible(true); - }} - /> - )} - - {state && ( -
- -
- )} - -
- ); -} - -class NodeModel extends HtmlNodeModel { - setAttributes() { - const name = this.properties.name as string; - this.width = 200 + name.length * 10; - this.height = 45; - this.text.editable = false; - this.menu = []; - - this.anchorsOffset = [ - [this.width / 2, 0], - [0, this.height / 2], - [-this.width / 2, 0], - [0, -this.height / 2], - ]; - } - - -} - -class NodeNode extends HtmlNode { - setHtml(rootEl: SVGForeignObjectElement) { - const {properties} = this.props.model as HtmlNodeModel; - const div = document.createElement('div'); - - const settingVisible = properties.settingVisible !== false; - - ReactDOM.createRoot(div).render( - { - this.props.model.setProperties(values); - }}/>, - ); - //需要清空 - rootEl.innerHTML = ''; - rootEl.appendChild(div); - super.setHtml(rootEl); - } -} - -export default { - type: 'node-node', - view: NodeNode, - model: NodeModel, -}; - diff --git a/mobile-ui/src/components/flow/nodes/Over/index.scss b/mobile-ui/src/components/flow/nodes/Over/index.scss deleted file mode 100644 index 771c830f..00000000 --- a/mobile-ui/src/components/flow/nodes/Over/index.scss +++ /dev/null @@ -1,49 +0,0 @@ - -$color: #ea3f21; - -.over-node { - display: flex; - align-items: center; - max-height: 40px; - border: 1px solid $color; - border-radius: 12px; - padding: 10px; - position: relative; - - .icon { - margin-left: 10px; - position: absolute; - color: $color; - font-size: 16px; - left: 0; - } - - .code { - margin-left: 20px; - font-size: 14px; - color: #4a5e63; - } - - .title { - margin-left: 5px; - font-size: 14px; - color: black; - } - - - .setting { - margin-right: 10px; - font-size: 14px; - position: absolute; - color: $color; - right: 0; - } - - .state { - margin-right: 10px; - font-size: 14px; - position: absolute; - right: 0; - } - -} diff --git a/mobile-ui/src/components/flow/nodes/Over/index.tsx b/mobile-ui/src/components/flow/nodes/Over/index.tsx deleted file mode 100644 index 18158067..00000000 --- a/mobile-ui/src/components/flow/nodes/Over/index.tsx +++ /dev/null @@ -1,134 +0,0 @@ -import {HtmlNode, HtmlNodeModel} from '@logicflow/core'; -import React from "react"; -import ReactDOM from "react-dom/client"; -import "./index.scss"; -import {NodeState} from "@/components/flow/nodes/states"; -import StateLabel from "@/components/flow/nodes/StateLabel"; -import {PlayOutline, SetOutline} from "antd-mobile-icons"; - -type OverProperties = { - id:string; - name: string; - code: string; - type:string; - view: string; - operatorMatcher:string; - editable:boolean; - titleGenerator:string; - errTrigger:string; - approvalType:string; - timeout:number; - settingVisible?: boolean; - state?: NodeState; -} - -interface OverProps { - name: string; - code?: string; - update?: (values: any) => void; - settingVisible?: boolean; - properties?: OverProperties; -} - -export const OverView: React.FC = (props) => { - const [visible, setVisible] = React.useState(false); - - const state = props.properties?.state; - - return ( -
- -
- - {props.code && ( - <>({props.code}) - )} - - {props.name} -
- {props.settingVisible && ( - { - setVisible(true); - }} - /> - )} - - {state && ( -
- -
- )} - -
- ); -} - -class OverModel extends HtmlNodeModel { - - setAttributes() { - const name = this.properties.name as string; - this.width = 200 + name.length * 10; - this.height = 45; - this.text.editable = false; - this.menu = []; - - this.sourceRules = [ - { - message: `不允许输出`, - validate: (sourceNode, targetNode: any, sourceAnchor, targetAnchor) => { - const edges = this.graphModel.getNodeIncomingEdge(targetNode.id); - if (edges.length >= 0) { - return false; - } else { - return true; - } - }, - }, - ]; - - this.anchorsOffset = [ - // [this.width / 2, 0], - // [0, this.height / 2], - // [-this.width / 2, 0], - [0, -this.height / 2], - ]; - } - - -} - -class OverNode extends HtmlNode { - setHtml(rootEl: SVGForeignObjectElement) { - const {properties} = this.props.model as HtmlNodeModel; - const div = document.createElement('div'); - - const settingVisible = properties.settingVisible !== false; - - ReactDOM.createRoot(div).render( - { - this.props.model.setProperties(values); - }}/>, - ); - //需要清空 - rootEl.innerHTML = ''; - rootEl.appendChild(div); - super.setHtml(rootEl); - } -} - -export default { - type: 'over-node', - view: OverNode, - model: OverModel, -}; - diff --git a/mobile-ui/src/components/flow/nodes/Start/index.scss b/mobile-ui/src/components/flow/nodes/Start/index.scss deleted file mode 100644 index 0b511a9d..00000000 --- a/mobile-ui/src/components/flow/nodes/Start/index.scss +++ /dev/null @@ -1,47 +0,0 @@ -$color: #b1ad30; - -.start-node { - display: flex; - align-items: center; - max-height: 40px; - border: 1px solid $color; - border-radius: 12px; - padding: 10px; - position: relative; - - .icon { - margin-left: 10px; - position: absolute; - color: $color; - font-size: 16px; - left: 0; - } - - .code { - margin-left: 20px; - font-size: 14px; - color: #4a5e63; - } - - .title { - margin-left: 5px; - font-size: 14px; - color: black; - } - - .setting { - margin-right: 10px; - font-size: 14px; - position: absolute; - color: $color; - right: 0; - } - - .state { - margin-right: 10px; - font-size: 14px; - position: absolute; - right: 0; - } - -} diff --git a/mobile-ui/src/components/flow/nodes/Start/index.tsx b/mobile-ui/src/components/flow/nodes/Start/index.tsx deleted file mode 100644 index 084440bd..00000000 --- a/mobile-ui/src/components/flow/nodes/Start/index.tsx +++ /dev/null @@ -1,118 +0,0 @@ -import {HtmlNode, HtmlNodeModel} from '@logicflow/core'; -import React from "react"; -import ReactDOM from "react-dom/client"; -import "./index.scss"; -import {NodeState} from "@/components/flow/nodes/states"; -import StateLabel from "@/components/flow/nodes/StateLabel"; -import {PlayOutline, SetOutline} from "antd-mobile-icons"; - -type StartProperties = { - id: string; - name: string; - code: string; - type: string; - view: string; - operatorMatcher: string; - editable: boolean; - titleGenerator: string; - errTrigger: string; - approvalType: string; - timeout: number; - settingVisible?: boolean; - state?: NodeState; -} - -interface StartProps { - name: string; - code?: string; - settingVisible?: boolean; - update?: (values: any) => void; - properties?: StartProperties; -} - -export const StartView: React.FC = (props) => { - const [visible, setVisible] = React.useState(false); - - const state = props.properties?.state; - - return ( -
- -
- - {props.code && ( - <>({props.code}) - )} - - {props.name} -
- {props.settingVisible && ( - { - setVisible(true); - }} - /> - )} - - {state && ( -
- -
- )} - -
- ); -} - -class StartModel extends HtmlNodeModel { - setAttributes() { - const name = this.properties.name as string; - this.width = 200 + name.length * 10; - this.height = 45; - this.text.editable = false; - this.menu = []; - - this.anchorsOffset = [ - // [this.width / 2, 0], - [0, this.height / 2], - // [-this.width / 2, 0], - // [0, -this.height / 2], - ]; - } - -} - -class StartNode extends HtmlNode { - setHtml(rootEl: SVGForeignObjectElement) { - const {properties} = this.props.model as HtmlNodeModel; - const div = document.createElement('div'); - - const settingVisible = properties.settingVisible !== false; - - ReactDOM.createRoot(div).render( - { - this.props.model.setProperties(values); - }}/>, - ); - //需要清空 - rootEl.innerHTML = ''; - rootEl.appendChild(div); - super.setHtml(rootEl); - } -} - -export default { - type: 'start-node', - view: StartNode, - model: StartModel, -}; - diff --git a/mobile-ui/src/components/flow/nodes/StateLabel.tsx b/mobile-ui/src/components/flow/nodes/StateLabel.tsx deleted file mode 100644 index e2dc1006..00000000 --- a/mobile-ui/src/components/flow/nodes/StateLabel.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import React from "react"; -import {NodeState} from "@/components/flow/nodes/states"; -import {Tag} from "antd-mobile"; - -interface StateLabelProps { - state:NodeState; -} - -const StateLabel:React.FC = (props)=>{ - - const color = (state:NodeState) => { - switch (state) { - case "done": - return "success"; - case "undone": - return "processing"; - case "current": - return "warning"; - default: - return "default"; - } - } - - const label = (state:NodeState) => { - switch (state) { - case "done": - return "已执行"; - case "undone": - return "未执行"; - case "current": - return "当前节点"; - default: - return "待执行"; - } - } - - return ( - - {label(props.state)} - - ) -} - -export default StateLabel diff --git a/mobile-ui/src/components/flow/nodes/states.ts b/mobile-ui/src/components/flow/nodes/states.ts deleted file mode 100644 index b43944d0..00000000 --- a/mobile-ui/src/components/flow/nodes/states.ts +++ /dev/null @@ -1,2 +0,0 @@ -export type NodeState = "done" | "wait" | "undone" | "current"; - diff --git a/mobile-ui/src/components/flow/register.tsx b/mobile-ui/src/components/flow/register.tsx new file mode 100644 index 00000000..88ec70cb --- /dev/null +++ b/mobile-ui/src/components/flow/register.tsx @@ -0,0 +1,66 @@ +import {PostponedFormViewKey} from "@codingapi/ui-framework"; +import {UserSelectFormViewKey} from "@codingapi/ui-framework"; +import {ComponentBus} from "@codingapi/ui-framework"; +import PostponedFormView from "@/components/flow/PostponedFormView"; +import UserSelectFormView from "@/components/flow/UserSelectFormView"; +import {FlowApi, FlowApiContent} from "@codingapi/flow-mobile"; +import * as flowApi from "@/api/flow"; + + +ComponentBus.getInstance().registerComponent(PostponedFormViewKey,PostponedFormView); +ComponentBus.getInstance().registerComponent(UserSelectFormViewKey,UserSelectFormView); + + +class DefaultFlowApiImpl implements FlowApi{ + + startFlow(body: any): Promise { + return flowApi.startFlow(body); + } + + submitFlow(body: any): Promise { + return flowApi.submitFlow(body); + } + + removeFlow(body: any): Promise { + return flowApi.removeFlow(body); + } + + saveFlow(body: any): Promise { + return flowApi.saveFlow(body); + } + + postponedFlow(body: any): Promise { + return flowApi.postponed(body); + } + + triggerCustomFlow(body: any): Promise { + return flowApi.custom(body); + } + + transferFlow(body: any): Promise { + return flowApi.transfer(body); + } + + urgeFlow(body: any): Promise { + return flowApi.urge(body); + } + + recallFlow(body: any): Promise { + return flowApi.recall(body); + } + + trySubmitFlow(body: any): Promise { + return flowApi.trySubmitFlow(body); + } + + getDetailById(id: string): Promise { + return flowApi.detail(id,null); + } + + getDetailByWorkCode(workCode: string): Promise { + return flowApi.detail(null,workCode); + } + +} + +FlowApiContent.getInstance().registerFlowApi(new DefaultFlowApiImpl()); diff --git a/mobile-ui/src/components/flow/register/index.tsx b/mobile-ui/src/components/flow/register/index.tsx deleted file mode 100644 index 35686332..00000000 --- a/mobile-ui/src/components/flow/register/index.tsx +++ /dev/null @@ -1,8 +0,0 @@ -import {registerComponent} from "@/framework/ComponentBus"; -import {PostponedFormViewKey, UserSelectFormViewKey} from "@/components/flow/types"; -import PostponedFormView from "@/components/flow/plugins/PostponedFormView"; -import UserSelectFormView from "@/components/flow/plugins/UserSelectFormView"; - -registerComponent(PostponedFormViewKey, PostponedFormView); -registerComponent(UserSelectFormViewKey, UserSelectFormView); - diff --git a/mobile-ui/src/components/flow/store/FlowSlice.ts b/mobile-ui/src/components/flow/store/FlowSlice.ts deleted file mode 100644 index ab1f5bdd..00000000 --- a/mobile-ui/src/components/flow/store/FlowSlice.ts +++ /dev/null @@ -1,124 +0,0 @@ -import {configureStore, createSlice, PayloadAction} from '@reduxjs/toolkit'; -import {FlowResultMessage, UserSelectFormType} from "@/components/flow/types"; - -export interface FlowStore { - // 流程记录ID - recordId: string; - // 请求加载中 - requestLoading: boolean; - // 流程结果 - result: FlowResultMessage | null; - // 隐藏page的内容 - contentHiddenVisible: boolean; - // 版本号 - version: number; - // 数据版本号 - dataVersion: number; - - // 意见框展示状态 - opinionVisible: boolean; - - // 延期时间窗口状态 - postponedVisible: boolean; - // 选人窗口状态 - userSelectVisible: boolean; - // 选人状态 - userSelectMode: UserSelectMode | null; -} - -export interface UserSelectMode { - // 指定人员范围 - specifyUserIds?: number[]; - // 当前选择的人员 - currentUserIds?: number[]; - // 选人类型 - userSelectType: UserSelectFormType; - // 是否多选 - multiple: boolean; -} - -export type FlowStoreAction = { - updateState: (state: FlowStore, action: PayloadAction) => void; - initState: (state: FlowStore) => void; -} - -export const flowSlice = createSlice({ - name: 'flow', - initialState: { - recordId: '', - requestLoading: false, - result: null, - contentHiddenVisible: false, - postponedVisible: false, - opinionVisible: true, - userSelectVisible: false, - userSelectMode: null, - version: 0, - dataVersion: 0, - }, - reducers: { - updateState: (state, action) => { - const currentState = action.payload; - const keys = Object.keys(currentState); - if (keys.includes('requestLoading')) { - state.requestLoading = currentState.requestLoading; - } - if (keys.includes('recordId')) { - state.recordId = action.payload.recordId; - } - if (keys.includes('result')) { - state.result = action.payload.result; - state.contentHiddenVisible = state.result != null; - } - if (keys.includes('postponedVisible')) { - state.postponedVisible = action.payload.postponedVisible; - } - - if (keys.includes('userSelectMode')) { - state.userSelectVisible = true; - state.userSelectMode = action.payload.userSelectMode; - } - - if (keys.includes('version')) { - state.version = action.payload.version; - } - - if (keys.includes('dataVersion')) { - state.dataVersion = action.payload.dataVersion; - } - - if (keys.includes('userSelectVisible')) { - state.userSelectVisible = action.payload.userSelectVisible; - if (!state.userSelectVisible) { - state.userSelectMode = null; - } - } - }, - initState: (state) => { - state.recordId = ''; - state.requestLoading = false; - state.result = null; - state.postponedVisible = false; - state.opinionVisible = true; - state.contentHiddenVisible = false; - state.userSelectVisible = false; - state.userSelectMode = null; - state.version = 0; - state.dataVersion = 0; - } - }, -}); - - -export const { - updateState, - initState -} = flowSlice.actions; -export const flowStore = configureStore({ - reducer: { - flow: flowSlice.reducer - }, -}); - -export type FlowReduxState = ReturnType; - diff --git a/mobile-ui/src/components/flow/types.ts b/mobile-ui/src/components/flow/types.ts deleted file mode 100644 index 64d066a9..00000000 --- a/mobile-ui/src/components/flow/types.ts +++ /dev/null @@ -1,129 +0,0 @@ -import React from "react"; -import FormInstance from "@/components/form/domain/FormInstance"; - - -// 流程图中线的类型 -export type EdgeType = 'line' | 'polyline' | 'bezier'; - -// 延期表单视图Key -export const PostponedFormViewKey = 'PostponedFormView'; -// 选人表单视图Key -export const UserSelectFormViewKey = 'UserSelectFormViewKey'; - -// 自定义按钮类型 -export type ButtonType = - 'RELOAD' - | 'SAVE' - | 'START' - | 'SUBMIT' - | 'TRY_SUBMIT' - | 'SPECIFY_SUBMIT' - | 'REJECT' - | 'TRANSFER' - | 'RECALL' - | 'POSTPONED' - | 'URGE' - | 'CUSTOM' - | 'VIEW' - | 'REMOVE'; - -// 流程自定义按钮 -export interface FlowButton { - id: string; - eventKey: string; - groovy: string; - name: string; - order: number; - style: any; - type: ButtonType; -} - -// 延期表单 【拓展视图】 -export interface PostponedFormProps { - visible: boolean; - setVisible: (visible: boolean) => void; - onFinish: (timeout: number) => void; -} - -// 选人表单 【拓展视图】 - -export type UserSelectFormType = -// 选择下级流程节点的人员,约定人员id范围 - 'nextNodeUser' - // 选择转办人员,约定本单位下的人员 - | 'transfer' - // 选择所有人员,在流程配置上使用 - | 'users'; - -export interface UserSelectFormProps { - visible: boolean; - setVisible: (visible: boolean) => void; - onFinish: (values: FlowUser[]) => void; - // 选择模式 - multiple: boolean; - // 指定人员范围 - specifyUserIds?: number[]; - // 当前选择的人员 - currentUserIds?: number[]; - // 选人方式 - userSelectType: UserSelectFormType; -} - - -// 结果展示数据项目 -export interface FlowResultItem { - label: string, - value: string -} - -type FlowResultMessageState = 'success'|'info'|'warning'; - -// 结果展示数据信息 -export interface FlowResultMessage { - title: string, - closeable: boolean, - state: FlowResultMessageState, - items?: FlowResultItem[] -} - -// 流程用户 -export interface FlowUser { - id: string; - name: string; - - [key: string]: any; -} - -// 表单参数 -export interface FlowFormParams { - clazzName: string; - - [key: string]: any; -} - -// 流程表单数据 -export interface FlowFormViewProps { - // 表单数据 - data: FlowFormParams; - // 表单控制对象 - form: FormInstance; - // 数据版本 - dataVersion?: number; -} - -// 表单视图 -export interface FlowFormView { - [key: string]: React.ComponentType; -} - -export interface FlowViewProps { - // 流程编号 - id?: string; - // 流程的设计编号 - workCode?: string; - // 流程的视图数据 - view: React.ComponentType | FlowFormView; - // 表单参数,参数仅当在发起节点时才会传递 - formParams?: FlowFormParams; -} - diff --git a/mobile-ui/src/components/flow/view/index.scss b/mobile-ui/src/components/flow/view/index.scss deleted file mode 100644 index 4c104da0..00000000 --- a/mobile-ui/src/components/flow/view/index.scss +++ /dev/null @@ -1,161 +0,0 @@ -@use "@/config/variables" as *; - -$flow-footer-height: 80px; - -.flow-skeleton-header { - width: 100%; - height: 40px; -} - -.flow-skeleton-body { - height: 60vh; - width: 100%; -} - -.flow-view { - background-color: white !important; - width: 100%; -} - -.flow-view-content { - height: calc(100vh - $page-header-height - $flow-footer-height); - overflow: auto; -} - -.flow-view-footer { - height: $flow-footer-height; - box-shadow: inset 0 1px 0 $body-background-color; - display: flex; - align-items: center; - justify-content: center; - - .flow-view-footer-button { - margin: 10px; - font-size: 14px; - padding: 8px 8px; - width: 100%; - border-color: $theme-primary-color; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - } -} - -.flow-result-content { - padding: 10px; - - .flow-result-content-item { - display: flex; - font-size: $content-font-size; - margin-top: 10px; - - .flow-result-content-item-label { - flex: 1; - text-align: right; - margin: 5px; - } - - .flow-result-content-item-value { - flex: 1; - text-align: left; - margin: 5px; - } - } - - .flow-result-content-footer { - display: flex; - align-items: center; - justify-content: center; - margin-top: 20px; - - .flow-result-content-button { - margin: 10px; - font-size: $content-font-size; - padding: 8px 8px; - width: 40%; - border-color: $theme-primary-color; - } - } -} - -.flow-history-opinion-item { - padding: 5px; - font-size: $content-font-size; - border-bottom: 1px solid $body-background-color; - - .flow-history-opinion-item-row { - margin-top: 10px; - - span:first-child { - font-weight: bold; - } - } -} - -.flow-history { - .flow-basic { - - } - - .flow-history-list { - margin-top: 10px; - - .flow-history-list-item { - padding: 5px; - display: flex; - flex-direction: row; - margin-left: 10px; - - .flow-history-list-item-line { - width: 30px; - height: 30px; - background-color: $theme-primary-color; - border-radius: 50%; - position: relative; - } - - .flow-history-list-item-dot { - content: ''; - position: absolute; - top: 33px; - left: 15px; - width: 1px; - height: calc(100% + 75px); - background-color: $theme-primary-color; - z-index: 0; - } - - .flow-history-list-item-content { - display: flex; - flex-direction: column; - margin-left: 10px; - padding: 5px; - - .flow-history-list-item-title { - font-size: $title-font-size; - font-weight: bold; - } - - .flow-history-list-item-attr { - margin-top: 5px; - font-size: $content-font-size; - } - } - } - } -} - -.flow-chart { - transition: 0.3s; - border-radius: 12px; - - .flow-chart-content { - width: 0; - height: 0; - } - - .flow-img { - width: 100%; - height: 100%; - } -} diff --git a/mobile-ui/src/components/flow/view/index.tsx b/mobile-ui/src/components/flow/view/index.tsx deleted file mode 100644 index 2cdd5cf5..00000000 --- a/mobile-ui/src/components/flow/view/index.tsx +++ /dev/null @@ -1,107 +0,0 @@ -import React, {createContext, useEffect} from "react"; -import {Provider, useDispatch, useSelector} from "react-redux"; -import {FlowReduxState, flowStore, initState, updateState} from "@/components/flow/store/FlowSlice"; -import {FlowViewProps} from "@/components/flow/types"; -import {Skeleton} from "antd-mobile"; -import {FlowRecordContext} from "@/components/flow/domain/FlowRecordContext"; -import {FlowEventContext} from "@/components/flow/domain/FlowEventContext"; -import {detail} from "@/api/flow"; -import {FlowStateContext} from "@/components/flow/domain/FlowStateContext"; -import FlowPage from "@/components/flow/components/FlowPage"; -import {FlowTriggerContext} from "@/components/flow/domain/FlowTriggerContext"; -import {FlowButtonClickContext} from "@/components/flow/domain/FlowButtonClickContext"; -import "./index.scss"; -import FormInstance from "@/components/form/domain/FormInstance"; - -// 流程视图上下文属性 -interface FlowViewReactContextProps { - // 流程的详情控制上下文对象 - flowRecordContext: FlowRecordContext; - // 流程的事件控制上下文对象 - flowEventContext: FlowEventContext; - // 流程的状态数据上下文对象 - flowStateContext: FlowStateContext; - // 流程事件触发控制上下文对象 - flowTriggerContext: FlowTriggerContext; - // 流程按钮点击触发控制器上下文对象 - flowButtonClickContext: FlowButtonClickContext; - // 表单操作对象 - formInstance: FormInstance; - // 审批意见操作对象 - opinionInstance: FormInstance; -} - -export const FlowViewReactContext = createContext(null); - -const $FlowView: React.FC = (props) => { - const [data, setData] = React.useState(null); - - const dispatch = useDispatch(); - - const version = useSelector((state: FlowReduxState) => state.flow.version); - - // 请求流程详情数据 - const loadFlowDetail = () => { - if (props.id) { - detail(props.id, null).then(res => { - if (res.success) { - setData(res.data); - } - }); - return; - } - if (props.workCode) { - detail(null, props.workCode).then(res => { - if (res.success) { - setData(res.data); - } - }); - return; - } - } - - useEffect(() => { - if(data){ - const dataVersion = Math.random(); - dispatch(updateState({dataVersion:dataVersion})); - } - }, [data]); - - useEffect(() => { - return ()=>{ - dispatch(initState()); - } - }, []); - - useEffect(() => { - loadFlowDetail(); - }, [version]); - - - return ( - <> - {!data && ( - <> - - - - )} - {data && ( - - )} - - ) -} - -const FlowView: React.FC = (props) => { - return ( - - <$FlowView {...props} /> - - ) -} - -export default FlowView; diff --git a/mobile-ui/src/components/form/captcha.tsx b/mobile-ui/src/components/form/captcha.tsx deleted file mode 100644 index 1e0db267..00000000 --- a/mobile-ui/src/components/form/captcha.tsx +++ /dev/null @@ -1,57 +0,0 @@ -import React, {useEffect, useState} from "react"; -import {FormItemProps} from "@/components/form/types"; -import {Form, Input} from "antd-mobile"; -import formFieldInit from "@/components/form/common"; -import "./form.scss"; - -const FormCaptcha: React.FC = (props) => { - const [captchaImg, setCaptchaImg] = useState(''); - const {formContext,rules} = formFieldInit(props); - - const reloadCaptcha = () => { - props.onCaptchaRefresh && props.onCaptchaRefresh().then((res) => { - if(res) { - setCaptchaImg(res.url); - props.onCaptchaChange && props.onCaptchaChange(res.code); - } - }); - } - - useEffect(() => { - reloadCaptcha(); - }, []) - - return ( - - ) -} - -export default FormCaptcha; diff --git a/mobile-ui/src/components/form/cascader.tsx b/mobile-ui/src/components/form/cascader.tsx deleted file mode 100644 index 0840151c..00000000 --- a/mobile-ui/src/components/form/cascader.tsx +++ /dev/null @@ -1,113 +0,0 @@ -import React, {useEffect} from "react"; -import {FormItemProps} from "@/components/form/types"; -import {Cascader, Form} from "antd-mobile"; -import {RightOutline} from "antd-mobile-icons"; -import formFieldInit from "@/components/form/common"; -import "./form.scss"; - -const valueToForm = (value: string) => { - if (value && value.length > 0) { - return value.split(","); - } - return value; -} - -const formToValue = (value: string[]) => { - if (value && value.length > 0) { - return value.join(",") - } - return value; -} - -const FormCascader: React.FC = (props) => { - - - const [visible, setVisible] = React.useState(false); - const [options, setOptions] = React.useState(props.options); - - const {formContext, rules} = formFieldInit(props, () => { - reloadOptions(); - }); - - const reloadOptions = () => { - if (props.loadOptions) { - props.loadOptions(formContext).then(res => { - setOptions(res); - }); - } - } - - useEffect(() => { - reloadOptions(); - }, []); - - useEffect(() => { - if (visible) { - reloadOptions(); - } - }, [visible]); - - return ( - - ) -} - -export default FormCascader; diff --git a/mobile-ui/src/components/form/checkbox.tsx b/mobile-ui/src/components/form/checkbox.tsx deleted file mode 100644 index 64437625..00000000 --- a/mobile-ui/src/components/form/checkbox.tsx +++ /dev/null @@ -1,79 +0,0 @@ -import React, {useEffect} from "react"; -import {FormItemProps} from "@/components/form/types"; -import {Checkbox, Form, Space} from "antd-mobile"; -import formFieldInit from "@/components/form/common"; -import "./form.scss"; - -const valueToForm = (value: string) => { - if (value && value.length > 0) { - return value.split(","); - } - return value; -} - -const formToValue = (value: string[]) => { - if (value && value.length > 0) { - return value.join(",") - } - return value; -} - -const FormCheckbox: React.FC = (props) => { - const [options, setOptions] = React.useState(props.options); - - const {formContext, rules} = formFieldInit(props, () => { - reloadOptions(); - }); - - const reloadOptions = () => { - if (props.loadOptions) { - props.loadOptions(formContext).then(res => { - setOptions(res); - }); - } - } - - useEffect(() => { - reloadOptions(); - }, []); - - return ( - - ) -} - -export default FormCheckbox; diff --git a/mobile-ui/src/components/form/common.tsx b/mobile-ui/src/components/form/common.tsx deleted file mode 100644 index 6aa7bd0b..00000000 --- a/mobile-ui/src/components/form/common.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import React, {useEffect} from "react"; -import {FormContext} from "@/components/form"; -import {FormItemProps} from "@/components/form/types"; - -const formFieldInit = (props: FormItemProps,reloadOption?:()=>void) => { - const formContext = React.useContext(FormContext) || undefined; - const validateContext = formContext?.getFormValidateContext(); - const [random, setRandom] = React.useState(0); - - const rules= props.required?[{required: true}]:[]; - - useEffect(() => { - if (props.validateFunction) { - if (validateContext) { - if (props.disabled || props.hidden) { - // do nothing - } else { - validateContext.addValidateFunction(props.name, props.validateFunction); - } - } - } - const reloadContext = formContext?.getFormFieldReloadListenerContext(); - if (reloadContext) { - reloadContext.addListener(props.name, () => { - setRandom(Math.random); - }); - } - - const optionContext = formContext?.getFormFieldOptionListenerContext(); - if (optionContext) { - optionContext.addListener(props.name, () => { - if(reloadOption){ - reloadOption(); - } - }); - } - }, [formContext]); - - return {formContext, validateContext, rules}; -} - -export default formFieldInit; diff --git a/mobile-ui/src/components/form/date.tsx b/mobile-ui/src/components/form/date.tsx deleted file mode 100644 index d326d3fc..00000000 --- a/mobile-ui/src/components/form/date.tsx +++ /dev/null @@ -1,102 +0,0 @@ -import React from "react"; -import {FormItemProps} from "@/components/form/types"; -import {DatePicker, Form} from "antd-mobile"; -import {RightOutline} from "antd-mobile-icons"; -import dayjs from "dayjs"; -import formFieldInit from "@/components/form/common"; -import "./form.scss"; - -export const dateLabelRenderer = (type: string, data: number) => { - switch (type) { - case 'year': - return data + '年' - case 'month': - return data + '月' - case 'day': - return data + '日' - case 'hour': - return data + '时' - case 'minute': - return data + '分' - case 'second': - return data + '秒' - default: - return data - } -} - -const FormDate: React.FC = (props) => { - - const {formContext, rules} = formFieldInit(props); - - const format = props.dateFormat || 'YYYY-MM-DD'; - const precision = props.datePrecision || "day"; - const [visible, setVisible] = React.useState(false); - - return ( - - ) -} - -export default FormDate; diff --git a/mobile-ui/src/components/form/domain/FormInstance.tsx b/mobile-ui/src/components/form/domain/FormInstance.tsx deleted file mode 100644 index e3e42d3b..00000000 --- a/mobile-ui/src/components/form/domain/FormInstance.tsx +++ /dev/null @@ -1,323 +0,0 @@ -import {FormValidateContext} from "@/components/form/validate"; -import {FormFieldOptionListenerContext, FormFieldReloadListenerContext} from "@/components/form/listener"; -import {FormInstance as MobileFormInstance} from "rc-field-form/es/interface"; -import {FormField} from "@/components/form/types"; -import {NamePath} from "antd-mobile/es/components/form"; -import {Form as MobileForm, Toast} from "antd-mobile"; -import {FiledData, FormAction} from "@/components/form"; -import React from "react"; - -class FormInstance { - private readonly validateContext: FormValidateContext; - private readonly reloadContext: FormFieldReloadListenerContext; - private readonly optionContext: FormFieldOptionListenerContext; - private readonly formInstance: MobileFormInstance; - private readonly formAction: FormAction; - private fields: FormField[]; - - private fieldsUpdateDispatch: React.Dispatch> | undefined; - - public setFieldsUpdateDispatch = (fieldsUpdateDispatch: React.Dispatch>) => { - this.fieldsUpdateDispatch = fieldsUpdateDispatch; - } - - private updateFields = (resetFields:(prevState: FormField[]) => FormField[]) => { - this.fields = resetFields(this.fields); - if (this.fieldsUpdateDispatch) { - this.fieldsUpdateDispatch(resetFields); - } - } - - private namePathEqual = (name1: NamePath, name2: NamePath) => { - if (Array.isArray(name1) && Array.isArray(name2)) { - if (name1.length !== name2.length) { - return false; - } - for (let i = 0; i < name1.length; i++) { - if (name1[i] !== name2[i]) { - return false; - } - } - return true; - } - return name1 === name2; - } - - public submit = async () => { - const res = await this.validateContext.validate(this); - if (res) { - this.formInstance.submit(); - } - } - - public reset = (values?: any) => { - this.formInstance.resetFields(); - if (values) { - this.formInstance.setFieldsValue(values); - this.reloadContext.notifyAll(); - } - } - - public hidden = (name: NamePath) => { - if (this.fields.length == 0) { - Toast.show('表单项未加载'); - return; - } - this.updateFields(prevFields => prevFields.map((field) => { - if (this.namePathEqual(field.props.name, name)) { - return { - ...field, - props: { - ...field.props, - hidden: true, - required: false - } - } - } - return field; - })); - this.validateContext.clear(); - } - - public required = (name: NamePath, required: boolean) => { - if (this.fields.length == 0) { - Toast.show('表单项未加载'); - return; - } - this.updateFields(prevFields => prevFields.map((field) => { - if (this.namePathEqual(field.props.name,name)) { - return { - ...field, - props: { - ...field.props, - required: required - } - } - } - return field; - })); - this.validateContext.clear(); - } - - public show = (name: NamePath) => { - if (this.fields.length == 0) { - Toast.show('表单项未加载'); - return; - } - this.updateFields(prevFields => prevFields.map((field) => { - if (this.namePathEqual(field.props.name,name)) { - return { - ...field, - props: { - ...field.props, - hidden: false - } - } - } - return field; - })); - this.validateContext.clear(); - } - - public disable = (name: NamePath) => { - if (this.fields.length == 0) { - Toast.show('表单项未加载'); - return; - } - this.updateFields(prevFields => prevFields.map((field) => { - if (this.namePathEqual(field.props.name,name)) { - return { - ...field, - props: { - ...field.props, - disabled: true - } - } - } - return field; - })); - this.validateContext.clear(); - } - - public disableAll = () => { - if (this.fields.length == 0) { - Toast.show('表单项未加载'); - return; - } - this.updateFields(prevFields => prevFields.map((field) => { - return { - ...field, - props: { - ...field.props, - disabled: true - } - } - })); - this.validateContext.clear(); - } - - public enable = (name: NamePath) => { - if (this.fields.length == 0) { - Toast.show('表单项未加载'); - return; - } - this.updateFields(prevFields => prevFields.map((field) => { - if (this.namePathEqual(field.props.name,name)) { - return { - ...field, - props: { - ...field.props, - disabled: false - } - } - } - return field; - })); - this.validateContext.clear(); - } - - public enableAll = () => { - if (this.fields.length == 0) { - Toast.show('表单项未加载'); - return; - } - this.updateFields(prevFields => prevFields.map((field) => { - return { - ...field, - props: { - ...field.props, - disabled: false - } - } - })); - this.validateContext.clear(); - } - - public remove = (name: NamePath) => { - if (this.fields.length == 0) { - Toast.show('表单项未加载'); - return; - } - this.updateFields(prevFields => prevFields.filter((field) => !this.namePathEqual(field.props.name,name))); - this.validateContext.clear(); - } - - public create = (field: FormField, index?: number) => { - if (this.fields.length == 0) { - Toast.show('表单项未加载'); - return; - } - this.updateFields(prevFields => { - const filteredFields = prevFields.filter((item) => item.props.name !== field.props.name); - if (index === undefined || index < 0) { - return [...filteredFields, field]; - } else { - const newFields = [...filteredFields]; - newFields.splice(index, 0, field); - return newFields; - } - }); - this.validateContext.clear(); - } - - public getFieldValue = (name: NamePath) => { - return this.formInstance.getFieldValue(name); - } - - public getFieldsValue = () => { - return this.formInstance.getFieldsValue(); - } - - public getFieldProps = (name: NamePath) => { - for (const field of this.fields) { - if (this.namePathEqual(field.props.name, name)) { - return field; - } - } - return null; - } - - public reloadOptions = (name: NamePath) => { - this.optionContext.notify(name); - } - - public reloadAllOptions = () => { - this.optionContext.notifyAll(); - } - - public setFieldValue = (name: NamePath, value: any) => { - this.formInstance.setFieldValue(name, value); - this.reloadContext.notify(name); - this.validateContext?.validateField(name, this); - } - - public setFieldsValue = (values: any) => { - this.formInstance.setFieldsValue(values); - this.reloadContext.notifyAll(); - } - - public setFields = (fields: FiledData[]) => { - this.formInstance.setFields(fields); - } - - public validate = () => { - return this.validateContext.validate(this); - } - - public resetFields = (fields:FormField[]) => { - this.fields = fields; - } - - constructor() { - this.validateContext = new FormValidateContext(); - this.reloadContext = new FormFieldReloadListenerContext(); - this.optionContext = new FormFieldOptionListenerContext(); - this.formInstance = MobileForm.useForm()[0]; - this.fields = []; - this.formAction = { - submit: this.submit, - reset: this.reset, - hidden: this.hidden, - show: this.show, - remove: this.remove, - create: this.create, - disable: this.disable, - disableAll: this.disableAll, - enable: this.enable, - enableAll: this.enableAll, - required: this.required, - getFieldValue: this.getFieldValue, - getFieldsValue: this.getFieldsValue, - getFieldProps: this.getFieldProps, - reloadOptions: this.reloadOptions, - reloadAllOptions: this.reloadAllOptions, - setFieldValue: this.setFieldValue, - setFieldsValue: this.setFieldsValue, - setFields: this.setFields, - validate: this.validate, - } - } - - public getFormAction = () => { - return this.formAction; - } - - public getFormValidateContext = () => { - return this.validateContext; - } - - public getFormFieldReloadListenerContext = () => { - return this.reloadContext; - } - - public getFormFieldOptionListenerContext = () => { - return this.optionContext; - } - - public getFormControlInstance = ():MobileFormInstance => { - return this.formInstance; - } - -} - -export default FormInstance; diff --git a/mobile-ui/src/components/form/factory.tsx b/mobile-ui/src/components/form/factory.tsx deleted file mode 100644 index 1007193a..00000000 --- a/mobile-ui/src/components/form/factory.tsx +++ /dev/null @@ -1,163 +0,0 @@ -import React from "react"; -import {FormField} from "@/components/form/types"; -import FormInput from "@/components/form/input"; -import FormPassword from "@/components/form/password"; -import FormSelect from "@/components/form/select"; -import FormDate from "@/components/form/date"; -import FormRadio from "@/components/form/radio"; -import FormTextArea from "@/components/form/textarea"; -import FormCascader from "@/components/form/cascader"; -import FormCheckbox from "@/components/form/checkbox"; -import FormUploader from "@/components/form/uploder"; -import FormSwitch from "@/components/form/switch"; -import FormStepper from "@/components/form/stepper"; -import FormSlider from "@/components/form/slider"; -import FormRate from "@/components/form/rate"; -import FormSelector from "@/components/form/selector"; -import FormCaptcha from "@/components/form/captcha"; - -class FormFactory { - - static create = (field: FormField) => { - const type = field.type; - const props = field.props; - - if (type === 'input') { - return ( - - ) - } - - if (type === 'captcha') { - return ( - - ) - } - - if (type === 'password') { - return ( - - ) - } - - if (type === 'cascader') { - return ( - - ) - } - - if (type === 'select') { - return ( - - ) - } - - if (type === 'date') { - return ( - - ) - } - - if (type === 'radio') { - return ( - - ) - } - - if (type === 'checkbox') { - return ( - - ) - } - - if (type === 'textarea') { - return ( - - ) - } - - if (type === 'uploader') { - return ( - - ) - } - - if (type === 'switch') { - return ( - - ) - } - - if (type === 'stepper') { - return ( - - ) - } - - if (type === 'slider') { - return ( - - ) - } - - if (type === 'rate') { - return ( - - ) - } - - if (type === 'selector') { - return ( - - ) - } - } - -} - -export default FormFactory; diff --git a/mobile-ui/src/components/form/form.scss b/mobile-ui/src/components/form/form.scss deleted file mode 100644 index 3e306233..00000000 --- a/mobile-ui/src/components/form/form.scss +++ /dev/null @@ -1,145 +0,0 @@ -@use "../../config/variables" as *; - -.placeholder-span { - color: $from-placeholder-color; -} - -.form-uploader-file { - height: 90px; - width: 90px; - display: flex; - justify-content: center; - align-items: center; - flex-wrap: wrap; /* 允许子元素换行 */ - text-align: center; /* 水平居中 */ - word-wrap: break-word; /* 超出容器时换行 */ - border: 0.5px solid $body-background-color; - position: relative; - - .delete-icon { - margin-top: -5px; - margin-right: -5px; - position: absolute; - right: 0; - top: 0; - width: 15px; - height: 15px; - } - - a { - width: 100%; - font-size: 12px; - } -} - -.form-password { - display: flex; - - .form-password-eye { - flex: none; - margin-left: 8px; - padding: 4px; - cursor: pointer; - - svg { - display: block; - } - } -} - - -.select-popup { - padding: 12px; - width: 100%; - - .select-popup-content { - height: 45vh; - padding: 15px; - width: 100%; - overflow-y: scroll; - - .checkbox-parent { - cursor: pointer; - display: flex; - justify-content: space-between; - } - - - .select-popup-content-custom-form { - - - .select-popup-content-custom-footer { - display: flex; - justify-content: space-between; - align-items: center; - margin: 30px; - - button{ - width: 300px; - margin: 10px; - } - } - - } - - } - - .select-popup-header { - height: 30px; - display: flex; - justify-content: space-between; - align-items: center; - padding: 5px; - - a { - font-weight: bold; - } - } - - .select-popup-navbar { - padding: 10px 5px 0 10px; - height: 20px; - overflow-x: auto; - white-space: nowrap; - width: 100vw; - display: flex; - - .select-popup-navbar-item { - height: 20px; - align-items: center; - - .arrow { - color: $theme-primary-color; - } - - .span { - margin-left: 10px; - color: $theme-primary-color; - } - } - } - - .select-popup-navbar::-webkit-scrollbar { - display: none; - } - - .select-popup-search { - height: 30px; - padding: 5px; - display: flex; - align-items: center; - - .select-popup-search-bar { - flex: 9; - height: 30px; - } - - .select-popup-search-button { - flex: 1; - height: 20px; - } - } - -} - - diff --git a/mobile-ui/src/components/form/index.tsx b/mobile-ui/src/components/form/index.tsx deleted file mode 100644 index 59dec584..00000000 --- a/mobile-ui/src/components/form/index.tsx +++ /dev/null @@ -1,135 +0,0 @@ -import React, {useEffect} from "react"; -import {Form as MobileForm} from "antd-mobile"; -import FormFactory from "@/components/form/factory"; -import {FormField} from "@/components/form/types"; -import {NamePath} from "antd-mobile/es/components/form"; -import "./form.scss"; -import FormInstance from "@/components/form/domain/FormInstance"; - -export interface FiledData { - name: NamePath; - errors?: string[]; -} - -export interface FormAction { - // 提交表单,提取之前会先校验表单 - submit: () => void; - // 重置表单,恢复表单项最初的状态 - reset: (values?: any) => void; - // 隐藏表单项,隐藏后的表单项不会显示在表单中,但是值会被提交 - hidden: (name: NamePath) => void; - // 展示表单项,展示后的表单项会显示在表单中,值也会被提交 - show: (name: NamePath) => void; - // 删除表单项,删除后的表单项不会显示在表单中,值也不会被提交,在fields配置的情况下生效 - remove: (name: NamePath) => void; - // 添加表单项,添加后的表单项会显示在表单中,值也会被提交,在fields配置的情况下生效 - create: (field: FormField, index?: number) => void; - // 禁用表单项,禁用后的表单项还会被提交 - disable: (name: NamePath) => void; - // 全部禁用,禁用后的表单项还会被提交 - disableAll:()=>void; - // 启用表单项,启用后的表单项还会被提交 - enable: (name: NamePath) => void; - // 全部启用,启用后的表单项还会被提交 - enableAll:()=>void; - // 必填选项控制,true为必填false为非必填提示 - required: (name: NamePath,required:boolean) => void; - // 获取字段的值 - getFieldValue: (name: NamePath) => any; - // 重新加载选项 - reloadOptions: (name: NamePath) => any; - // 重新加载所有选项 - reloadAllOptions: () => any; - // 获取全部字段的值 - getFieldsValue: () => any; - // 设置字段的值 - setFieldValue: (name: NamePath, value: any) => void; - // 设置全部字段的值 - setFieldsValue: (values: any) => void; - // 设置Field字段 - setFields: (fields: FiledData[]) => void; - // 获取Field属性 - getFieldProps: (name: NamePath) => FormField | null; - // 校验表单 - validate: () => Promise; -} - -export interface FormProps { - // 表单字段 - loadFields?: ()=>Promise; - // 表单提交事件 - onFinish?: (values: any) => Promise; - // form布局,默认vertical - layout?: 'horizontal' | 'vertical'; - // children元素 - children?: React.ReactNode; - // footer元素 - footer?: React.ReactNode; - // 初始化值 - initialValues?: any; - // 表单实例 - form?: FormInstance; -} - - -export const FormContext = React.createContext(null); - -const FormComponent: React.FC = (props) => { - - const formInstance = props.form? props.form : new FormInstance(); - - const [fields, setFields] = React.useState([]); - formInstance.setFieldsUpdateDispatch(setFields); - - const formControl = formInstance.getFormControlInstance() ; - - const reloadFields = ()=>{ - if(props.loadFields){ - props.loadFields().then(fields=>{ - setFields(fields); - formInstance.resetFields(fields); - }) - } - } - - useEffect(() => { - reloadFields(); - }, [props.loadFields]); - - - return ( - - { - props.onFinish && props.onFinish(values); - }} - initialValues={props.initialValues} - layout={props.layout} - footer={props.footer} - > - {fields.length > 0 && fields.map((field) => { - return FormFactory.create(field) as React.ReactNode; - })} - - {props.children} - - - - ) -} - -type FormType = typeof FormComponent; -type FormComponentType = FormType & { - useForm: ()=>FormInstance; -}; - -const Form = FormComponent as FormComponentType; -Form.useForm = ()=>{ - return new FormInstance(); -}; - -export default Form; - diff --git a/mobile-ui/src/components/form/input.tsx b/mobile-ui/src/components/form/input.tsx deleted file mode 100644 index e89401ba..00000000 --- a/mobile-ui/src/components/form/input.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import React from "react"; -import {FormItemProps} from "@/components/form/types"; -import {Form, Input} from "antd-mobile"; -import formFieldInit from "@/components/form/common"; -import "./form.scss"; - -const FormInput: React.FC = (props) => { - - const inputType = props.inputType || "text"; - const {formContext, rules} = formFieldInit(props); - - return ( - - ) -} - -export default FormInput; diff --git a/mobile-ui/src/components/form/listener.ts b/mobile-ui/src/components/form/listener.ts deleted file mode 100644 index 47c13d06..00000000 --- a/mobile-ui/src/components/form/listener.ts +++ /dev/null @@ -1,64 +0,0 @@ -import {NamePath} from "antd-mobile/es/components/form"; - -// Form字段组件刷新控制监听上下文对象 -export class FormFieldReloadListenerContext { - - private readonly map:Mapvoid>; - - constructor() { - this.map = new Map(); - } - - public addListener(name:NamePath,listener:()=>void){ - const key = Array.isArray(name)?name.join("."):name; - this.map.set(key, listener); - } - - - public notify(name:NamePath){ - const key = Array.isArray(name)?name.join("."):name; - const listener = this.map.get(key); - if(listener){ - listener(); - } - } - - public notifyAll(){ - this.map.forEach(listener=>{ - listener(); - }) - } - -} - -// Form字段组件选项刷新控制监听上下文对象 -export class FormFieldOptionListenerContext { - - private readonly map:Mapvoid>; - - constructor() { - this.map = new Map(); - } - - public addListener(name:NamePath,listener:()=>void){ - const key = Array.isArray(name)?name.join("."):name; - this.map.set(key, listener); - } - - - public notify(name:NamePath){ - const key = Array.isArray(name)?name.join("."):name; - const listener = this.map.get(key); - if(listener){ - listener(); - } - } - - public notifyAll(){ - this.map.forEach(listener=>{ - listener(); - }) - } - -} - diff --git a/mobile-ui/src/components/form/password.tsx b/mobile-ui/src/components/form/password.tsx deleted file mode 100644 index 55ad6ed7..00000000 --- a/mobile-ui/src/components/form/password.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import React from "react"; -import {FormItemProps} from "@/components/form/types"; -import {Form, Input} from "antd-mobile"; -import {EyeInvisibleOutline, EyeOutline} from "antd-mobile-icons"; -import formFieldInit from "@/components/form/common"; -import "./form.scss"; - -const FormPassword: React.FC = (props) => { - - const [visible, setVisible] = React.useState(false); - - const {formContext, rules} = formFieldInit(props); - - return ( - - ) -} - -export default FormPassword; diff --git a/mobile-ui/src/components/form/radio.tsx b/mobile-ui/src/components/form/radio.tsx deleted file mode 100644 index 00b0ad08..00000000 --- a/mobile-ui/src/components/form/radio.tsx +++ /dev/null @@ -1,57 +0,0 @@ -import React, {useEffect} from "react"; -import {FormItemProps} from "@/components/form/types"; -import {Form, Radio, Space} from "antd-mobile"; -import formFieldInit from "@/components/form/common"; -import "./form.scss"; - -const FormRadio: React.FC = (props) => { - const [options, setOptions] = React.useState(props.options); - - const {formContext, rules} = formFieldInit(props, () => { - reloadOptions(); - }); - - const reloadOptions = () => { - if (props.loadOptions) { - props.loadOptions(formContext).then(res => { - setOptions(res); - }); - } - } - - useEffect(() => { - reloadOptions(); - }, []); - - return ( - - ) -} - -export default FormRadio; diff --git a/mobile-ui/src/components/form/rate.tsx b/mobile-ui/src/components/form/rate.tsx deleted file mode 100644 index 6936325e..00000000 --- a/mobile-ui/src/components/form/rate.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import React from "react"; -import {FormItemProps} from "@/components/form/types"; -import {Form, Rate} from "antd-mobile"; -import formFieldInit from "@/components/form/common"; -import "./form.scss"; - -const FormRate: React.FC = (props) => { - const {formContext, rules} = formFieldInit(props); - - return ( - - ) -} - -export default FormRate; diff --git a/mobile-ui/src/components/form/select.tsx b/mobile-ui/src/components/form/select.tsx deleted file mode 100644 index 8bae7b86..00000000 --- a/mobile-ui/src/components/form/select.tsx +++ /dev/null @@ -1,426 +0,0 @@ -import React, {useEffect} from "react"; -import {FormItemProps, FormOption} from "@/components/form/types"; -import {Button, CheckList, Form as AntdForm, InfiniteScroll, Popup, PullToRefresh, SearchBar} from "antd-mobile"; -import {RightOutline, SetOutline} from "antd-mobile-icons"; -import formFieldInit from "@/components/form/common"; -import Form from "@/components/form"; -import "./form.scss"; - - -const valueToForm = (value: string) => { - if (value && value.length > 0) { - return value.split(","); - } - return value; -} - -const formToValue = (value: string[]) => { - if (value && value.length > 0) { - return value.join(",") - } - return value; -} - -interface CheckboxItemProps { - item: FormOption; - paths: FormOption[]; - setPaths: (paths: FormOption[]) => void; - setOptions: (options: FormOption[]) => void; - -} - -const CheckboxItem: React.FC = (props) => { - const {item, paths, setPaths, setOptions} = props; - if (item.children && item.children.length > 0) { - return ( - -
{ - setPaths([...paths, item]); - setOptions(item.children || []); - }} - > - {item.label} - -
-
- ) - } - return ( - - {item.label} - - ) -} - -interface CheckboxListViewProps { - data: FormOption[]; - paths: FormOption[]; - setPaths: (paths: FormOption[]) => void; - setOptions: (options: FormOption[]) => void; -} - -const CheckboxListView: React.FC = (props) => { - const {data} = props; - const pageSize = 20; - - const [currentPage, setCurrentPage] = React.useState(1); - - const [list, setList] = React.useState([]); - - const [hasMore, setHasMore] = React.useState(true); - - const reload = () => { - const currentPage = 1; - if (data.length > 0) { - const list = data.slice((currentPage - 1) * pageSize, currentPage * pageSize); - setList(list); - setHasMore(true); - } else { - setList([]); - setHasMore(false); - } - setCurrentPage(currentPage); - } - - const loadMore = () => { - setCurrentPage(prevState => { - const newPage = prevState + 1; - if (newPage * pageSize >= data.length) { - setHasMore(false); - } else { - setHasMore(true); - } - setList(prevState => { - const list = data.slice((newPage - 1) * pageSize, newPage * pageSize); - return [...prevState, ...list] - }); - return newPage; - }); - } - - - useEffect(() => { - reload(); - }, [props.data]); - - return ( - <> - { - reload(); - }} - > - {list && list.map((item: FormOption, index: number) => { - return - })} - - {hasMore && ( - { - loadMore(); - }} - hasMore={hasMore} - /> - )} - - - - ) -} - -const FormSelect: React.FC = (props) => { - - const [visible, setVisible] = React.useState(false); - const [searchText, setSearchText] = React.useState(''); - - const {formContext, rules} = formFieldInit(props, () => { - reloadOptions(); - }); - - const currentValue = valueToForm(formContext?.getFieldValue(props.name)) as string[] || valueToForm(props.value) as string[] || []; - - const [selected, setSelected] = React.useState(currentValue); - - const [settingOptionVisible, setSettingOptionVisible] = React.useState(false); - - // 当前页面展示的选项的数据,会随着树级目录进入子数据,从而更新数据 - const [options, setOptions] = React.useState(props.options); - - // 用于展示数据的缓存数据,在数据插入不在更新 - const [optionCaches, setOptionCaches] = React.useState(props.options); - - const [paths, setPaths] = React.useState([]); - - const CheckBoxValueText = () => { - const currentValue = valueToForm(formContext?.getFieldValue(props.name)) as string[] || valueToForm(props.value) as string[] || []; - const optionLabelFetch = (value: string) => { - let label = value; - let fetchState = false; - const loadLabel = (item: FormOption) => { - if (!fetchState) { - if (item.value === value) { - label = item.label; - fetchState = true; - } - if (item.children) { - item.children.forEach(child => { - loadLabel(child); - }) - } - } - } - - optionCaches?.forEach(item => { - loadLabel(item); - }) - return label; - } - const displaySpan = (list: string[]) => { - if (list.length > 0) { - return ( - { - setVisible(true); - }}> - {list && - list.map(item => { - return optionLabelFetch(item); - }).join(",") - } - - ) - } else { - return ( - { - setVisible(true); - }}> - {props.placeholder || "请选择"} - - ) - } - } - return displaySpan(currentValue); - } - - const reloadOptions = () => { - if (props.loadOptions) { - props.loadOptions(formContext).then(list => { - setOptions(list); - setOptionCaches(list); - }); - } - } - - useEffect(() => { - setPaths([]); - reloadOptions(); - }, []); - - - useEffect(() => { - setSearchText(''); - if (visible) { - setPaths([]); - setSelected(currentValue); - reloadOptions(); - } - }, [visible]); - - const selectOptionFormEditInstance = Form.useForm(); - - - const handlerOptionFormFinish = () => { - if (props.onSelectOptionFormFinish && selectOptionFormEditInstance && formContext) { - props.onSelectOptionFormFinish( - formContext, - selectOptionFormEditInstance, - reloadOptions, - () => { - setSettingOptionVisible(false); - setVisible(false); - } - ); - } - } - - return ( - - ) -} - -export default FormSelect; diff --git a/mobile-ui/src/components/form/selector.tsx b/mobile-ui/src/components/form/selector.tsx deleted file mode 100644 index 42daecea..00000000 --- a/mobile-ui/src/components/form/selector.tsx +++ /dev/null @@ -1,73 +0,0 @@ -import React, {useEffect} from "react"; -import {FormItemProps} from "@/components/form/types"; -import {Form, Selector} from "antd-mobile"; -import formFieldInit from "@/components/form/common"; -import "./form.scss"; - -const valueToForm = (value: string) => { - if (value && value.length > 0) { - return value.split(","); - } - return value; -} - -const formToValue = (value: string[]) => { - if (value && value.length > 0) { - return value.join(",") - } - return value; -} - - -const FormSelector: React.FC = (props) => { - - const [options, setOptions] = React.useState(props.options); - - const {formContext, rules} = formFieldInit(props, () => { - reloadOptions(); - }); - - const reloadOptions = () => { - if (props.loadOptions) { - props.loadOptions(formContext).then(res => { - setOptions(res); - }); - } - } - - useEffect(() => { - reloadOptions(); - }, []); - - return ( - - ) -} - -export default FormSelector; diff --git a/mobile-ui/src/components/form/slider.tsx b/mobile-ui/src/components/form/slider.tsx deleted file mode 100644 index 08291774..00000000 --- a/mobile-ui/src/components/form/slider.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import React from "react"; -import {FormItemProps} from "@/components/form/types"; -import {Form, Slider} from "antd-mobile"; -import formFieldInit from "@/components/form/common"; -import "./form.scss"; - -const FormSlider: React.FC = (props) => { - - const {formContext, rules} = formFieldInit(props); - - return ( - - ) -} - -export default FormSlider; diff --git a/mobile-ui/src/components/form/stepper.tsx b/mobile-ui/src/components/form/stepper.tsx deleted file mode 100644 index 5d6d4843..00000000 --- a/mobile-ui/src/components/form/stepper.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import React from "react"; -import {FormItemProps} from "@/components/form/types"; -import {Form, Stepper} from "antd-mobile"; -import formFieldInit from "@/components/form/common"; -import "./form.scss"; - -const FormStepper: React.FC = (props) => { - - const {formContext, rules} = formFieldInit(props); - - return ( - - ) -} - -export default FormStepper; diff --git a/mobile-ui/src/components/form/switch.tsx b/mobile-ui/src/components/form/switch.tsx deleted file mode 100644 index 566b48a5..00000000 --- a/mobile-ui/src/components/form/switch.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import React from "react"; -import {FormItemProps} from "@/components/form/types"; -import {Form, Switch as AntSwitch} from "antd-mobile"; -import {SwitchProps as AntdSwitchProps} from "antd-mobile/es/components/switch/switch"; -import formFieldInit from "@/components/form/common"; -import "./form.scss"; - -interface SwitchProps extends AntdSwitchProps { - value?: boolean; -} - -const Switch: React.FC = ({value, ...props}) => { - return ( - - ) -} - -const FormSwitch: React.FC = (props) => { - - const {formContext, rules} = formFieldInit(props); - - return ( - - ) -} - -export default FormSwitch; diff --git a/mobile-ui/src/components/form/textarea.tsx b/mobile-ui/src/components/form/textarea.tsx deleted file mode 100644 index 0807f0b2..00000000 --- a/mobile-ui/src/components/form/textarea.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import React from "react"; -import {FormItemProps} from "@/components/form/types"; -import {Form, TextArea} from "antd-mobile"; -import formFieldInit from "@/components/form/common"; -import "./form.scss"; - -const FormTextArea: React.FC = (props) => { - - const {formContext, rules} = formFieldInit(props); - - return ( -