Commit 36345e44 authored by wuting's avatar wuting

feat: 合同管理流程规则配置

parent 75ed863e
......@@ -32,6 +32,35 @@ const router = {
name: '请购单流程规则配置',
component: '@/pages/systemManage/buyingRequisitionProcess',
},
// 合同管理流程规则配置
{
path: '/system/ruleSettingManager/contractManagementProcess',
name: '合同管理流程规则配置',
component: '@/pages/systemManage/contractManagementProcess',
},
{
path: '/system/ruleSettingManager/contractManagementProcess/add',
name: '新增合同管理流程规则',
hideInMenu: true,
component: '@/pages/systemManage/contractManagementProcess/add',
},
{
path: '/system/ruleSettingManager/contractManagementProcess/edit',
name: '编辑合同管理流程规则',
hideInMenu: true,
component: '@/pages/systemManage/contractManagementProcess/edit',
},
{
path: '/system/ruleSettingManager/contractManagementProcess/view',
name: '合同管理流程规则详情',
hideInMenu: true,
component: '@/pages/systemManage/contractManagementProcess/view',
},
{
path: '/system/ruleSettingManager/buyingRequisitionProcess/add',
name: '新增请购单流程规则',
......
/**
* 系统管理 - 平台规则 - 新增合同管理流程规则
* @author: Crayon
*/
import React from 'react'
import AddEditContent from './components/AddEditContent'
const Add: React.FC = () => {
return (
<AddEditContent
type='add'
title='新增合同管理流程规则'
/>
)
}
export default Add
import { ColumnType } from 'antd/lib/table/interface';
/** 流程规则ID */
export const id: ColumnType<any> = {
title: 'ID',
key: 'processId',
dataIndex: 'processId',
}
/** 流程规则名称 */
export const name: ColumnType<any> = {
title: '流程规则名称',
key: 'name',
dataIndex: 'name',
}
/** 流程名称 */
export const processName: ColumnType<any> = {
title: '流程名称',
key: 'processName',
dataIndex: 'processName',
}
/** 操作时间 */
export const createTime: ColumnType<any> = {
title: '操作时间',
key: 'createTime',
dataIndex: 'createTime',
}
/** 状态 */
export const status: ColumnType<any> = {
title: '状态',
key: 'status',
dataIndex: 'status',
}
/** 操作 */
export const operation: ColumnType<any> = {
title: '操作',
key: 'operation',
dataIndex: 'operation',
width: 150
}
import React, { useState, useRef, useEffect } from 'react';
import PeripheralLayout from '@/components/DetailLayout';
import FormProgress, { HandleType } from '@/components/FormProgress'
import { Button, Form } from 'antd';
import { SaveOutlined } from '@ant-design/icons';
import ProcessEngLayout from '../ProcessEng';
import ProcessSelectLayout from '../ProcessSelect';
import ApplyMemberLayout from '../ApplyMember';
import usePrompt from '@/hooks/usePrompt';
import { VISIT_TYPE } from '@/constants';
import { history } from 'umi';
import { getPurchaseRequisitionWebProcessBaseList, getPurchaseRequisitionWebProcessGet, postPurchaseRequisitionWebProcessSave, postPurchaseRequisitionWebProcessUpdate } from '@/services/PurchaseV2Api'
import { getContractPlatformProcessBaseList, getContractPlatformProcessGet, postContractPlatformProcessSave, postContractPlatformProcessUpdate } from '@/services/ManageV2Api';
type PropsType = {
type: 'add' | 'edit' | 'view'
id?: string;
btnCode?: string;
title?: string | React.ReactNode
}
export const layout: any = {
colon: false,
labelCol: { style: { width: "144px" } },
labelAlign: "left"
};
const AddInvoiceProcessEng: React.FC<PropsType> = ({ id: processId, title, type }) => {
console.log('type:',type)
const [form] = Form.useForm();
const { renderPrompt, handleLeave } = usePrompt()
const [loading, setLoading] = useState<boolean>(false)
const [processBaseList, setProcessBaseList] = useState<any[]>([])
const progressRef = useRef<HandleType>()
const handleSubmit = () => {
form.validateFields().then(values => {
setLoading(true)
const params = {
processId,
...values,
allMembers: values.allMembers === 1 ? true : false,
members: values.allMembers === 2 ? values.members?.map(_item => {
return {
memberId: _item.memberId,
roleId: _item.roleId,
}
})
: undefined
}
console.log('param-------',params)
const fetchApi = processId ? postContractPlatformProcessUpdate : postContractPlatformProcessSave
fetchApi({ ...params }).then(res => {
if (res.code !== 1000) {
setLoading(false)
return
}
handleLeave(false)
history.goBack()
})
})
}
const getProcessBaseList = async () => {
const { code, data } = await getContractPlatformProcessBaseList();
if (code === 1000) {
setProcessBaseList(data)
}
}
const getDetail = async () => {
if (processId) {
const { code, data } = await getContractPlatformProcessGet({ processId });
if (code === 1000) {
form.setFieldsValue({
name: data.name,
baseProcessId: data.baseProcessId,
allMembers: data.allMembers ? 1 : 2,
})
}
}
}
useEffect(() => {
const getData = async () => {
await getProcessBaseList()
getDetail()
}
getData()
}, [])
return (
<>
<PeripheralLayout
hideBreak
detail={<FormProgress title={title} ref={progressRef} disabled={type === VISIT_TYPE.VIEW} />}
tabLink={[
{ title: '流程规则', id: 'processEng' },
{ title: '流程选择', id: 'processSelect' },
{ title: '适用会员', id: 'applyMember' },
]}
effect={
type !== VISIT_TYPE.VIEW && (
<Button
icon={<SaveOutlined />}
type="primary"
onClick={handleSubmit}
loading={loading}
>
保存
</Button>
)
}
components={
<Form
form={form}
{...layout}
onValuesChange={() => { handleLeave() }}
onFieldsChange={() => { progressRef?.current?.render(form) }}
>
<ProcessEngLayout type={type} />
<ProcessSelectLayout type={type} optionsData={processBaseList} />
<ApplyMemberLayout form={form} type={type} processId={processId} />
</Form>
}
/>
{renderPrompt()}
</>
)
}
export default AddInvoiceProcessEng
.invoice-type {
:global {
.ant-radio-wrapper {
padding: 8px 16px;
border-radius: 4px;
border: 1px solid #F5F6F7;
background-color: #F5F6F7;
margin-right: 16px;
}
.ant-radio-wrapper-checked {
border: 1px solid #00A98F;
background-color: rgba(0, 169, 143, 0.04);
span {
color: #00A98F;
}
}
.ant-space {
width: 100%;
}
}
}
import React, { useCallback, useEffect, useState } from 'react';
import { Button, Form, Popconfirm, Radio, Space, Table } from 'antd';
import { FormInstance } from 'antd/es/form/Form';
import { ColumnType } from 'antd/lib/table/interface';
import { PlusOutlined } from '@ant-design/icons';
import CardLayout from '../card';
import styles from './index.less';
import { operation } from '../../columns';
import { FORM_FILTER_PATH } from '@/formSchema/const';
import { useStateFilterSearchLinkageEffect } from '@/formSchema/effects/useFilterSearch';
import TableModal from '@/components/TableModal';
import { getPurchaseRequisitionWebProcessMemberPage } from '@/services/PurchaseV2Api';
import { getMemberManageAllConsumerPage } from '@/services/MemberV2Api';
import { VISIT_TYPE } from '@/constants';
import { getContractPlatformProcessMemberPage } from '@/services/ManageV2Api';
interface InvoiceTypeProps {
form?: FormInstance
type: 'add' | 'edit' | 'view'
processId?: string
}
const InvoiceTypeLayout: React.FC<InvoiceTypeProps> = (props: any) => {
const { form, type, processId } = props;
const [visible, setVisible] = useState<boolean>(false);
const [member, setMember] = useState<any>([])
const mock = [
{ value: 1, label: '所有会员(默认)' },
{ value: 2, label: '指定会员' },
]
const columns: ColumnType<any>[] = [
{
title: 'ID',
dataIndex: 'memberId',
key: 'memberId',
},
{
title: '会员名称',
dataIndex: 'name',
key: 'name',
},
{
title: '会员类型',
dataIndex: 'memberTypeName',
key: 'memberTypeName',
},
{
title: '会员角色',
dataIndex: 'roleName',
key: 'roleName',
},
{
title: '会员等级',
dataIndex: 'levelTag',
key: 'levelTag',
},
]
const handleFetchData = useCallback((params: any) => {
return new Promise(resolve => {
getMemberManageAllConsumerPage({ ...params }).then(res => {
if (res.code !== 1000) {
return
}
resolve(res.data)
}).catch(error => {
console.warn(error)
})
})
}, [])
const confirm = (records) => {
const newMember = member.filter(_item => _item.memberId !== records.memberId);
console.log(newMember)
setMember(newMember)
form.setFieldsValue({ 'members': newMember })
}
const handleConfirm = (selectRow: number[] | string[], selectedRows: { [key: string]: any }[]) => {
console.log('selectedRows', selectedRows)
setMember(selectedRows)
form.setFieldsValue({
'members': selectedRows
})
setVisible(false)
}
const columnFunc = useCallback(() => {
const params = [...columns];
!(type === VISIT_TYPE.VIEW) && params.push({
...operation,
render: (_text, record) => <>{(
<Popconfirm
title="确认要删除吗?"
onConfirm={() => confirm(record)}
okText="是"
cancelText="否"
>
<Button type="link">删除</Button>
</Popconfirm>
)}</>
})
return params
}, [member])
useEffect(() => {
if (processId) {
getContractPlatformProcessMemberPage({ processId } as any).then(({ code, data }) => {
if (code === 1000) {
setMember(data);
form.setFieldsValue({ 'members': data })
}
})
}
}, [processId])
return (
<CardLayout
id="applyMember"
title='适用会员'
weight
bodyStyle={{ paddingBottom: '1px' }}
classNames={styles['invoice-type']}
>
<Form.Item
name='allMembers'
initialValue={1}
rules={[
{ required: true, message: '请选择适用会员' },
]}
>
<Radio.Group disabled={type === VISIT_TYPE.VIEW}>
{mock.map(_item => (
<Radio key={_item.value} value={_item.value}>{_item.label}</Radio>
))}
</Radio.Group>
</Form.Item>
<Form.Item
noStyle
shouldUpdate={(prevValues, currentValues) => (prevValues.allMembers !== currentValues.allMembers)}
>
{({ getFieldValue }) =>
getFieldValue('allMembers') === 2 ? (
<Space direction='vertical'>
<Button block type='dashed' icon={<PlusOutlined />} onClick={() => setVisible(true)}>选择</Button>
<Form.Item name="members" rules={[{ required: true, message: '请选择适用会员' }]}>
<Table
rowKey='memberId'
columns={columnFunc()}
dataSource={member}
/>
</Form.Item>
</Space>
) : null
}
</Form.Item>
<TableModal
modalType="Drawer"
visible={visible}
title='选择适用会员'
mode='checkbox'
tableProps={{
rowKey: 'memberId',
}}
customKey='memberId'
fetchData={handleFetchData}
onClose={() => setVisible(false)}
onOk={handleConfirm}
columns={columns}
effects={($, actions) => {
actions.reset()
useStateFilterSearchLinkageEffect($, actions, "name", FORM_FILTER_PATH)
}}
schema={{
type: "object",
properties: {
megalayout: {
type: "object",
"x-component": "mega-layout",
properties: {
name: {
type: "string",
"x-component": "Search",
"x-mega-props": {},
"x-component-props": {
placeholder: '会员名称',
advanced: false,
align: "flex-left",
}
}
}
},
[FORM_FILTER_PATH]: {
type: "object",
"x-component": "flex-layout",
"x-component-props": {
rowStyle: {
justifyContent: "flex-start",
flexWrap: "nowrap"
},
colStyle: {//改变间隔
marginRight: 20
}
},
}
}
}}
value={member}
/>
</CardLayout>
)
}
export default InvoiceTypeLayout;
import React from 'react';
import { Form, Row, Col, Input } from 'antd';
import { FormInstance } from 'antd/es/form/Form';
import CardLayout from '../card';
import { validatorByte } from '@/utils/regExp';
import { VISIT_TYPE } from '@/constants';
interface ProcessEngProps {
/** FormInstance */
form?: FormInstance
type: 'add' | 'edit' | 'view'
}
const ProcessEngLayout: React.FC<ProcessEngProps> = (props: any) => {
const { form, type } = props;
return (
<CardLayout
id="processEng"
title='流程规则'
weight
bodyStyle={{ paddingBottom: '0px' }}
>
<Row gutter={[24, 24]}>
<Col span={12}>
<Form.Item
label='流程规则名称'
name='name'
rules={[
{ required: true, message: '请输入流程规则名称' },
{ validator: (rule, value, callback) => validatorByte(rule, value, callback, 48) }
]}
>
<Input disabled={type === VISIT_TYPE.VIEW} placeholder='最长48个字符,24个汉字' />
</Form.Item>
</Col>
</Row>
</CardLayout>
)
}
export default ProcessEngLayout;
.select-box {
:global {
.ant-radio-group {
width: 100%;
.ant-radio-wrapper {
width: 100%;
padding: 16px 0px;
display: flex;
align-items: center;
border-bottom: 1px solid #F5F6F7;
.ant-radio {
width: 16px;
height: 16px;
}
span:nth-child(2) {
flex: 1;
display: inline-block;
}
}
.ant-radio-wrapper:last-child {
border-bottom: 0px;
}
}
}
.box {
display: flex;
align-items: center;
justify-content: space-between;
.box-clerk {
padding-left: 8px;
.box-clerk-name {
font-size: 14px;
color: #252D37;
font-weight: 400;
line-height: 20px;
padding: 4px 0px
}
.box-clerk-value {
font-weight: 400;
font-size: 12px;
color: #91959B;
line-height: 16px;
padding: 4px 0px
}
}
.box-tag {
padding: 4px;
border-radius: 2px;
color: #4787F0;
background-color: #ECF2FE;
}
}
}
import React from 'react';
import { Form, Radio, Space } from 'antd';
import { FormInstance } from 'antd/es/form/Form';
import CardLayout from '../card';
import styles from './index.less';
import { VISIT_TYPE } from '@/constants';
interface ProcessSelectProps {
/** FormInstance */
form?: FormInstance
type: 'add' | 'edit' | 'view'
optionsData: any[]
}
const ProcessSelectLayout: React.FC<ProcessSelectProps> = (props: any) => {
const { form, type, optionsData } = props;
return (
<CardLayout
id="processSelect"
title='流程选择'
weight
bodyStyle={{ paddingBottom: '1px' }}
classNames={styles['select-box']}
>
<Form.Item
name='baseProcessId'
rules={[
{ required: true, message: '请选择流程' },
]}
>
<Radio.Group disabled={type === VISIT_TYPE.VIEW}>
{optionsData.map(_item => (
<Radio key={_item.baseProcessId} value={_item.baseProcessId}>
<div className={styles['box']}>
<div className={styles['box-clerk']}>
<div className={styles['box-clerk-name']}>{_item.processName}</div>
<div className={styles['box-clerk-value']}>{_item.description}</div>
</div>
<div className={styles['box-tag']}>{_item.processTypeName}</div>
</div>
</Radio>
))}
</Radio.Group>
</Form.Item>
</CardLayout>
)
}
export default ProcessSelectLayout;
.cardLayout {
margin-bottom: 24px;
border-radius: 8px;
background-color: #FFF;
.card_title {
padding: 12px 16px;
}
.card_title_weight {
font-size: 14px;
font-weight: 600;
}
.card_body {
padding: 16px;
}
}
import React, { CSSProperties } from 'react';
import style from './index.less';
import cx from 'classnames';
export interface cardProps {
/** 瞄点id */
id?: string,
/** 标题 */
title?: string,
/** 加粗标题 */
weight?: boolean,
/** body样式修改 */
bodyStyle?: CSSProperties,
/** calssName */
classNames?: string,
}
const CardLayout: React.FC<cardProps> = (props: any) => {
const { id, title, weight, children, bodyStyle, classNames, click } = props;
return (
<div id={id} className={cx(style.cardLayout, classNames && classNames)}>
{title && (
<div className={cx(style.card_title, weight && style.card_title_weight)}>{title}</div>
)}
<div className={style.card_body} style={bodyStyle && bodyStyle}>
{children}
</div>
</div>
)
}
export default CardLayout;
/**
* 系统管理 - 平台规则 - 修改合同管理流程规则
* @author: Crayon
*/
import React from 'react'
import { useLocation } from 'umi'
import AddEditContent from './components/AddEditContent'
const Edit: React.FC = () => {
const location: any = useLocation()
const { id } = location.query
return (
<AddEditContent
type='edit'
id={id}
title='修改合同管理流程规则'
/>
)
}
export default Edit
import React, { useRef } from 'react';
import { history, Link } from 'umi';
import { Button, Popconfirm, Space, Switch } from 'antd';
import moment from 'moment';
import { PlusOutlined } from '@ant-design/icons';
import TableLayout from '@/components/TableLayout';
import { ColumnType } from 'antd/lib/table/interface';
import { createTime, id, name, operation, processName, status } from './columns';
import { getPurchaseRequisitionWebProcessPage, postPurchaseRequisitionWebProcessStatusUpdate, getPurchaseRequisitionWebProcessDelete } from '@/services/PurchaseV2Api'
import { getContractPlatformProcessDelete,getContractPlatformProcessPage, postContractPlatformProcessStatusUpdate } from '@/services/ManageV2Api';
const InvoiceProcessEng: React.FC<{}> = () => {
const ref = useRef<any>()
const format = (text, fmt?: string) => {
return <>{moment(text).format(fmt || "YYYY-MM-DD HH:mm:ss")}</>
}
const handleStatus = async ({ processId }, status: number) => {
const res = await postContractPlatformProcessStatusUpdate({
processId,
status
})
if (res.code === 1000) {
ref.current.reload()
}
}
const fetchDeleteBatch = (processId) => {
getContractPlatformProcessDelete({ processId }).then(res => {
if (res.code === 1000) {
ref.current.reload()
}
})
}
const columns: ColumnType<any>[] = [
{
...id,
},
{
...name,
render: (_text, record) => <Link to={`/system/ruleSettingManager/contractManagementProcess/view?id=${record.processId}`}>{_text}</Link>
},
{
...createTime,
render: (_text) => format(_text)
},
{
...status,
render: (_text, record) => <Switch checked={!!_text} onChange={() => handleStatus(record, !!_text ? 0 : 1)} />
},
{
...operation,
render: (_text, record) => (
<Space size={16}>
<a onClick={() => { history.push(`/system/ruleSettingManager/contractManagementProcess/view?id=${record.processId}`) }}>查看</a>
{ !record.status && (
<>
<a onClick={() => { history.push(`/system/ruleSettingManager/contractManagementProcess/edit?id=${record.processId}`) }}>编辑</a>
<Popconfirm
title='确定删除么?'
okText='是'
cancelText='否'
onConfirm={() => fetchDeleteBatch(record.processId)}
>
<a >删除</a>
</Popconfirm>
</>
)}
</Space>
)
},
]
return (
<TableLayout
reload={ref}
columns={columns}
fetch={getContractPlatformProcessPage}
schema={{
type: "object",
properties: {
megalayout: {
type: "object",
"x-component": "flex-layout",
"x-component-props": {
rowStyle: {
justifyContent: 'space-between'
}
},
properties: {
ctl: {
type: "object",
"x-component": "controllerBtns",
},
name: {
type: 'string',
'x-component': 'Search',
'x-component-props': {
placeholder: '流程规则名称',
advanced: false
},
},
}
}
}
}}
controllerBtns={(
<Button
type='primary'
icon={<PlusOutlined />}
onClick={() => history.push('/system/ruleSettingManager/contractManagementProcess/add')}
>
新增
</Button>
)}
/>
)
}
export default InvoiceProcessEng;
/**
* 系统管理 - 平台规则 - 查看合同管理流程规则
* @author: Crayon
*/
import React from 'react'
import { useLocation } from 'umi'
import AddEditContent from './components/AddEditContent'
const View: React.FC = () => {
const location: any = useLocation()
const { id } = location.query
return (
<AddEditContent
type='view'
id={id}
title='查看合同管理流程规则'
/>
)
}
export default View
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment