wenlele 2 years ago
parent
commit
970261a505
  1. 56
      api/app/lib/controllers/workOrder/index.js
  2. 4
      api/app/lib/routes/workOrder/index.js
  3. 3
      api/config.js
  4. BIN
      web/client/assets/images/fs-logo.png
  5. 5
      web/client/src/components/index.js
  6. 87
      web/client/src/components/workflow/index.jsx
  7. 70
      web/client/src/components/workflow/view.jsx
  8. 1
      web/client/src/layout/actions/global.js
  9. 2
      web/client/src/layout/reducers/global.js
  10. 29
      web/client/src/sections/workOrder/containers/jobOrder.jsx
  11. 3
      web/client/src/utils/webapi.js
  12. 7
      web/config.js
  13. 2
      web/package.json
  14. 3
      web/routes/attachment/index.js

56
api/app/lib/controllers/workOrder/index.js

@ -21,6 +21,7 @@ async function getEnabledWorkflowProcess (ctx) {
WHERE
workflow_process.deleted = 0
AND workflow_process.is_enable = 1
order by workflow_process.update_time desc
`).toPromise()
ctx.status = 200;
@ -34,6 +35,59 @@ async function getEnabledWorkflowProcess (ctx) {
}
}
async function basicDataAllProject (ctx) {
try {
const { models } = ctx.fs.dc;
const { clickHouse } = ctx.app.fs
const proRes = await models.ProjectCorrelation.findAll({
where: {
del: false,
},
order: [['updateTime', 'desc']],
})
let pepProjectIds = new Set()
for (let p of proRes) {
if (p.pepProjectId) {
pepProjectIds.add(p.pepProjectId)
}
}
const pepProjectRes = pepProjectIds.size ?
await clickHouse.projectManage.query(
`
SELECT
t_pim_project.id AS id,
t_pim_project.project_name AS project_name,
t_pim_project.isdelete AS isdelete
FROM t_pim_project
WHERE id IN (${[...pepProjectIds].join(',')}, -1)
`
).toPromise() || [] :
[]
const list = proRes.map(p => {
const corPro = pepProjectRes.find(pp => pp.id == p.pepProjectId) || {}
return {
value: p.id,
label: corPro.project_name || p.name,
disabled: corPro.isdelete == 1
}
})
ctx.status = 200;
ctx.body = list
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
ctx.body = {
message: typeof error == 'string' ? error : undefined
}
}
}
module.exports = {
getEnabledWorkflowProcess
getEnabledWorkflowProcess,
basicDataAllProject
};

4
api/app/lib/routes/workOrder/index.js

@ -5,4 +5,8 @@ const workOrder = require('../../controllers/workOrder');
module.exports = function (app, router, opts) {
app.fs.api.logAttr['GET/workflow/process/enabled'] = { content: '获取工作流可用表单', visible: true };
router.get('/workflow/process/enabled', workOrder.getEnabledWorkflowProcess);
// 单一数据源
app.fs.api.logAttr['GET/basic-data/workflow/single/allProject'] = { content: '查询所有项目', visible: false };
router.get('/basic-data/workflow/single/allProject', workOrder.basicDataAllProject);
};

3
api/config.js

@ -160,7 +160,8 @@ const product = {
{ p: '/alarm/service/api', o: 'POST' }, //后端服务告警入库
{ p: '/data/continuity/api', o: 'POST' }, //数据连续性监控入库
{ p: '/page/performance/api', o: 'POST' }, //页面加载性能入库
{ p: '/alarm/video/added_log', o: 'POST' }
{ p: '/alarm/video/added_log', o: 'POST' },
{ p: '/basic-data/workflow/single/allProject', o: 'GET' },
], // 不做认证的路由,也可以使用 exclude: ["*"] 跳过所有路由
apMergeDeVeAnxinProjectId: AP_MERGE_DEVE_ANXINPROJECT_ID,
anxinCloud: {

BIN
web/client/assets/images/fs-logo.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

5
web/client/src/components/index.js

@ -6,7 +6,7 @@ import { SkeletonScreen } from './skeletonScreen'
import OutHidden from './outHidden'
import Uploads from './Uploads/index'
import WorkflowModal from './workflow';
import WorkFlowViewModal from './workflow/view'
export {
SimpleFileDownButton,
ReminderBox,
@ -14,5 +14,6 @@ export {
SkeletonScreen,
OutHidden,
Uploads,
WorkflowModal
WorkflowModal,
WorkFlowViewModal
};

87
web/client/src/components/workflow/index.jsx

@ -2,9 +2,9 @@
import React from 'react';
import { connect } from 'react-redux';
import { Modal, Spin, Notification, Row } from '@douyinfe/semi-ui';
import { Modal, Spin, Notification, Row, Space, Button } from '@douyinfe/semi-ui';
import { RouteTable } from '../../utils/webapi';
import { RouteRequest } from '@peace/utils';
import { EmisRequest, EmisApiTable } from '$utils';
import { useState } from 'react';
import { useEffect } from 'react';
import { IconClose } from '@douyinfe/semi-icons';
@ -14,10 +14,9 @@ import './index.less'
const { confirm } = Modal;
const WorkFlowModal = (props) => {
const { successCallBack = () => { }, title, visible, clientHeight, postData, processId, user, dispatch, webEmis } = props;
const { successCallBack = () => { }, title, visible, clientHeight, postData, processId, user, dispatch, webEmis, webOa, onCancel } = props;
const [loading, setLoading] = useState(true);
const [ifShowMessage, setIfShowMessage] = useState(true);
const [webUrl, setWebUrl] = useState(null);
const [customVisible, setCustomVisible] = useState(false);
const [draftId, setDraftId] = useState();
const iframeRef = React.createRef();
@ -26,7 +25,6 @@ const WorkFlowModal = (props) => {
if (draftId) {
setCustomVisible(false);
} else {
const { onCancel } = props;
onCancel && onCancel();
setLoading(false);
}
@ -47,11 +45,9 @@ const WorkFlowModal = (props) => {
}
const handelApprovalCenter = (activeKey) => {
RouteRequest.get(RouteTable.getWebUrl + `?sys=emisWebUrl`).then(res => {
if (res.url) {
window.open(res.url + "/approval/center?activeKey=" + activeKey);
if (webOa) {
window.open(webOa + "/approval/center?activeKey=" + activeKey);
}
});
}
const handelOk = () => {
@ -63,18 +59,13 @@ const WorkFlowModal = (props) => {
}
const handelDeny = () => {
// dispatch(delDraft(draftId)).then(res => {
// if (res.success) {
// Notification.success({title:'稿'});
// const { onCancel } = props;
// onCancel && onCancel();
// setLoading(false);
// setCustomVisible(false);
// setDraftId(null);
// } else {
// Notification.error({title:'稿'});
// }
// })
EmisRequest.delete(EmisApiTable.delDraftUrl.replace('{draftId}', draftId)).then(res => {
Notification.success({ title: '删除草稿成功' });
onCancel && onCancel();
setLoading(false);
setCustomVisible(false);
setDraftId(null);
})
}
const cancel = () => {
@ -87,6 +78,7 @@ const WorkFlowModal = (props) => {
let data = JSON.parse(event.data);
const { type, isSaveDraft, setIntervalSave, draftId, msg } = data;
if (msg) {
console.log(msg);
Notification.success({ title: msg })
}
setDraftId(draftId);
@ -112,31 +104,21 @@ const WorkFlowModal = (props) => {
}
//message
window.addEventListener('message', receiveMessageFromIndex, false);
//webURL
if (!webUrl) {
RouteRequest.get(RouteTable.getEmisWebUrl).then(res => {
if (res.url) {
setWebUrl(res.url);
}
});
}
}, [])
console.log(webEmis, processId);
return (
<div>
<Modal
fullScreen
visible={visible}
width={'100%'}
// style={{ top: 0, left: 0, padding: 0, maxWidth: '100%' }}
// bodyStyle={{ paddingLeft: 32, paddingRight: 32, margin: 0, backgroundColor: '#FAFAFA', height: '100%', overflowY: 'auto' }}
title={''}
closable={false}
hasCancel={false}
footer={null}
wrapClassName='process_modal_wrap'
onCancel={showConfirm}
destroyOnClose
// onCancel={showConfirm}
>
<Row>
<div className='workflow-body'>
@ -152,7 +134,7 @@ const WorkFlowModal = (props) => {
<Spin
spinning={loading}
>
{webUrl && processId ? <iframe
{webEmis && processId ? <iframe
id='workflowFrame'
onLoad={() => {
let frameWin = document.getElementById('workflowFrame');
@ -161,7 +143,7 @@ const WorkFlowModal = (props) => {
}}
allowTransparency="true"
ref={iframeRef}
src={`${webUrl}/process/${processId}/apply?token=${user.token}`}
src={`${webEmis}/process/${processId}/apply?token=${user.token}`}
width={'100%'}
style={{ height: '100vh' }}
frameBorder="0"
@ -170,16 +152,27 @@ const WorkFlowModal = (props) => {
</div>
</div>
{
customVisible ? <div className='modalBox'>
<div className='modal'>
<p className='content'>此表单已保存为草稿是否需要保留</p>
<ul className='btn'>
<li className='confirm' onClick={() => handelOk()}></li>
<li className='deny' onClick={() => handelDeny()}></li>
<li className='cancel' onClick={() => cancel()}>取消</li>
</ul>
</div>
</div> : ''
customVisible ?
<Modal
title="此表单已保存为草稿,是否需要保留"
visible={customVisible}
footer={
<Space>
<Button type="primary" onClick={() => handelOk()}>
</Button>
<Button type="primary" onClick={() => handelDeny()}>
</Button>
<Button type="primary" onClick={() => cancel()}>
取消
</Button>
</Space>
}
>
</Modal>
: ''
}
</Row>
</Modal >
@ -189,11 +182,11 @@ const WorkFlowModal = (props) => {
function mapStateToProps (state) {
const { auth, global } = state;
console.log(global);
return {
user: auth.user,
clientHeight: global.clientHeight,
webEmis: global.webEmis
webEmis: global.webEmis,
webOa: global.webOa
}
}

70
web/client/src/components/workflow/view.jsx

@ -2,62 +2,46 @@
import React from 'react';
import { connect } from 'react-redux';
// import { Modal, Spin, message, Row } from 'antd';
import { RouteTable } from '../../utils/webapi';
import { RouteRequest } from '@peace/utils';
import { Modal, Spin, Row } from '@douyinfe/semi-ui';
import { useState } from 'react';
import { useEffect } from 'react';
import { CloseCircleFilled } from '@ant-design/icons';
import { IconClose } from '@douyinfe/semi-icons';
import './index.less'
const { confirm } = Modal;
const WorkFlowViewModal = (props) => {
// const { successCallBack, title, visible, clientHeight, postData, processId, user } = props;
// const [loading, setLoading] = useState(true);
// const [ifShowMessage, setIfShowMessage] = useState(true);
// const [webUrl, setWebUrl] = useState(null);
// const iframeRef = React.createRef();
const { visible, clientHeight, postData, processId, user, webEmis, webOa } = props;
const [loading, setLoading] = useState(true);
const [ifShowMessage, setIfShowMessage] = useState(true);
const iframeRef = React.createRef();
// const handelCancel = () => {
// const { onCancel } = props;
// onCancel && onCancel();
// setLoading(false);
// }
const handelCancel = () => {
const { onCancel } = props;
onCancel && onCancel();
setLoading(false);
}
// const showConfirm = () => {
// confirm({
// title: '?',
// onOk() {
// handelCancel()
// },
// onCancel() { },
// });
// }
const showConfirm = () => {
confirm({
title: '确定关闭此表单吗?',
onOk () {
handelCancel()
},
onCancel () { },
});
}
// useEffect(() => {
// if (!webUrl) {
// RouteRequest.get(RouteTable.getEmisWebUrl).then(res => {
// if (res.url) {
// setWebUrl(res.url);
// }
// });
// }
// }, [])
const bodyStyle = { paddingLeft: 32, paddingRight: 32, margin: 0, backgroundColor: '#FAFAFA', height: '100%', overflowY: 'auto' }
return (
<div>
{/* <Modal
<Modal
fullScreen
visible={visible}
width={'100%'}
style={{ top: 0, left: 0, padding: 0, maxWidth: '100%' }}
bodyStyle={bodyStyle}
title={''}
closable={false}
footer={null}
wrapClassName='process_modal_wrap'
onCancel={showConfirm}
destroyOnClose
>
<Row>
<div className='workflow-body'>
@ -67,13 +51,13 @@ const WorkFlowViewModal = (props) => {
<span className='workflow-body-header_title_shu'>|</span>
<span className='workflow-body-header_title_zi'>让世间万物拥有感知服务人类社会于美好</span>
</span>
<CloseCircleFilled className="workflow-body-header_closeIcon" onClick={showConfirm} />
<IconClose className="workflow-body-header_closeIcon" onClick={showConfirm} />
</div>
<div className='workflow-body-form'>
<Spin
spinning={loading}
>
{webUrl && processId ? <iframe
{webEmis && processId ? <iframe
id='workflowViewFrame'
name='workflowViewFrame'
onLoad={() => {
@ -83,7 +67,7 @@ const WorkFlowViewModal = (props) => {
}}
allowTransparency="true"
ref={iframeRef}
src={`${webUrl}/process/my-apply?token=${user.token}&processInstanceId=${processId}&type=oaleader`}
src={`${webEmis}/process/my-apply?token=${user.token}&processInstanceId=${processId}&type=oaleader`}
width={'100%'}
style={{ height: '100vh' }}
frameBorder="0"
@ -93,7 +77,7 @@ const WorkFlowViewModal = (props) => {
</div>
</Row>
</Modal > */}
</Modal >
</div>
)
@ -104,6 +88,8 @@ function mapStateToProps(state) {
return {
user: auth.user,
clientHeight: global.clientHeight,
webEmis: global.webEmis,
webOa: global.webOa
}
}

1
web/client/src/layout/actions/global.js

@ -49,6 +49,7 @@ export function initApiRoot () {
dcWeb: res.dcWeb,
qiniu: res.qiniu,
webEmis: res.webEmis,
webOa: res.webOa,
}
})
});

2
web/client/src/layout/reducers/global.js

@ -50,6 +50,8 @@ function global (state = {
pomsNotebook: payload.pomsNotebook,
dcWeb: payload.dcWeb,
qiniu: payload.qiniu,
webEmis: payload.webEmis,
webOa: payload.webOa,
}).toJS();
case PEPPROJECTID:
return Immutable.fromJS(state).merge({

29
web/client/src/sections/workOrder/containers/jobOrder.jsx

@ -1,32 +1,35 @@
import React, { useEffect, useState } from 'react';
import { connect } from 'react-redux';
import moment from 'moment';
import { WorkflowModal } from "$components"
import { WorkflowModal, WorkFlowViewModal } from "$components"
import { EmisRequest, EmisApiTable } from "$utils"
import { Card, Notification, Space, Button } from '@douyinfe/semi-ui';
import { Card, Notification, Space, Button, Spin } from '@douyinfe/semi-ui';
import { IconArticle } from '@douyinfe/semi-icons';
import '../style.less'
const { Meta } = Card;
const JobOrder = (props) => {
const { dispatch, actions, user, workflowProcess } = props
const { dispatch, actions, user, workflowProcess, clientHeight } = props
const { workOrder } = actions
const [workflowModalVisible, setWorkflowModalVisible] = useState(false)
const [launchProcessId, setLaunchProcessId] = useState(null)
const [formStateRequestingIndex, setFormStateRequestingIndex] = useState(false)
useEffect(() => {
dispatch(workOrder.getEnabledWorkflowProcess())
}, [])
return (
<div>
<div className='work-order-container' style={{
display: 'grid', gridTemplateColumns: `repeat(auto-fit, minmax(340px, 1fr))`, gridAutoRows: `minmax(0, auto)`, gridGap: 12,
}}>
{
workflowProcess.map(p => {
workflowProcess.map((p, index) => {
return (
<Card
key={p.id}
className='work-order-card'
style={{ gridColumn: `span 1`, cursor: 'pointer' }}
footerLine={true}
@ -35,11 +38,13 @@ const JobOrder = (props) => {
`创建时间:${moment(p.createTime || p.updateTime).format('YYYY-MM-DD HH:mm:ss')}`
}
onClick={() => {
if (formStateRequestingIndex) return;
setFormStateRequestingIndex(index + 1)
EmisRequest.get(EmisApiTable.getProcessByName, {
name: decodeURIComponent(p.name),
resource: p.id
}).then(res => {
console.log(res);
setFormStateRequestingIndex(false)
if (res) {
// if (res.deleted) {
// Notification.error({ title: '退' })
@ -60,8 +65,9 @@ const JobOrder = (props) => {
>
<div>
<Meta
title={p.name}
// description=""
title={
<span>{p.name}<Spin style={{ position: 'relative', top: 6, left: 6, }} spinning={formStateRequestingIndex == index + 1} /></span>
}
avatar={
<IconArticle style={{ fontSize: 42 }} />
}
@ -71,6 +77,7 @@ const JobOrder = (props) => {
)
})
}
</div>
<WorkflowModal
visible={workflowModalVisible}
title={''}
@ -78,16 +85,22 @@ const JobOrder = (props) => {
onCancel={() => { setWorkflowModalVisible(false) }}
successCallBack={() => { setWorkflowModalVisible(false) }}
/>
{/* <WorkFlowViewModal
visible={workflowModalVisible}
processId={launchProcessId}
/> */}
</div>
)
}
function mapStateToProps (state) {
const { auth, global, workflowProcess } = state;
console.log(global);
return {
user: auth.user,
actions: global.actions,
workflowProcess: workflowProcess.data || []
workflowProcess: workflowProcess.data || [],
clientHeight: global.clientHeight
};
}

3
web/client/src/utils/webapi.js

@ -86,8 +86,11 @@ export const ApiTable = {
export const EmisApiTable = {
//通过流程名称查找指定流程
getProcessByName: 'workflow/process/name',
processInstancemMyApplyList: 'process-instance/my-apply-list',
processInstancemMyAuditList: 'process-instance/my-audit-list?',
//删除草稿
delDraftUrl: 'process/drafts/{draftId}',
}
export const RouteTable = {

7
web/config.js

@ -15,7 +15,8 @@ args.option(['u', 'api-url'], 'webapi的URL');
args.option('apiPomsUrl', 'webapi的URL 外网可访问');
args.option('apiAnxinyunUrl', '安心云 api');
args.option('apiEmisUrl', '企业管理 api');
args.option('webEmisUrl', '企业管理 web');
args.option('webEmisUrl', '统一认证 web - 统一认证 配置表单的');
args.option('webOaUrl', 'OA web - 项企填表单的');
args.option('iotVcmpWeb', 'IOT 视频服务');
args.option('pomsMonitor', '运维监控 web');
args.option('pomsKubesphere', 'kubesphere web');
@ -39,6 +40,7 @@ const API_URL = process.env.API_URL || flags.apiUrl;
const API_POMS_URL = process.env.API_POMS_URL || flags.apiPomsUrl;
const API_EMIS_URL = process.env.API_EMIS_URL || flags.apiEmisUrl;
const WEB_EMIS_URL = process.env.WEB_EMIS_URL || flags.webEmisUrl;
const WEB_OA_URL = process.env.WEB_OA_URL || flags.webOaUrl;
const API_ANXINYUN_URL = process.env.API_ANXINYUN_URL || flags.apiAnxinyunUrl;
const IOT_VIDEO_WEB = process.env.IOT_VIDEO_WEB || flags.iotVcmpWeb;
const POMS_MONITOR = process.env.POMS_MONITOR || flags.pomsMonitor;
@ -59,7 +61,7 @@ const ANXINCLOUD_QINIU_DOMAIN_QNDMN_RESOURCE = process.env.ANXINCLOUD_QINIU_DOMA
if (
!API_URL
|| !API_EMIS_URL || !WEB_EMIS_URL
|| !API_EMIS_URL || !WEB_EMIS_URL || !WEB_OA_URL
|| !API_ANXINYUN_URL
|| !POMS_MONITOR || !DC_WEB
|| !ANXINCLOUD_QINIU_AK || !ANXINCLOUD_QINIU_SK || !ANXINCLOUD_QINIU_BUCKET_RESOURCE || !ANXINCLOUD_QINIU_DOMAIN_QNDMN_RESOURCE || !IOT_VIDEO_WEB) {
@ -106,6 +108,7 @@ const product = {
opts: {
apiUrl: API_POMS_URL,
webEmis: WEB_EMIS_URL,
webOa: WEB_OA_URL,
iotVcmpWeb: IOT_VIDEO_WEB,
pomsMonitor: POMS_MONITOR,
pomsKubesphere: POMS_KUBESPHERE,

2
web/package.json

@ -7,7 +7,7 @@
"test": "mocha",
"start-vite": "cross-env NODE_ENV=developmentVite npm run start-params",
"start": "cross-env NODE_ENV=development npm run start-params",
"start-params": "node server -p 5600 -u http://localhost:4600 --apiPomsUrl http://localhost:4600 --apiAnxinyunUrl http://10.8.30.112:4100 --apiEmisUrl http://10.8.30.161:1111 --webEmisUrl http://10.8.30.161:1111 --qnak 5XrM4wEB9YU6RQwT64sPzzE6cYFKZgssdP5Kj3uu --qnsk w6j2ixR_i-aelc6I7S3HotKIX-ukMzcKmDfH6-M5 --qnbkt anxinyun-test --qndmn http://test.resources.anxinyun.cn --iotVcmpWeb https://mediaconsole.ngaiot.com --pomsMonitor http://monitor.anxinyun.cn/goto/PaEDLE84z?orgId=1 --pomsKubesphere https://k8sadmin.anxinyun.cn/ --pomsAmbari https://ambari.anxinyun.cn/ --pomsKowl https://kafka.anxinyun.cn/ --pomsPghero https://pghero.anxinyun.cn/ --pomsEs https://esc.anxinyun.cn/ --pomsNotebook https://inotebook.anxinyun.cn/ --dcWeb https://fsiot-oamss.anxinyun.cn",
"start-params": "node server -p 5600 -u http://localhost:4600 --apiPomsUrl http://localhost:4600 --apiAnxinyunUrl http://10.8.30.112:4100 --apiEmisUrl http://10.8.30.161:1111 --webEmisUrl http://10.8.30.161:1112 --webOaUrl http://10.8.30.161:8668 --qnak 5XrM4wEB9YU6RQwT64sPzzE6cYFKZgssdP5Kj3uu --qnsk w6j2ixR_i-aelc6I7S3HotKIX-ukMzcKmDfH6-M5 --qnbkt anxinyun-test --qndmn http://test.resources.anxinyun.cn --iotVcmpWeb https://mediaconsole.ngaiot.com --pomsMonitor http://monitor.anxinyun.cn/goto/PaEDLE84z?orgId=1 --pomsKubesphere https://k8sadmin.anxinyun.cn/ --pomsAmbari https://ambari.anxinyun.cn/ --pomsKowl https://kafka.anxinyun.cn/ --pomsPghero https://pghero.anxinyun.cn/ --pomsEs https://esc.anxinyun.cn/ --pomsNotebook https://inotebook.anxinyun.cn/ --dcWeb https://fsiot-oamss.anxinyun.cn",
"deploy": "export NODE_ENV=production&& npm run build && node server",
"build-dev": "cross-env NODE_ENV=development&&webpack --config webpack.config.js",
"build": "cross-env NODE_ENV=production&&webpack --config webpack.config.prod.js"

3
web/routes/attachment/index.js

@ -19,7 +19,7 @@ module.exports = {
entry: function (app, router, opts) {
const getApiRoot = async function (ctx) {
const { apiUrl, iotVcmpWeb, pomsMonitor, pomsKubesphere, pomsAmbari, pomsKowl, pomsPghero, pomsEs, pomsNotebook, dcWeb, qiniu, webEmis, } = opts;
const { apiUrl, iotVcmpWeb, pomsMonitor, pomsKubesphere, pomsAmbari, pomsKowl, pomsPghero, pomsEs, pomsNotebook, dcWeb, qiniu, webEmis, webOa } = opts;
ctx.status = 200;
ctx.body = {
@ -35,6 +35,7 @@ module.exports = {
dcWeb: dcWeb,
qiniu: qiniu,
webEmis,
webOa,
};
};

Loading…
Cancel
Save