ylj20011123 b3eba7b8bd update
2025-07-14 19:31:26 +08:00

823 lines
28 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { connect } from "umi";
import type { CurrentUser } from "umi";
import type { ConnectState } from "@/models/connect";
import React, { useRef, useState } from "react";
import ProCard from "@ant-design/pro-card";
import { FileSearchOutlined, MenuFoldOutlined, PlusOutlined } from "@ant-design/icons";
import type { FormInstance } from "antd";
import { Popconfirm } from "antd";
import { Button, message, Modal, Space, Spin, Tree } from "antd";
import useRequest from "@ahooksjs/use-request";
import { getFieldEnumTree, getServerpartTree } from "@/services/options";
import type { ActionType } from "@ant-design/pro-table";
import ProTable from "@ant-design/pro-table";
import ReactHTMLTableToExcel from "react-html-table-to-excel";
import { ModalForm, ProFormRadio, ProFormSelect, ProFormText, ProFormTreeSelect } from "@ant-design/pro-form";
import {
handleDeleteAPPROVALROUTE,
handleDeleteBUSINESSAPPROVAL,
handleGetAPPROVALROUTEList, handleGetUSERList,
handleSynchroAPPROVALROUTE
} from "@/pages/Setting/serviceConfig/service";
import moment from "moment";
import { getUserList, getUserTypeTree } from "@/pages/Setting/Users/service";
import ChosePlayers from "@/pages/Setting/serviceConfig/component/ChosePlayers";
import Draggable from "react-draggable";
import session from "@/utils/session";
const ServiceConfig: React.FC<{ currentUser: CurrentUser }> = (props) => {
const { currentUser } = props
// 选择参与人的
const choseDetail = useRef()
const downloadBtnRef = useRef<any>()
const actionRef = useRef<ActionType>();
const formRef = useRef<FormInstance>();
const addFormRef = useRef<FormInstance>();
const [reqDetailList, setReqDetailList] = useState<any>(); // 合计项数据源
const [printOut, setPrintOut] = useState<any>(); // 打印数据的内容
const [collapsible, setCollapsible] = useState<boolean>(false)
// const [treeView,setTreeView] = useState<any>()
// 业务类型的选择器内容
// const [serviceType,setServiceType] = useState<any>()
// 业务类型的obj格式
// const [serviceTypeObj,setServiceTypeObj] = useState<any>()
const serviceTypeObj = session.get('PROCESS_TYPEObj')
const serviceType = session.get('PROCESS_TYPEList')
const treeView = session.get('PROCESS_TYPETree')
// 选择的角色类型
const [selectRoleType, setSelectRoleType] = useState<number>(0)
// 选择的角色类别id
const [selectRoleTypeId, setSelectRoleTypeId] = useState<any>()
// 加载服务区树
// const { loading: treeLoading, data: treeViews } = useRequest(async () => {
// const data = await getFieldEnumTree({ FieldExplainField: 'PROCESS_TYPE', sessionName: 'PROCESS_TYPE' })
// setTreeView(data)
// const list: any = []
// const obj: any = {}
// if (data && data.length>0){
// data.forEach((item: any)=>{
// list.push({label:item.label,value: item.value})
// obj[item.value] = item.label
// })
// }
// setServiceType(list)
// setServiceTypeObj(obj)
// // return data
// })
// 显示悬浮抽屉
const [modalVisible, setModalVisible] = useState<boolean>(false)
// 树相关的属性和方法
const [selectedId, setSelectedId] = useState<string>()
// 导出的加载效果
const [showLoading, setShowLoading] = useState<boolean>(false)
// 是否显示打印的表格
const [showExportTable, setShowExportTable] = useState<boolean>(false)
// 业务状态选择
const [businessTypeState, setBussinessTypeState] = useState<any>([
// 1000,2000,2010,2020,2030,2040,2050,2060,2070,2080,2090
{ label: '1000', value: 1000 },
{ label: '2000', value: 2000 },
{ label: '2010', value: 2010 },
{ label: '2020', value: 2020 },
{ label: '2030', value: 2030 },
{ label: '2040', value: 2040 },
{ label: '2050', value: 2050 },
{ label: '2060', value: 2060 },
{ label: '2070', value: 2070 },
{ label: '2080', value: 2080 },
{ label: '2090', value: 2090 },
// {label:'商户确认审核',value:2000},
// {label:'服务区经理审核',value:2010},
// {label:'片区中心审核',value:2020},
// {label:'经发部审核',value:2030},
// {label:'财务部审核',value:2040},
])
const [nextBusinessTypeState, setNextBusinessTypeState] = useState<any>([
// 2000,2010,2020,2030,2040,2050,2060,2070,2080,2090,9000
{ label: '2000', value: 2000 },
{ label: '2010', value: 2010 },
{ label: '2020', value: 2020 },
{ label: '2030', value: 2030 },
{ label: '2040', value: 2040 },
{ label: '2050', value: 2050 },
{ label: '2060', value: 2060 },
{ label: '2070', value: 2070 },
{ label: '2080', value: 2080 },
{ label: '2090', value: 2090 },
{ label: '9000', value: 9000 },
// {label:'商户确认审核',value:2000},
// {label:'服务区经理审核',value:2010},
// {label:'片区中心审核',value:2020},
// {label:'经发部审核',value:2030},
// {label:'财务部审核',value:2040},
])
// 显示参与人的悬浮框
const [showChosePlayer, setShowChosePlayer] = useState<boolean>(false)
// 参与人列表
const [playerList, setPlayerList] = useState<any>()
const [playDefaulterList, setPlayDefaulterList] = useState<any>()
// 点击的行的详情数据
const [currentRow, setCurrentRow] = useState<any>()
// 默认的人
const [defaultPerson, setDefaultPerson] = useState<any>()
const draggleRef = React.createRef<any>()
const [disabled, setDraggleDisabled] = useState<boolean>() // 是否拖动
const [bounds, setBounds] = useState<{ left: number, right: number, top: number, bottom: number }>() // 移动的位置
const onDraggaleStart = (event, uiData) => {
const { clientWidth, clientHeight } = window.document.documentElement;
const targetRect = draggleRef.current?.getBoundingClientRect();
if (!targetRect) {
return;
}
setBounds({
left: -targetRect.left + uiData.x,
right: clientWidth - (targetRect.right - uiData.x),
top: -targetRect.top + uiData.y,
bottom: clientHeight - (targetRect.bottom - uiData.y),
});
};
const columns: any = [
{
title: '环节名称',
dataIndex: 'APPROVALROUTE_NAME',
hideInSearch: true
},
{
title: '业务状态',
dataIndex: 'APPROVALROUTE_STATE',
sorter: (a, b) => a.APPROVALROUTE_STATE - b.APPROVALROUTE_STATE,
defaultSortOrder: 'ascend',
// valueType: 'select',
// 暂时只支持业务类型为11的商户对账审批
// valueEnum:{
// 2000:'商户确认审核',
// 2010:'服务区经理审核',
// 2020:'片区中心审核',
// 2030:'经发部审核',
// 2040:'财务部审核',
// }
},
{
title: '业务类型',
dataIndex: 'OPERATION_TYPE',
valueType: 'select',
hideInSearch: true,
valueEnum: serviceTypeObj
},
{
title: '下一业务状态',
dataIndex: 'NEXT_STATE',
// valueType: 'select',
hideInSearch: true,
// valueEnum:{
// 2000:'商户确认审核',
// 2010:'服务区经理审核',
// 2020:'片区中心审核',
// 2030:'经发部审核',
// 2040:'财务部审核',
// }
},
{
title: '参与人',
dataIndex: 'APPROVALSTAFF_NAME',
width: 200,
ellipsis: true,
hideInSearch: true,
},
{
title: '生成时间',
dataIndex: 'RECORD_DATE',
hideInSearch: true,
render: (_, record) => {
return record?.RECORD_DATE ? moment(record?.RECORD_DATE).format('YYYY-MM-DD') : ''
}
},
{
title: '业主单位',
width: 120,
dataIndex: 'PROVINCE_CODE',
hideInSearch: true,
},
{
dataIndex: 'option',
title: '操作',
width: 120,
valueType: 'option',
align: 'center',
fixed: 'right',
hideInSearch: true,
render: (_, record) => {
return (
<Space>
<a onClick={() => {
console.log('record', record)
setSelectRoleType(record?.USER_PATTERN)
setCurrentRow(record)
setModalVisible(true)
}}></a>
<Popconfirm
title="确认删除?"
onConfirm={async () => {
const req: any = {
APPROVALROUTEId: record?.APPROVALROUTE_ID
}
const data = await handleDeleteAPPROVALROUTE(req)
if (data.Result_Code === 100) {
message.success('删除成功')
actionRef.current?.reload()
} else {
message.error(data.Result_Desc)
}
}}
>
<a></a>
</Popconfirm>
</Space>
)
}
}
]
const exportTable = (e) => {
e.stopPropagation(); // 防止Collapse组件收起
const main = document.getElementsByClassName('saleReportHideBox')[0]
const thead = main.querySelector('thead').cloneNode(true); // 深克隆DOM节点
const tbody = main.querySelector('tbody').cloneNode(true); // 深克隆DOM节点
const container = document.querySelector('#hiddenBox');
const tempTable = document.createElement('table');
tempTable.appendChild(thead);
tempTable.appendChild(tbody);
tempTable.setAttribute('id', 'table-to-xls-saleRankReport'); // 给table添加id值与按钮上的table字段对应
container.appendChild(tempTable); // 把创建的节点添加到页面容器中
setShowLoading(false)
downloadBtnRef.current.handleDownload();
setShowExportTable(false)
tempTable.remove() // 防止重复打印一个内容
}
// 查询的条件
const [searchParams, setSearchParams] = useState<any>()
return (
<div ref={(el) => {
// 打印报表
if (!reqDetailList || reqDetailList.length === 0) return;
setPrintOut(el);
}} >
{
showLoading ?
<div
style={{
width: '100%',
height: '100%',
background: 'rgba(0,0,0,0.1)',
position: 'fixed',
zIndex: 5,
display: 'flex',
alignItems: 'center',
justifyContent: 'center'
}}
>
<div style={{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
padding: '15px 20px 10px',
background: '#fff',
borderRadius: '8px',
width: '200px'
}}>
<Spin />
<span style={{ marginLeft: '5px' }}>...</span>
</div>
</div> : ''
}
<div className={'saleReportHideBox'} style={{ position: 'fixed', zIndex: -1, top: 0, left: 0 }}>
{
showExportTable && reqDetailList && reqDetailList.length > 0 ?
<ProTable
columns={columns}
dataSource={reqDetailList}
pagination={false}
expandable={{
defaultExpandAllRows: true
}}
/> : ''
}
</div>
<div id='hiddenBox' style={{ position: 'fixed', zIndex: -1, top: 0, left: 0 }} />
<div style={{ backgroundColor: '#fff', display: 'flex' }}>
<ProCard
style={{ width: !collapsible ? "300px" : "60px" }}
className="pageTable-leftnav"
bodyStyle={{ padding: 0, paddingTop: 20, paddingLeft: 20, width: !collapsible ? "300px" : "60px" }}
extra={<MenuFoldOutlined onClick={() => {
setCollapsible(!collapsible)
}} />}
colSpan={!collapsible ? "300px" : "60px"}
title={!collapsible ? "请选择业务类型" : ""}
headerBordered
collapsed={collapsible}
>
{treeView && treeView.length > 0 ? <Tree
checkable
treeData={[{
label: '全部',
value: 0,
key: '0-0',
children: treeView
}]}
fieldNames={{
title: "label",
key: "key"
}}
blockNode
defaultExpandedKeys={['0-0']}
onCheck={(checkedKeys: React.Key[] | any, info) => {
const selectedIds = info.checkedNodes.filter(n => n?.type === 2)
setSelectedId(selectedIds.map(n => n?.value)?.toString() || '')
// actionRef?.current?.reload()
// getData(selectedIds.map(n => n?.value)?.toString() || '')
}}
// switcherIcon={<PlusOutlined />}
/> : ''}
</ProCard>
<div style={{
width: !collapsible ? 'calc(100% - 300px)' : 'calc(100% - 60px)',
paddingTop: 0,
paddingBottom: 0,
paddingRight: 0
}}>
<ProTable
actionRef={actionRef}
formRef={formRef}
columns={columns}
bordered
expandable={{
expandRowByClick: true
}}
headerTitle={'业务配置'}
search={{ span: 6 }}
request={async (params) => {
if (!selectedId) {
return
}
const req: any = {
SearchParameter: {
OPERATION_TYPES: selectedId || '11',
APPROVALROUTE_VALID: 1,
APPROVALROUTE_STATES: params?.APPROVALROUTE_STATE || '',
},
PageIndex: 1,
PageSize: 999999
}
setSearchParams(params)
const data = await handleGetAPPROVALROUTEList(req)
if (data && data.length > 0) {
return { data, success: true }
}
return { data: [], success: true }
}}
toolbar={{
actions: [
<Button
key="new"
icon={<PlusOutlined />}
type="primary"
onClick={(e) => {
setModalVisible(true)
}}
>
</Button>
]
}}
/>
</div>
</div>
<ModalForm
formRef={addFormRef}
title={currentRow ? "编辑环节" : "新建环节"}
visible={modalVisible}
layout={"horizontal"}
wrapperCol={{ span: 16 }}
labelCol={{ span: 6 }}
labelAlign="right"
width={600}
onVisibleChange={async (value) => {
if (!value) {
addFormRef.current?.resetFields();
setSelectRoleType(0)
setPlayerList([])
setDefaultPerson([])
setCurrentRow(undefined)
setNextBusinessTypeState([
{ label: '2000', value: 2000 },
{ label: '2010', value: 2010 },
{ label: '2020', value: 2020 },
{ label: '2030', value: 2030 },
{ label: '2040', value: 2040 },
{ label: '2050', value: 2050 },
{ label: '2060', value: 2060 },
{ label: '2070', value: 2070 },
{ label: '2080', value: 2080 },
{ label: '2090', value: 2090 },
{ label: '9000', value: 9000 },
])
} else {
if (currentRow) {
setSelectRoleTypeId(currentRow.SYSTEMROLEIDS)
if (currentRow?.APPROVALROUTE_STATE) {
const list: any = [{ label: '2000', value: 2000 },
{ label: '2010', value: 2010 },
{ label: '2020', value: 2020 },
{ label: '2030', value: 2030 },
{ label: '2040', value: 2040 },
{ label: '2050', value: 2050 },
{ label: '2060', value: 2060 },
{ label: '2070', value: 2070 },
{ label: '2080', value: 2080 },
{ label: '2090', value: 2090 },
{ label: '9000', value: 9000 }]
list.forEach((item: any) => {
if (item.value <= Number(currentRow?.APPROVALROUTE_STATE)) {
item.disabled = true
}
})
setNextBusinessTypeState(list)
}
else {
const list: any = [{ label: '2000', value: 2000 },
{ label: '2010', value: 2010 },
{ label: '2020', value: 2020 },
{ label: '2030', value: 2030 },
{ label: '2040', value: 2040 },
{ label: '2050', value: 2050 },
{ label: '2060', value: 2060 },
{ label: '2070', value: 2070 },
{ label: '2080', value: 2080 },
{ label: '2090', value: 2090 },
{ label: '9000', value: 9000 }]
list.forEach((item: any) => {
item.disabled = false
})
setNextBusinessTypeState(list)
}
addFormRef.current?.setFieldsValue({
...currentRow,
APPROVALSTAFF_ID: currentRow?.APPROVALSTAFF_ID.split(',')
})
const req: any = {
SearchParameter: {
USER_IDS: currentRow?.APPROVALSTAFF_ID
},
PageIndex: 1,
PageSize: 999999
}
const data = await handleGetUSERList(req)
if (data && data.length > 0) {
const list: any = []
data.forEach((item: any) => {
list.push({ label: item.USER_NAME, value: item.USER_ID.toString() })
})
setPlayerList(list)
setPlayDefaulterList(list)
}
}
}
}}
onFinish={() => {
addFormRef.current.validateFields().then(async (res: any) => {
if (res) {
let req: any = {}
if (currentRow) {
req = {
APPROVALROUTE_ID: currentRow?.APPROVALROUTE_ID,
APPROVALROUTE_NAME: res?.APPROVALROUTE_NAME || '',
APPROVALROUTE_STATE: res?.APPROVALROUTE_STATE || '',
OPERATION_TYPE: res?.OPERATION_TYPE || '',
USER_PATTERN: res?.USER_PATTERN,
APPROVALSTAFF_ID: res?.APPROVALSTAFF_ID ? res?.APPROVALSTAFF_ID.toString() : null,
APPROVALROUTE_VALID: 1,
// RECORD_DATE:moment().format('YYYY-MM-DD'),
NEXT_STATE: res?.NEXT_STATE
}
} else {
req = {
APPROVALROUTE_NAME: res?.APPROVALROUTE_NAME || '',
APPROVALROUTE_STATE: res?.APPROVALROUTE_STATE || '',
OPERATION_TYPE: res?.OPERATION_TYPE || '',
USER_PATTERN: res?.USER_PATTERN,
APPROVALSTAFF_ID: res?.APPROVALSTAFF_ID ? res?.APPROVALSTAFF_ID.toString() : null,
APPROVALROUTE_VALID: 1,
// RECORD_DATE:moment().format('YYYY-MM-DD'),
NEXT_STATE: res?.NEXT_STATE
}
}
const data = await handleSynchroAPPROVALROUTE(req)
if (data.Result_Code === 100) {
message.success('新增成功')
addFormRef.current?.resetFields();
setCurrentRow(undefined)
actionRef.current?.reload()
setModalVisible(false)
return true
}
message.error(data.Result_Desc)
}
})
}}
modalProps={{
modalRender: (modal) => {
return <Draggable
disabled={disabled}
bounds={bounds}
onStart={(event, uiData) => onDraggaleStart(event, uiData)}
>
<div ref={draggleRef}>{modal}</div>
</Draggable>
},
onCancel: () => {
setModalVisible(false)
},
}}
>
<ProFormTreeSelect
label={'业务类型'}
name={'OPERATION_TYPE'}
request={async (params) => {
if (treeView && treeView.length > 0) {
treeView.forEach((item: any) => {
item.disabled = true
})
}
return treeView
}}
fieldProps={{
treeDefaultExpandAll: true
// options: treeView || []
}}
rules={[
{
required: true,
message: '请选择业务类型',
},
]}
/>
<ProFormText
label={'环节名称'}
name={'APPROVALROUTE_NAME'}
rules={[
{
required: true,
message: '请输入环节名称',
},
]}
/>
<ProFormSelect
label={'业务状态'}
name={'APPROVALROUTE_STATE'}
fieldProps={{
options: businessTypeState || [],
onChange: (e) => {
if (e) {
const list: any = [{ label: '2000', value: 2000 },
{ label: '2010', value: 2010 },
{ label: '2020', value: 2020 },
{ label: '2030', value: 2030 },
{ label: '2040', value: 2040 },
{ label: '2050', value: 2050 },
{ label: '2060', value: 2060 },
{ label: '2070', value: 2070 },
{ label: '2080', value: 2080 },
{ label: '2090', value: 2090 },
{ label: '9000', value: 9000 }]
list.forEach((item: any) => {
if (item.value <= e) {
// if (item.value===e){
item.disabled = true
}
})
setNextBusinessTypeState(list)
} else {
const list: any = [{ label: '2000', value: 2000 },
{ label: '2010', value: 2010 },
{ label: '2020', value: 2020 },
{ label: '2030', value: 2030 },
{ label: '2040', value: 2040 },
{ label: '2050', value: 2050 },
{ label: '2060', value: 2060 },
{ label: '2070', value: 2070 },
{ label: '2080', value: 2080 },
{ label: '2090', value: 2090 },
{ label: '9000', value: 9000 }]
list.forEach((item: any) => {
item.disabled = false
})
setNextBusinessTypeState(list)
}
}
}}
rules={[
{
required: true,
message: '请选择业务状态',
},
]}
/>
<ProFormRadio.Group
label={'角色类型'}
name={'USER_PATTERN'}
options={[
{
label: "业主",
value: 1000,
},
{
label: "商户",
value: 2000,
}
]}
fieldProps={{
onChange: (e: any) => {
setSelectRoleType(Number(e.target.value))
}
}}
rules={[
{
required: true,
message: '请选择业务状态',
},
]}
>
</ProFormRadio.Group>
<ProFormSelect
label={'下一个业务状态'}
name={'NEXT_STATE'}
fieldProps={{
options: nextBusinessTypeState || []
}}
rules={[
{
required: true,
message: '请选择下一个业务状态',
},
]}
/>
{
selectRoleType === 2000 ? '' :
<ProFormSelect
label={'参与人'}
name={'APPROVALSTAFF_ID'}
fieldProps={{
options: playerList || [],
mode: 'multiple'
}}
// rules={[
// {
// required: true,
// message: '请选择参与人',
// },
// ]}
// disabled
addonAfter={
<Button type="primary" icon={<FileSearchOutlined />}
onClick={() => {
const res = addFormRef.current?.getFieldValue('APPROVALSTAFF_ID')
if (res && res.length > 0) {
const list: any = []
res.forEach((item: any) => {
list.push(Number(item))
})
setDefaultPerson(list)
} else {
setDefaultPerson([])
setPlayerList([])
}
setShowChosePlayer(true)
}}>
</Button>}
/>
}
</ModalForm>
<Modal
title={'选择参与人'}
open={showChosePlayer}
width={1400}
// destroyOnClose={!modalVisible}
destroyOnClose
afterClose={() => {
}}
onCancel={() => {
setShowChosePlayer(false)
setDefaultPerson([])
}}
onOk={async () => {
const rowDetailList = choseDetail.current?.selectModalRowList
const rowList = choseDetail.current?.selectedModalOrderRowKeys
const chosePlaySelectId = choseDetail.current?.selectedId
setSelectRoleTypeId(chosePlaySelectId)
addFormRef.current?.setFieldsValue({
APPROVALSTAFF_ID: rowList
})
setDefaultPerson([])
if (rowDetailList && rowDetailList.length > 0) {
console.log('rowDetailList', rowDetailList)
// 去重
const indexList: any = []
// 去重后的list
const deduplicationList: any = []
rowDetailList.forEach((item: any) => {
if (indexList && indexList.length > 0) {
if (indexList.indexOf(item.value) === -1) {
indexList.push(item.value)
deduplicationList.push(item)
}
} else {
indexList.push(item.value)
deduplicationList.push(item)
}
})
console.log('deduplicationList', deduplicationList)
setPlayerList(deduplicationList)
} else {
const req: any = {
SearchParameter: {
USER_IDS: rowList && rowList.length > 0 ? rowList.toString() : ''
},
PageIndex: 1,
PageSize: 999999
}
const data = await handleGetUSERList(req)
if (data && data.length > 0) {
const list: any = []
data.forEach((item: any) => {
list.push({ label: item.USER_NAME, value: Number(item.USER_ID) })
})
setPlayerList(list)
setPlayDefaulterList(list)
}
}
setShowChosePlayer(false)
setDefaultPerson([])
// // 去重
// const newList: any = []
// if (rowDetailList && rowDetailList.length>0){
// const indexList: any = []
// rowDetailList.forEach((item: any)=>{
// if (indexList.indexOf(Number(item.value))===-1){
// newList.push(item)
// indexList.push(Number(item.value))
// }
// })
// }
// setPlayerList(newList)
// addFormRef.current?.setFieldsValue({
// APPROVALSTAFF_ID: rowList
// })
// setShowChosePlayer(false)
// setDefaultPerson([])
}}
>
<ChosePlayers playerList={playerList} defaultPerson={defaultPerson} currentRow={currentRow} modalVisible={modalVisible}
onRef={choseDetail} showChosePlayer={showChosePlayer} currentUser={currentUser} selectRoleTypeId={selectRoleTypeId} />
</Modal>
</div>
)
}
export default connect(({
user
}: ConnectState) => ({
currentUser: user.currentUser
}))(ServiceConfig);