Browse Source

EmisApiTable

dev
巴林闲侠 2 years ago
parent
commit
8c1403be44
  1. 15
      api/.vscode/launch.json
  2. 19
      api/app/lib/controllers/workOrder/index.js
  3. 4
      web/client/index.html
  4. 4
      web/client/src/components/index.js
  5. 198
      web/client/src/components/workflow/index.js
  6. 123
      web/client/src/components/workflow/index.less
  7. 110
      web/client/src/components/workflow/view.js
  8. 5
      web/client/src/sections/workOrder/actions/index.js
  9. 2
      web/client/src/sections/workOrder/actions/jobOrder.js
  10. 61
      web/client/src/sections/workOrder/containers/jobOrder.jsx
  11. 11
      web/client/src/sections/workOrder/style.less
  12. 7
      web/client/src/utils/webapi.js

15
api/.vscode/launch.json

@ -41,11 +41,14 @@
// "--qndmn http://rhvqdivo5.hn-bkt.clouddn.com",
// click
// "--clickHouseUrl http://10.8.30.71",
// "--clickHousePort 8123",
// click
"--clickHouseUrl http://10.8.30.161",
// "--clickHouseUrl http://10.8.30.161",
// "--clickHousePort 30123",
// "--clickHouseUrl https://clickhouse01.anxinyun.cn/play",
"--clickHousePort 30123",
// "--clickHousePort 8123",
// click
"--clickHouseUrl http://10.8.30.156",
"--clickHousePort 8123",
// * 2
// "--clickHouseUser ",
// "--clickHousePassword ",
@ -57,9 +60,9 @@
// "--clickHouseDataAlarm default",
// "--clickHouseIot iot",
//
"--clickHouseAnxincloud anxinyun1",
"--clickHousePepEmis pepca9",
"--clickHouseProjectManage peppm",
"--clickHouseAnxincloud Anxinyun101",
"--clickHousePepEmis pepca8",
"--clickHouseProjectManage peppm8",
"--clickHouseVcmp video_access_dev",
"--clickHouseDataAlarm default",
"--clickHouseIot iota",

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

@ -4,8 +4,27 @@ const moment = require('moment')
async function getEnabledWorkflowProcess (ctx) {
try {
const { models } = ctx.fs.dc;
const { clickHouse } = ctx.app.fs
const listRes = await clickHouse.pepEmis.query(`
SELECT
workflow_process.id AS id,
workflow_process.name AS name,
workflow_process.create_time AS createTime,
workflow_process.update_time AS updateTime,
workflow_group.name AS group
FROM
workflow_process
RIGHT JOIN workflow_group
ON workflow_process.group_id = workflow_group.id
AND workflow_group.name = '销售'
WHERE
workflow_process.deleted = 0
AND workflow_process.is_enable = 1
`).toPromise()
ctx.status = 200;
ctx.body = listRes
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: error`);
ctx.status = 400;

4
web/client/index.html

@ -10,8 +10,8 @@
<script charset="UTF-8" id="LA_COLLECT" src="//sdk.51.la/js-sdk-pro.min.js"></script>
<script>LA.init({ id: "Jo4eTlZVqgx3uwqm", ck: "Jo4eTlZVqgx3uwqm" })</script>
<script
src="https://lf1-cdn-tos.bytegoofy.com/obj/iconpark/icons_19077_11.559b91c217b8ddc76c0c4b1397d84d48.es5.js"></script>
<!-- <script
src="https://lf1-cdn-tos.bytegoofy.com/obj/iconpark/icons_19077_11.559b91c217b8ddc76c0c4b1397d84d48.es5.js"></script> -->
</head>
<body>

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

@ -5,6 +5,7 @@ import Setup from './setup'
import { SkeletonScreen } from './skeletonScreen'
import OutHidden from './outHidden'
import Uploads from './Uploads/index'
import WorkflowModal from './workflow';
export {
SimpleFileDownButton,
@ -12,5 +13,6 @@ export {
Setup,
SkeletonScreen,
OutHidden,
Uploads
Uploads,
WorkflowModal
};

198
web/client/src/components/workflow/index.js

@ -0,0 +1,198 @@
'use strict';
import React from 'react';
import { connect } from 'react-redux';
import { Modal, Spin, Notification, Row } from '@douyinfe/semi-ui';
import { RouteTable } from '../../utils/webapi';
import { RouteRequest } from '@peace/utils';
import { useState } from 'react';
import { useEffect } from 'react';
import { IconClose } from '@douyinfe/semi-icons';
// import { delDraft } from '../../sections/myform/actions/myform';
import './index.less'
const { confirm } = Modal;
const WorkFlowModal = (props) => {
const { successCallBack = () => { }, title, visible, clientHeight, postData, processId, user, dispatch } = 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();
const handelCancel = () => {
if (draftId) {
setCustomVisible(false);
} else {
const { onCancel } = props;
onCancel && onCancel();
setLoading(false);
}
}
const showConfirm = () => {
if (draftId) {
setCustomVisible(true);
} else {
confirm({
title: '确定关闭此表单吗?',
onOk () {
handelCancel()
},
onCancel () { },
});
}
}
const handelApprovalCenter = (activeKey) => {
RouteRequest.get(RouteTable.getWebUrl + `?sys=emisWebUrl`).then(res => {
if (res.url) {
window.open(res.url + "/approval/center?activeKey=" + activeKey);
}
});
}
const handelOk = () => {
const { onCancel } = props;
onCancel && onCancel();
setLoading(false);
setCustomVisible(false);
setDraftId(null);
}
const handelDeny = () => {
// dispatch(delDraft(draftId)).then(res => {
// if (res.success) {
// Notification.success('删除草稿成功');
// const { onCancel } = props;
// onCancel && onCancel();
// setLoading(false);
// setCustomVisible(false);
// setDraftId(null);
// } else {
// Notification.error('删除草稿失败,请联系管理员');
// }
// })
}
const cancel = () => {
setCustomVisible(false);
}
useEffect(() => {
window.receiveMessageFromIndex = function (event) {
if (event && event.data && typeof (event.data) == 'string') {
let data = JSON.parse(event.data);
const { type, isSaveDraft, setIntervalSave, draftId, msg } = data;
if (msg) {
Notification.success(msg)
}
setDraftId(draftId);
if (type == 'saveSuccess' && successCallBack) {
if (ifShowMessage) {
if (isSaveDraft) {
if (setIntervalSave) {
Notification.success('60秒,表单暂存草稿成功');
} else {
Notification.success(<span>保存草稿成功可在<a onClick={() => handelApprovalCenter("save")}>审批中心/保存待发</a></span>, 10);
}
} else {
Notification.success('提交成功,可在【审批中心/由我发起】查看详情');
successCallBack();
}
//防止提示多次
setIfShowMessage(false);
}
// successCallBack(); 阻止页面关闭
setLoading(false);
}
}
}
//监听message事件
window.addEventListener('message', receiveMessageFromIndex, false);
//获取企业统一认证管理平台web的URL
if (!webUrl) {
RouteRequest.get(RouteTable.getEmisWebUrl).then(res => {
if (res.url) {
setWebUrl(res.url);
}
});
}
}, [])
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
>
<Row>
<div className='workflow-body'>
<div className='workflow-body-header'>
<img src='/assets/images/fs-logo.png' />
<span className='workflow-body-header_title'>
<span className='workflow-body-header_title_shu'>|</span>
<span className='workflow-body-header_title_zi'>让世间万物拥有感知服务人类社会于美好</span>
</span>
<IconClose className="workflow-body-header_closeIcon" onClick={showConfirm} />
</div>
<div className='workflow-body-form'>
<Spin
spinning={loading}
>
{webUrl && processId ? <iframe
id='workflowFrame'
onLoad={() => {
let frameWin = document.getElementById('workflowFrame');
frameWin.contentWindow.postMessage(JSON.stringify(postData), '*');
setLoading(false);
}}
allowTransparency="true"
ref={iframeRef}
src={`${webUrl}/process/${processId}/apply?token=${user.token}`}
width={'100%'}
style={{ height: '100vh' }}
frameBorder="0"
></iframe> : ''}
</Spin>
</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> : ''
}
</Row>
</Modal >
</div>
)
}
function mapStateToProps (state) {
const { auth, global } = state;
return {
user: auth.user,
clientHeight: global.clientHeight,
}
}
export default connect(mapStateToProps)(WorkFlowModal);

123
web/client/src/components/workflow/index.less

@ -0,0 +1,123 @@
.process_modal_wrap {
overflow: hidden !important;
}
.workflow-body {
height : 100vh;
width : 100%;
position: relative;
&-header {
width : calc(100vw - 71px);
height : 80px;
background: linear-gradient(90deg, #1890ff 0%, #1271c9 100%);
position : fixed;
top : 0px;
left : 32px;
z-index : 1;
img {
// padding: 12px 0 0 18px;
position: relative;
// top: 18%;
left : 18px;
top : 8px;
}
&_title {
span {
margin-top : 32px;
position : relative;
top : 10px;
left : 12px;
padding-left: 20px;
}
&_shu {
font-size: 25px;
color : rgba(255, 255, 255, 0.5);
}
&_zi {
font-size : 16px;
color : #fff;
line-height: 60px;
font-weight: bold;
}
}
&_closeIcon {
position : absolute;
top : 0px;
right : 0px;
font-size: 37px;
color : #999999 !important;
}
}
&-form {
position: fixed;
top : 0;
bottom : 0;
left : 0;
right : 0;
height : 100vh;
}
}
.modalBox {
position : fixed;
width : 96.3%;
height : 100%;
background: rgba(0, 0, 0, 0.3);
.modal {
background : #fff;
position : absolute;
top : 20%;
left : 50%;
margin-left : -112px;
padding : 10px 20px;
border : 1px solid #ddd;
border-radius: 2px;
width : 370px;
.content {
margin-top : 20px;
font-size : 18px;
color : #000;
}
.btn {
display : flex;
justify-content: flex-end;
width : 100%;
font-size : 16px;
.confirm {
padding : 0 10px;
color : #fff;
border-radius: 3px;
background : #1890FF;
cursor : pointer;
}
.deny {
padding : 0 10px;
color : #1890FF;
border-radius: 3px;
border : 1px solid #1890FF;
margin : 0 10px;
cursor : pointer;
}
.cancel {
padding : 0 10px;
color : #1890FF;
border-radius: 3px;
border : 1px solid #1890FF;
cursor : pointer;
}
}
}
}

110
web/client/src/components/workflow/view.js

@ -0,0 +1,110 @@
'use strict';
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 { useState } from 'react';
import { useEffect } from 'react';
import { CloseCircleFilled } from '@ant-design/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 handelCancel = () => {
// const { onCancel } = props;
// onCancel && onCancel();
// setLoading(false);
// }
// 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
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'>
<div id="processHeader" className='workflow-body-header'>
<img src='/assets/images/fs-logo.png' />
<span className='workflow-body-header_title'>
<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} />
</div>
<div className='workflow-body-form'>
<Spin
spinning={loading}
>
{webUrl && processId ? <iframe
id='workflowViewFrame'
name='workflowViewFrame'
onLoad={() => {
let frameWin = document.getElementById('workflowViewFrame');
frameWin.contentWindow.postMessage(JSON.stringify(postData), '*');
setLoading(false);
}}
allowTransparency="true"
ref={iframeRef}
src={`${webUrl}/process/my-apply?token=${user.token}&processInstanceId=${processId}&type=oaleader`}
width={'100%'}
style={{ height: '100vh' }}
frameBorder="0"
></iframe> : ''}
</Spin>
</div>
</div>
</Row>
</Modal > */}
</div>
)
}
function mapStateToProps(state) {
const { auth, global } = state;
return {
user: auth.user,
clientHeight: global.clientHeight,
}
}
export default connect(mapStateToProps)(WorkFlowViewModal);

5
web/client/src/sections/workOrder/actions/index.js

@ -1,2 +1,7 @@
'use strict';
import * as jobOrder from './jobOrder'
export default {
...jobOrder
}

2
web/client/src/sections/workOrder/actions/jobOrder.js

@ -6,7 +6,7 @@ export function getEnabledWorkflowProcess () {
return dispatch => basicAction({
type: 'get',
dispatch: dispatch,
actionType: 'GET_MEMBERS',
actionType: 'GET_ENABLED_WORKFLOW_PROCESS',
url: `${ApiTable.getEnabledWorkflowProcess}`,
msg: { error: '获取可用表单失败' },
reducer: { name: 'workflowProcess' }

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

@ -1,24 +1,73 @@
import React, { useEffect } from 'react';
import React, { useEffect, useState } from 'react';
import { connect } from 'react-redux';
import moment from 'moment';
import { WorkflowModal } from "$components"
import { Card, Typography, Space, Button } from '@douyinfe/semi-ui';
import { IconArticle } from '@douyinfe/semi-icons';
import '../style.less'
const { Meta } = Card;
const JobOrder = (props) => {
const { dispatch, actions, user, loading, socket } = props
const { dispatch, actions, user, workflowProcess } = props
const { workOrder } = actions
const [workflowModalVisible, setWorkflowModalVisible] = useState(false)
const [launchProcessId, setLaunchProcessId] = useState(null)
useEffect(() => {
dispatch(workOrder.getEnabledWorkflowProcess())
}, [])
return (
<div className='work-order-container' style={{
display: 'grid', gridTemplateColumns: `repeat(auto-fit, minmax(340px, 1fr))`, gridAutoRows: `minmax(0, auto)`, gridGap: 12,
}}>
{
workflowProcess.map(p => {
return (
<Card
className='work-order-card'
style={{ gridColumn: `span 1`, cursor: 'pointer' }}
footerLine={true}
footerStyle={{ display: 'flex', justifyContent: 'flex-end' }}
footer={
`创建时间:${moment(p.createTime || p.updateTime).format('YYYY-MM-DD HH:mm:ss')}`
}
onClick={() => {
setLaunchProcessId(p.id)
setWorkflowModalVisible(true)
}}
>
<div>
<Meta
title={p.name}
// description=""
avatar={
<IconArticle style={{ fontSize: 42 }} />
}
/>
</div>
</Card>
)
})
}
<WorkflowModal
visible={workflowModalVisible}
title={''}
processId={launchProcessId}
onCancel={() => { setWorkflowModalVisible(false) }}
successCallBack={() => { setWorkflowModalVisible(false) }}
/>
</div>
)
}
function mapStateToProps (state) {
const { auth, global, members, webSocket } = state;
const { auth, global, workflowProcess } = state;
return {
// user: auth.user,
user: auth.user,
actions: global.actions,
workflowProcess: workflowProcess.data || []
};
}

11
web/client/src/sections/workOrder/style.less

@ -1,7 +1,6 @@
#example {
box-shadow: 3px 3px 2px black;
}
#example:hover {
color: yellowgreen;
.work-order-container {
.work-order-card:hover {
// 阴影
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
}
}

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

@ -79,6 +79,13 @@ export const ApiTable = {
//工单
getEnabledWorkflowProcess: 'workflow/process/enabled',//获取工作流可用表单
};
// 项企的接口
export const EmisApiTable = {
//通过流程名称查找指定流程
getProcessByName: '/workflow/process/name',
}
export const RouteTable = {
apiRoot: "/api/root",
fileUpload: "/_upload/new",

Loading…
Cancel
Save