Compare commits

...

5 Commits

Author SHA1 Message Date
ylj20011123
7c36384a38 update 2026-03-02 14:30:30 +08:00
ylj20011123
5e7284afb9 转办流程内容添加 2026-02-12 10:56:27 +08:00
ylj20011123
abcad3423f update 2026-02-09 19:28:24 +08:00
ylj20011123
5e6b143a72 update 2026-02-09 14:48:46 +08:00
ylj20011123
9925f330bd update 2026-02-09 14:33:36 +08:00
16 changed files with 5550 additions and 195 deletions

View File

@ -85,8 +85,28 @@ export default [
},
{
path: '/ComplaintForwardingProcess/index',
name: '投诉转发流程',
name: '投诉受理',
component: "@/pages/ComplaintForwardingProcess/index",
},
{
path: '/ComplaintForwardingProcess/ComplaintForwardingProcessReview',
name: '投诉审核',
component: "@/pages/ComplaintForwardingProcess/ComplaintForwardingProcessReview",
},
{
path: '/ComplaintForwardingProcess/ComplaintForwardingProcessList',
name: '投诉台账',
component: "@/pages/ComplaintForwardingProcess/ComplaintForwardingProcessList",
},
{
path: '/ComplaintForwardingProcess/ComplaintForwardingProcessReport',
name: '投诉报表',
component: "@/pages/ComplaintForwardingProcess/ComplaintForwardingProcessReport",
},
{
path: '/BusinessConfiguration/index',
name: '业务配置',
component: "@/pages/BusinessConfiguration/index",
}
]

Binary file not shown.

View File

@ -314,9 +314,33 @@ const UserModel: UserModelType = {
{
path: '/ComplaintForwardingProcess/index',
redirect: '',
name: '投诉转发流程',
name: '投诉受理',
component: "@/pages/ComplaintForwardingProcess/index",
},
{
path: '/ComplaintForwardingProcess/ComplaintForwardingProcessReview',
redirect: '',
name: '投诉审核',
component: "@/pages/ComplaintForwardingProcess/ComplaintForwardingProcessReview",
},
{
path: '/ComplaintForwardingProcess/ComplaintForwardingProcessList',
redirect: '',
name: '投诉台账',
component: "@/pages/ComplaintForwardingProcess/ComplaintForwardingProcessList",
},
{
path: '/ComplaintForwardingProcess/ComplaintForwardingProcessReport',
redirect: '',
name: '投诉报表',
component: "@/pages/ComplaintForwardingProcess/ComplaintForwardingProcessReport",
},
{
path: '/BusinessConfiguration/index',
redirect: '',
name: '业务配置',
component: "@/pages/BusinessConfiguration/index",
},
]
}
@ -402,6 +426,10 @@ const UserModel: UserModelType = {
"/realEstate/index",
"/ComplaintApproval/index",
"/ComplaintForwardingProcess/index",
"/ComplaintForwardingProcess/ComplaintForwardingProcessReview",
"/ComplaintForwardingProcess/ComplaintForwardingProcessList",
"/ComplaintForwardingProcess/ComplaintForwardingProcessReport",
"/BusinessConfiguration/index",
// '/examine/index',
// '/examine/modal',
// '/examine/question',

View File

@ -0,0 +1,205 @@
import { ActionType, ProCard, ProTable } from "@ant-design/pro-components"
import { useEffect, useImperativeHandle, useRef, useState } from "react"
import { getOnwer, getUserTypeTree, handleGetUserList } from "@/pages/serverpartAssets/service";
import { Tree } from "antd";
import './style.less'
const ChosePlayers = ({ actionRef, onRef, defaultPerson }: { actionRef?: any, onRef: any, defaultPerson: any }) => {
const tableRef = useRef<ActionType>();
// 是否显示账号分类查询树
// 左侧业务类型数据
const [treeView, setTreeView] = useState<any>()
// 选择的部门
const [selectedId, setSelectedId] = useState<any>()
// 选择的行
const [selectedRowKeys, setSelectedRowKeys] = useState<any>()
// 选择的行的详情
const [selectedRowDetail, setSelectedRowDetial] = useState<any>()
// 表格内容
const columns: any = [
{
title: '查询账号',
dataIndex: 'searchKey',
hideInDescriptions: true,
hideInTable: true,
fieldProps: {
placeholder: '请输入账号/用户名称/业主单位/手机号/经营单位'
}
},
{
title: '账号部门',
dataIndex: 'USERTYPE_ID',
hideInSearch: true,
hideInDescriptions: true,
valueType: "treeSelect",
fieldProps: {
options: treeView,
},
},
{
title: '账号',
dataIndex: 'USER_PASSPORT',
sorter: (a, b) => {
return (a.USER_PASSPORT || '').localeCompare((b.USER_PASSPORT || ''))
},
hideInSearch: true,
},
{
title: '用户名称',
dataIndex: 'USER_NAME',
sorter: (a, b) => {
return (a.USER_NAME || '').localeCompare((b.USER_NAME || ''))
},
hideInDescriptions: true,
hideInSearch: true,
},
{
title: '手机号',
dataIndex: 'USER_MOBILEPHONE',
hideInSearch: true
},
{
title: '状态',
dataIndex: 'USER_STATUS',
hideInSearch: true,
filters: true,
onFilter: true,
valueType: 'select',
initialValue: 1,
valueEnum: {
all: { text: '全部', status: 'Default' },
0: { text: '无效', status: 'Default' },
1: { text: '有效', status: 'Processing' },
},
},
{
title: '业主单位',
dataIndex: 'PROVINCE_UNIT',
hideInSearch: true,
ellipsis: true,
sorter: (a, b) => {
return (a.PROVINCE_UNIT || '').localeCompare((b.PROVINCE_UNIT || ''))
},
valueType: "select",
request: async () => {
let data = await getOnwer({ ProvinceCode: "530000", DataType: 1 })
if (data && data.length > 0) {
data.forEach((item: any) => {
item.value = item.value.toString()
})
}
return data
}
},
{
title: '备注',
dataIndex: 'USER_DESC',
hideInSearch: true,
render: (_, record) => {
return record?.USER_DESC
}
}
]
useEffect(() => {
getUserTypeTree({ UserTypePattern: 1000, ShowStatus: true }).then((res: any) => {
console.log('fdsklfja', res);
setTreeView(res)
})
}, [])
useEffect(() => {
console.log('defaultPersondefaultPerson', defaultPerson);
if (defaultPerson && defaultPerson.length > 0) {
setSelectedRowDetial(defaultPerson)
let keyList: any = []
defaultPerson.forEach((item: any) => {
keyList.push(item.USER_ID)
})
setSelectedRowKeys(keyList)
}
}, [defaultPerson])
useImperativeHandle(onRef, () => ({
selectedRowKeys,
selectedRowDetail
}))
return (
<div className="ChosePlayersBox" style={{ backgroundColor: '#fff', display: 'flex', width: '100%', height: '700px' }}>
<ProCard
style={{ width: "300px" }}
className="ChosePlayersLeft"
bodyStyle={{ padding: 0, paddingTop: 20, paddingLeft: 20, width: "300px" }}
colSpan={"300px"}
headerBordered
>
{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: any) => n?.type === 1)
let res: any = selectedIds.map(n => n?.value)?.toString() || ''
console.log('reqs', res);
setSelectedId(res)
}}
/> : ''}
</ProCard>
<div style={{
width: 'calc(100% - 300px)',
paddingTop: 0,
paddingBottom: 0,
paddingRight: 0
}}>
<ProTable
rowKey="USER_ID"
actionRef={tableRef}
scroll={{ x: "100%", y: '430px' }}
options={false}
request={async (params, sorter) => {
if (!selectedId) return []
const req: any = {
SearchParameter: {
UserTypeIds: selectedId,
USER_STATUS: 1
},
PageIndex: 1,
PageSize: 999999,
keyWord: params.searchKey ? { key: "USER_PASSPORT,USER_NAME,USER_MOBILEPHONE,PROVINCE_UNIT,BUSINESSMAN_NAME", value: params.searchKey } : null,
}
const data = await handleGetUserList(req)
console.log('dsajkdjas;da', data);
if (data && data.length > 0) {
return { data, success: true }
}
return { data: [], success: true }
}}
columns={columns}
rowSelection={{
selectedRowKeys: selectedRowKeys,
onChange: (e: any, detail: any) => {
setSelectedRowDetial(detail)
setSelectedRowKeys(e)
},
preserveSelectedRowKeys: true,
}}
/>
</div>
</div>
)
}
export default ChosePlayers;

View File

@ -0,0 +1,7 @@
.ChosePlayersBox {
.ant-pro-card {
.ant-pro-card-body {
overflow-y: scroll;
}
}
}

View File

@ -0,0 +1,491 @@
import { FileSearchOutlined, MenuFoldOutlined, PlusOutlined } from "@ant-design/icons"
import { ActionType, ProCard, ProForm, ProFormDigit, ProFormSelect, ProFormText, ProFormTreeSelect, ProTable } from "@ant-design/pro-components"
import { Button, FormInstance, message, Modal, Popconfirm, Space, Tree } from "antd"
import { useEffect, useRef, useState } from "react"
import { getFieldEnumTree, handleDeleteAPPROVALROUTE, handleGetAPPROVALROUTEList, handleGetUserList, handleSynchroAPPROVALROUTE } from "../serverpartAssets/service"
import moment from "moment"
import ChosePlayers from "./components/ChosePlayers"
const BusinessConfiguration = () => {
const actionRef = useRef<ActionType>();
const formRef = useRef<FormInstance>();
const modalFormRef = useRef<FormInstance>();
const chosePlayerRef = useRef<any>();
// 折叠左侧业务类型属性选择框
const [collapsible, setCollapsible] = useState<boolean>(false)
// 左侧业务类型数据
const [treeView, setTreeView] = useState<any>()
// 左侧业务类型数据对象
const [serviceTypeObj, setServiceTypeObj] = useState<any>()
// 树相关的属性和方法
const [selectedId, setSelectedId] = useState<string>()
// 存储当前数据的搜索条件
const [searchParams, setSearchParams] = useState<any>()
// 显示新增环节的悬浮框
const [modalVisible, setModalVisible] = useState<boolean>(false)
// 选中的当前行数据
const [currentRow, setCurrentRow] = useState<any>()
// 悬浮框的确认按钮的loading
const [confirmLoading, setConfirmLoading] = useState<boolean>(false)
// 参与人列表
const [playerList, setPlayerList] = useState<any>()
// 当前已经存在了的参与人
const [defaultPerson, setDefaultPerson] = useState<any>()
// 显示参与人的悬浮框
const [showChosePlayer, setShowChosePlayer] = useState<boolean>(false)
const columns: any = [
{
title: '环节名称',
width: 150,
dataIndex: 'APPROVALROUTE_NAME',
align: 'center',
hideInSearch: true
},
{
title: '业务状态',
width: 120,
dataIndex: 'APPROVALROUTE_STATE',
align: 'center',
sorter: (a, b) => a.APPROVALROUTE_STATE - b.APPROVALROUTE_STATE,
defaultSortOrder: 'ascend',
},
{
title: '业务类型',
width: 120,
dataIndex: 'OPERATION_TYPE',
valueType: 'select',
align: 'center',
hideInSearch: true,
valueEnum: serviceTypeObj
},
{
title: '下一业务状态',
width: 120,
dataIndex: 'NEXT_STATE',
align: 'center',
// valueType: 'select',
hideInSearch: true,
// valueEnum:{
// 2000:'商户确认审核',
// 2010:'服务区经理审核',
// 2020:'片区中心审核',
// 2030:'经发部审核',
// 2040:'财务部审核',
// }
},
{
title: '参与人',
dataIndex: 'APPROVALSTAFF_NAME',
align: 'center',
width: 200,
ellipsis: true,
hideInSearch: true,
},
{
title: '生成时间',
width: 150,
dataIndex: 'RECORD_DATE',
align: 'center',
hideInSearch: true,
render: (_, record) => {
return record?.RECORD_DATE ? moment(record?.RECORD_DATE).format('YYYY-MM-DD') : ''
}
},
{
title: '业主单位',
width: 120,
align: 'center',
dataIndex: 'PROVINCE_CODE',
hideInSearch: true,
},
{
dataIndex: 'option',
title: '操作',
width: 120,
valueType: 'option',
align: 'center',
fixed: 'right',
hideInSearch: true,
render: (_, record) => {
return (
<Space>
<a onClick={() => {
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 extractValueLabel = (list: any) => {
const result: any = {};
function traverse(nodes: any) {
if (!Array.isArray(nodes)) return;
nodes.forEach(item => {
if (item.value !== undefined && item.label !== undefined) {
result[item.value] = item.label;
}
if (Array.isArray(item.children)) {
traverse(item.children);
}
});
}
traverse(list);
return result;
}
useEffect(() => {
getFieldEnumTree({ FieldExplainField: 'PROCESS_TYPE', sessionName: 'PROCESS_TYPE' }).then((res: any) => {
console.log('resresresres', res);
let obj = extractValueLabel(res)
setServiceTypeObj(obj)
setTreeView(res)
})
}, []);
return (
<div style={{ height: 'calc(100vh - 100px)', background: "#fff", display: 'flex' }}>
<div>
<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() || '')
}}
// switcherIcon={<PlusOutlined />}
/> : ''}
</ProCard>
</div>
<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: {
PROVINCE_CODE: '530000',
OPERATION_TYPES: selectedId || '',
APPROVALROUTE_VALID: 1
},
PageIndex: 1,
PageSize: 999999
}
setSearchParams(params)
const data = await handleGetAPPROVALROUTEList(req)
console.log('fdsjfhsdjkfhsa', data);
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>
<Modal
title={currentRow ? "编辑环节" : "新增环节"}
width={600}
destroyOnClose
open={modalVisible}
confirmLoading={confirmLoading}
onOk={(e) => {
setModalVisible(false)
modalFormRef.current?.validateFields().then((res: any) => {
modalFormRef.current?.submit()
})
}}
onCancel={(e) => {
setModalVisible(false)
modalFormRef.current?.resetFields();
setDefaultPerson([])
setPlayerList([])
setCurrentRow(undefined)
}}
>
<ProForm
formRef={modalFormRef}
submitter={false}
initialValues={currentRow}
request={async () => {
console.log('currentRowcurrentRow', currentRow);
if (currentRow) {
if (currentRow?.APPROVALSTAFF_ID) {
const req: any = {
SearchParameter: {
USER_IDS: currentRow?.APPROVALSTAFF_ID
},
PageIndex: 1,
PageSize: 999999
}
const data = await handleGetUserList(req)
console.log('datadatadata', data);
setDefaultPerson(data)
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)
}
}
return {
...currentRow,
APPROVALSTAFF_ID: currentRow?.APPROVALSTAFF_ID ? currentRow?.APPROVALSTAFF_ID.split(',') : null
}
} else {
return {}
}
}}
onFinish={async (res) => {
console.log('readas', 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 || '',
APPROVALSTAFF_ID: res?.APPROVALSTAFF_ID ? res?.APPROVALSTAFF_ID.toString() : null,
APPROVALROUTE_VALID: 1,
PROVINCE_CODE: '530000',
NEXT_STATE: res?.NEXT_STATE
}
} else {
req = {
APPROVALROUTE_NAME: res?.APPROVALROUTE_NAME || '',
APPROVALROUTE_STATE: res?.APPROVALROUTE_STATE || '',
OPERATION_TYPE: res?.OPERATION_TYPE || '',
APPROVALSTAFF_ID: res?.APPROVALSTAFF_ID ? res?.APPROVALSTAFF_ID.toString() : null,
APPROVALROUTE_VALID: 1,
PROVINCE_CODE: '530000',
NEXT_STATE: res?.NEXT_STATE
}
}
setConfirmLoading(true)
const data = await handleSynchroAPPROVALROUTE(req)
setConfirmLoading(false)
if (data.Result_Code === 100) {
message.success('新增成功')
modalFormRef.current?.resetFields();
setCurrentRow(undefined)
actionRef.current?.reload()
setModalVisible(false)
setDefaultPerson([])
setPlayerList([])
return true
}
message.error(data.Result_Desc)
}}
>
<ProFormTreeSelect
label={"业务类型"}
name={"OPERATION_TYPE"}
request={async (params) => {
let list: any = JSON.parse(JSON.stringify(treeView))
if (list && list.length > 0) {
list.forEach((item: any) => {
item.disabled = true
})
}
return list
}}
fieldProps={{
treeDefaultExpandAll: true
}}
rules={[
{
required: true,
message: '请选择业务类型',
},
]}
/>
<ProFormText
label={'环节名称'}
name={'APPROVALROUTE_NAME'}
rules={[
{
required: true,
message: '请输入环节名称',
},
]}
/>
<ProFormDigit
label={"业务状态"}
name={"APPROVALROUTE_STATE"}
rules={[
{
required: true,
message: '请选择业务状态',
},
]}
/>
<ProFormDigit
label={"下一个业务状态"}
name={"NEXT_STATE"}
rules={[
{
required: true,
message: '请选择下一个业务状态',
},
]}
tooltip={'若下一状态为审结则是9000'}
/>
<ProFormSelect
label={'参与人'}
name={'APPROVALSTAFF_ID'}
width={'lg'}
fieldProps={{
options: playerList || [],
mode: 'multiple'
}}
addonAfter={
<Button type="primary" icon={<FileSearchOutlined />}
onClick={() => {
const res = modalFormRef.current?.getFieldValue('APPROVALSTAFF_ID')
console.log('resres', res);
// if ([res] && [res].length > 0) {
// const list: any = []
// [res].forEach((item: any) => {
// list.push(Number(item))
// })
// setDefaultPerson(list)
// } else {
// setDefaultPerson([])
// setPlayerList([])
// }
setShowChosePlayer(true)
}}>
</Button>}
/>
</ProForm>
</Modal>
<Modal
title={'选择参与人'}
width={'80%'}
open={showChosePlayer}
destroyOnClose
bodyStyle={{
height: '700px',
overflowY: 'auto'
}}
onOk={(e) => {
setShowChosePlayer(false)
let detailList: any = chosePlayerRef?.current?.selectedRowDetail
console.log('detailListdetailList', detailList);
let list: any = []
if (detailList && detailList.length > 0) {
detailList.forEach((item: any) => {
list.push({ label: item?.USER_NAME, value: item.USER_ID })
})
}
setPlayerList(list)
modalFormRef.current?.setFieldsValue({ APPROVALSTAFF_ID: chosePlayerRef?.current?.selectedRowKeys })
}}
onCancel={(e) => {
setShowChosePlayer(false)
}}
>
<ChosePlayers onRef={chosePlayerRef} actionRef={actionRef} defaultPerson={defaultPerson} />
</Modal>
</div>
)
}
export default BusinessConfiguration;

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,424 @@
import LeftSelectTree from "@/components/leftSelectTree/leftSelectTree";
import { ActionType, FormInstance, ProTable } from "@ant-design/pro-components";
import { useRef, useState } from "react";
import { getFieldEnumTreeNoSession, handleGetSUGGESTIONReport } from "../serverpartAssets/service";
import { Drawer, Modal } from "antd";
import ComplaintForwardingProcessList from "./ComplaintForwardingProcessList";
import moment from "moment";
const ComplaintForwardingProcessReport = () => {
const actionRef = useRef<ActionType>();
const formRef = useRef<FormInstance>();
// 树相关的属性和方法
const [selectedId, setSelectedId] = useState<string>()
const [collapsible, setCollapsible] = useState<boolean>(false)
// 显示详情抽屉
const [showDetailModal, setShowDetailModal] = useState<boolean>(false)
// 当前行数据
const [currentRow, setCurrentRow] = useState<any>()
// 要查看的类型
const [isComponentsParams, setIsComponentsParams] = useState<any>()
// 搜索入参
const [searchParams, setSearchParams] = useState<any>()
// 表格列内容
const columns: any = [
{
title: "统计时间",
dataIndex: 'search_date',
valueType: 'dateRange',
hideInTable: true,
hideInDescriptions: true,
search: {
transform: (value) => {
return {
SUGGESTION_CREATEDATE_Start: value[0],
SUGGESTION_CREATEDATE_End: value[1],
};
},
},
initialValue: [moment().subtract(1, 'M'), moment()],
},
{
title: "建议类型",
dataIndex: "SUGGESTION_TYPES",
valueType: 'select',
valueEnum: {
"1000": "投诉",
"1050": "表扬",
"4000": "咨询/建议"
},
hideInTable: true
},
{
title: "投诉渠道",
width: 120,
dataIndex: "SUGGESTION_CHANNELS",
valueType: "select",
align: 'center',
request: async () => {
const taxRateLabel: any = await getFieldEnumTreeNoSession({ FieldExplainField: 'SUGGESTION_CHANNEL', notformate: true })
console.log('taxRateLabeltaxRateLabel', taxRateLabel);
return taxRateLabel
},
hideInTable: true
},
{
title: "服务区",
width: 200,
align: 'center',
dataIndex: "ServerPart_Name",
hideInSearch: true,
ellipsis: true,
render: (_, record) => {
return record?.SpregionType_Name || record?.ServerPart_Name
}
},
{
title: "总计",
width: 120,
align: 'center',
dataIndex: "TotalCount",
hideInSearch: true,
ellipsis: true,
render: (_, record) => {
return record?.TotalCount ? <a onClick={() => {
handleGetComponentsParams(1, record)
setCurrentRow(record)
setShowDetailModal(true)
}}> {record?.TotalCount}</a > : "0"
}
},
{
title: "投诉数量",
width: 120,
align: 'center',
dataIndex: "ComplaintCount",
hideInSearch: true,
ellipsis: true,
render: (_, record) => {
return record?.ComplaintCount ? <a onClick={() => {
handleGetComponentsParams(2, record)
setCurrentRow(record)
setShowDetailModal(true)
}}> {record?.ComplaintCount}</a > : "0"
}
},
{
title: "表扬数量",
width: 120,
align: 'center',
dataIndex: "PraiseCount",
hideInSearch: true,
ellipsis: true,
render: (_, record) => {
return record?.PraiseCount ? <a onClick={() => {
handleGetComponentsParams(3, record)
setCurrentRow(record)
setShowDetailModal(true)
}}> {record?.PraiseCount}</a> : "0"
}
},
{
title: "咨询建议数量",
width: 120,
align: 'center',
dataIndex: "ConsultationSuggestionCount",
hideInSearch: true,
ellipsis: true,
render: (_, record) => {
return record?.ConsultationSuggestionCount ? <a onClick={() => {
handleGetComponentsParams(4, record)
setCurrentRow(record)
setShowDetailModal(true)
}}> {record?.ConsultationSuggestionCount}</a> : "0"
}
},
{
title: "小程序数量",
width: 120,
align: 'center',
dataIndex: "MiniProgramCount",
hideInSearch: true,
ellipsis: true,
render: (_, record) => {
return record?.MiniProgramCount ? <a onClick={() => {
handleGetComponentsParams(5, record)
setCurrentRow(record)
setShowDetailModal(true)
}}> {record?.MiniProgramCount}</a> : "0"
}
},
{
title: "12328交通运输数量",
width: 120,
align: 'center',
dataIndex: "Transport12328Count",
hideInSearch: true,
ellipsis: true,
render: (_, record) => {
return record?.Transport12328Count ? <a onClick={() => {
handleGetComponentsParams(6, record)
setCurrentRow(record)
setShowDetailModal(true)
}}> {record?.Transport12328Count}</a> : "0"
}
},
{
title: "网络舆情数量",
width: 120,
align: 'center',
dataIndex: "OnlineOpinionCount",
hideInSearch: true,
ellipsis: true,
render: (_, record) => {
return record?.OnlineOpinionCount ? <a onClick={() => {
handleGetComponentsParams(7, record)
setCurrentRow(record)
setShowDetailModal(true)
}}> {record?.OnlineOpinionCount}</a> : "0"
}
},
{
title: "监控中心值班电话数量",
width: 120,
align: 'center',
dataIndex: "MonitoringCenterCount",
hideInSearch: true,
ellipsis: true,
render: (_, record) => {
return record?.MonitoringCenterCount ? <a onClick={() => {
handleGetComponentsParams(8, record)
setCurrentRow(record)
setShowDetailModal(true)
}}> {record?.MonitoringCenterCount}</a> : "0"
}
},
{
title: "话务转办数量",
width: 120,
align: 'center',
dataIndex: "TransferCount",
hideInSearch: true,
ellipsis: true,
render: (_, record) => {
return record?.TransferCount ? <a onClick={() => {
handleGetComponentsParams(9, record)
setCurrentRow(record)
setShowDetailModal(true)
}}> {record?.TransferCount}</a> : "0"
}
}
]
// 处理入参的办法
const handleGetComponentsParams = (type: number, obj: any) => {
// type 1 总计 2 投诉数量 3 表扬数量 4 咨询建议数量 5 小程序数量 6 12328交通运输数量 7 网络舆情数量 8 监控中心值班电话数量 9 话务转办数量
console.log('dasda', obj);
let req: any = {}
let SERVERPART_IDS: string = ""
if (obj.Type === 0) {
if (obj.children && obj.children.length > 0) {
obj.children.forEach((item: any) => {
if (SERVERPART_IDS) {
SERVERPART_IDS += `,${item.ServerPart_Id}`
} else {
SERVERPART_IDS = item.ServerPart_Id
}
})
}
} else if (obj.Type === 1) {
SERVERPART_IDS = obj.ServerPart_Id
}
console.log('SERVERPART_IDSSERVERPART_IDS', SERVERPART_IDS);
console.log('type123123', type);
if (type === 1) {
req = {
SearchParameter: {
SERVERPART_IDS: SERVERPART_IDS,
SUGGESTION_CREATEDATE_Start: searchParams?.SUGGESTION_CREATEDATE_Start || "",
SUGGESTION_CREATEDATE_End: searchParams?.SUGGESTION_CREATEDATE_End || ""
},
SortStr: "SUGGESTION_CREATEDATE desc",
PageIndex: 1,
PageSize: "999999"
}
} else if (type === 2) {
req = {
SearchParameter: {
SERVERPART_IDS: SERVERPART_IDS,
SUGGESTION_TYPES: "1000,2000",
SUGGESTION_CREATEDATE_Start: searchParams?.SUGGESTION_CREATEDATE_Start || "",
SUGGESTION_CREATEDATE_End: searchParams?.SUGGESTION_CREATEDATE_End || ""
},
SortStr: "SUGGESTION_CREATEDATE desc",
PageIndex: 1,
PageSize: "999999"
}
} else if (type === 3) {
req = {
SearchParameter: {
SERVERPART_IDS: SERVERPART_IDS,
SUGGESTION_TYPES: "1050",
SUGGESTION_CREATEDATE_Start: searchParams?.SUGGESTION_CREATEDATE_Start || "",
SUGGESTION_CREATEDATE_End: searchParams?.SUGGESTION_CREATEDATE_End || ""
},
SortStr: "SUGGESTION_CREATEDATE desc",
PageIndex: 1,
PageSize: "999999"
}
} else if (type === 4) {
req = {
SearchParameter: {
SERVERPART_IDS: SERVERPART_IDS,
SUGGESTION_TYPES: "4000",
SUGGESTION_CREATEDATE_Start: searchParams?.SUGGESTION_CREATEDATE_Start || "",
SUGGESTION_CREATEDATE_End: searchParams?.SUGGESTION_CREATEDATE_End || ""
},
SortStr: "SUGGESTION_CREATEDATE desc",
PageIndex: 1,
PageSize: "999999"
}
} else if (type === 5) {
req = {
SearchParameter: {
SERVERPART_IDS: SERVERPART_IDS,
SUGGESTION_CHANNELS: "1000",
SUGGESTION_CREATEDATE_Start: searchParams?.SUGGESTION_CREATEDATE_Start || "",
SUGGESTION_CREATEDATE_End: searchParams?.SUGGESTION_CREATEDATE_End || ""
},
SortStr: "SUGGESTION_CREATEDATE desc",
PageIndex: 1,
PageSize: "999999"
}
} else if (type === 6) {
req = {
SearchParameter: {
SERVERPART_IDS: SERVERPART_IDS,
SUGGESTION_CHANNELS: "2000",
SUGGESTION_CREATEDATE_Start: searchParams?.SUGGESTION_CREATEDATE_Start || "",
SUGGESTION_CREATEDATE_End: searchParams?.SUGGESTION_CREATEDATE_End || ""
},
SortStr: "SUGGESTION_CREATEDATE desc",
PageIndex: 1,
PageSize: "999999"
}
} else if (type === 7) {
req = {
SearchParameter: {
SERVERPART_IDS: SERVERPART_IDS,
SUGGESTION_CHANNELS: "3000",
SUGGESTION_CREATEDATE_Start: searchParams?.SUGGESTION_CREATEDATE_Start || "",
SUGGESTION_CREATEDATE_End: searchParams?.SUGGESTION_CREATEDATE_End || ""
},
SortStr: "SUGGESTION_CREATEDATE desc",
PageIndex: 1,
PageSize: "999999"
}
} else if (type === 8) {
req = {
SearchParameter: {
SERVERPART_IDS: SERVERPART_IDS,
SUGGESTION_CHANNELS: "4000",
SUGGESTION_CREATEDATE_Start: searchParams?.SUGGESTION_CREATEDATE_Start || "",
SUGGESTION_CREATEDATE_End: searchParams?.SUGGESTION_CREATEDATE_End || ""
},
SortStr: "SUGGESTION_CREATEDATE desc",
PageIndex: 1,
PageSize: "999999"
}
} else if (type === 9) {
req = {
SearchParameter: {
SERVERPART_IDS: SERVERPART_IDS,
SUGGESTION_CHANNELS: "5000",
SUGGESTION_CREATEDATE_Start: searchParams?.SUGGESTION_CREATEDATE_Start || "",
SUGGESTION_CREATEDATE_End: searchParams?.SUGGESTION_CREATEDATE_End || ""
},
SortStr: "SUGGESTION_CREATEDATE desc",
PageIndex: 1,
PageSize: "999999"
}
}
console.log('reqreq', req);
setIsComponentsParams(req)
}
return (
<div style={{ height: 'calc(100vh - 100px)', background: "#fff", display: 'flex' }}>
<LeftSelectTree setSelectedId={setSelectedId} setCollapsible={setCollapsible} collapsible={collapsible} serverPartType={'1000'} ServerpartSiteType={'1000,1010,3000'} />
<div style={{
width: !collapsible ? 'calc(100% - 300px)' : 'calc(100% - 60px)',
paddingTop: 0,
paddingBottom: 0,
paddingRight: 0
}}>
<ProTable
actionRef={actionRef}
formRef={formRef}
columns={columns}
bordered
rowKey={(record: any) => {
return `${record?.SpregionType_Id}-${record?.ServerPart_Id}-${record?.Type}`
}}
scroll={{ x: '100%', y: 'calc(100vh - 450px)' }}
headerTitle={'流程报表'}
search={{ span: 6 }}
request={async (params) => {
if (!selectedId) {
return []
}
console.log('paramsparams', params);
const req: any = {
SearchParameter: {
SERVERPART_IDS: selectedId,
SUGGESTION_TYPES: params?.SUGGESTION_TYPES || "",
SUGGESTION_CHANNELS: params?.SUGGESTION_CHANNELS || "",
SUGGESTION_CREATEDATE_Start: params?.SUGGESTION_CREATEDATE_Start || "",
SUGGESTION_CREATEDATE_End: params?.SUGGESTION_CREATEDATE_End || ""
},
PageIndex: 1,
PageSize: 999999
}
setSearchParams(params)
const data = await handleGetSUGGESTIONReport(req)
console.log('datadata', data);
if (data && data.length > 0) {
return { data, success: true }
}
return { data: [], success: true }
}}
toolbar={{
actions: [
]
}}
/>
</div>
<Drawer
width={"80%"}
open={showDetailModal}
onClose={() => {
setShowDetailModal(false)
setCurrentRow(undefined)
setIsComponentsParams(undefined)
}}
destroyOnClose
footer={false}
>
<ComplaintForwardingProcessList isComponents={true} isComponentsParams={isComponentsParams} />
</Drawer>
</div>
)
}
export default ComplaintForwardingProcessReport;

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,287 @@
import moment from 'moment';
import React from 'react';
import ReactDOM from 'react-dom';
let SUGGESTION_TYPEOBJ: any = {
1000: "小程序",
2000: "12328交通运输",
3000: "网络舆情",
4000: "监控中心值班电话",
5000: "话务转办",
}
// 附件1 结构
export const Attachment1 = ({ record }: { record: any }) => {
// 根据 record 数据处理显示逻辑
return (
<div className="printContainer attachment1">
<div className="printTitle">1</div>
<div style={{ textAlign: 'left', marginBottom: '5px' }}>{record?.SUGGESTION_CODE || ''}</div>
<table className="printTable">
<tbody>
<tr>
<td className="labelCell"></td>
<td className="contentCell">{record?.SUGGESTION_NAME || ''}</td>
<td className="labelCell"></td>
<td className="contentCell">{record?.PHONE_NUMBER || ''}</td>
</tr>
<tr>
<td className="labelCell"></td>
<td className="contentCell">{record?.DISPOSITION_PERSON || ''}</td>
<td className="labelCell"></td>
<td className="contentCell">{record?.OPERATE_DATE ? moment(record?.OPERATE_DATE).format('YYYY-MM-DD HH:mm:ss') : ''}</td>
</tr>
<tr>
<td className="labelCell"></td>
<td className="contentCell">{record?.MANAGER_NAME || ''}</td>
<td className="labelCell"></td>
<td className="contentCell">{record?.SUGGESTION_TYPE ? SUGGESTION_TYPEOBJ[Number(record?.SUGGESTION_TYPE)] : ''}</td>
</tr>
<tr>
<td className="labelCell"></td>
<td colSpan={3} className="contentCell">{record?.SUGGESTION_REASON || ''}</td>
</tr>
<tr>
<td className="labelCell"></td>
<td colSpan={3} className="requirement-cell">{record?.SUGGESTION_INFO || ''}</td>
</tr>
<tr>
<td className="labelCell"></td>
<td colSpan={3} className="feedback-cell">
<div className="feedback-content-wrapper">
<div className="feedback-text">{record?.FEEDBACK_INFO || ''}</div>
<div className="feedback-footer">
{record?.LEADERSHIP_NAME || ''} {record?.FEEDBACK_DATE ? moment(record?.FEEDBACK_DATE).format('YYYY年MM月DD日 HH时mm分') : ''}
</div>
</div>
</td>
</tr>
</tbody>
</table>
<div style={{ textAlign: 'left', marginTop: '5px', marginBottom: '5px' }}>驿CYYGY0001</div>
</div>
);
};
// 附件2 结构
export const Attachment2 = ({ record }: { record: any }) => (
<div className="printContainer attachment2">
<div className="printTitle">12328</div>
<div style={{ textAlign: 'left', marginBottom: '5px' }}>{record?.SUGGESTION_CODE || ''}</div>
<table className="printTable">
<tbody>
<tr>
<td className="labelCell"></td>
<td className="contentCell">{record?.SUGGESTION_NAME || ''}</td>
<td className="labelCell"></td>
<td className="contentCell">{record?.PHONE_NUMBER || ''}</td>
</tr>
<tr>
<td className="labelCell"></td>
<td className="contentCell">{record?.SERVERPART_NAME || ''}</td>
<td className="labelCell"></td>
<td className="contentCell">{record?.SUGGESTION_CREATEDATE || ''}</td>
</tr>
<tr>
<td className="labelCell"></td>
<td className="contentCell">{record?.MANAGER_NAME || ''}</td>
<td className="labelCell"></td>
<td className="contentCell">{record?.SUGGESTION_TYPE ? SUGGESTION_TYPEOBJ[Number(record?.SUGGESTION_TYPE)] : ''}</td>
</tr>
<tr>
<td className="labelCell"></td>
<td colSpan={3} className="requirement-cell">
<div className="feedback-content-wrapper">
<div className="feedback-text">{record?.SUGGESTION_REASON || ''}</div>
</div>
</td>
</tr>
<tr>
<td className="labelCell"></td>
<td colSpan={3} className="requirement-cell">
<div className="feedback-content-wrapper">
<div className="feedback-text">{record?.SUGGESTION_INFO || ''}</div>
<div className="feedback-footer">
{record?.DISPOSITION_PERSON || ''} {record?.OPERATE_DATE ? moment(record?.OPERATE_DATE).format('YYYY年MM月DD日 HH时mm分') : ''}
</div>
</div>
</td>
</tr>
<tr>
<td className="labelCell"></td>
<td colSpan={3} className="feedback-cell">
<div className="feedback-content-wrapper">
<div className="feedback-text">{record?.FEEDBACK_INFO || ''}</div>
<div className="feedback-footer">
{record?.LEADERSHIP_NAME || ''} {record?.FEEDBACK_DATE ? moment(record?.FEEDBACK_DATE).format('YYYY年MM月DD日 HH时mm分') : ''}
</div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
);
// 附件3 结构
export const Attachment3 = ({ record }: { record: any }) => (
<div className="printContainer attachment3">
<div className="printTitle"></div>
<div style={{ textAlign: 'right', marginBottom: '5px' }}>{record?.SUGGESTION_CREATEDATE ? moment(record?.SUGGESTION_CREATEDATE).format('YYYY年MM月DD日 HH时mm分') : ''}</div>
<table className="printTable">
<tbody>
<tr>
<td className="labelCell"></td>
<td colSpan={3} className="contentCell">{record?.MANAGER_NAME || ''}</td>
</tr>
<tr>
<td className="labelCell"></td>
<td colSpan={3} className="contentCell">{record?.LEADERSHIP_NAME || ''}</td>
</tr>
<tr>
<td className="labelCell"></td>
<td className="contentCell">{record?.LEADERSHIP_NAME || ''}</td>
<td className="labelCell"></td>
<td className="contentCell">{record?.PHONE_NUMBER || ''}</td>
</tr>
<tr>
<td className="labelCell"></td>
<td colSpan={3} className="requirement-cell">
<div className="feedback-content-wrapper">
<div className="feedback-text">{record?.SUGGESTION_REASON || ''}</div>
</div>
</td>
</tr>
<tr>
<td className="labelCell"></td>
<td colSpan={3} className="feedback-cell">
<div className="feedback-content-wrapper">
<div className="feedback-text">{record?.SUGGESTION_INFO || ''}</div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
);
// 打印函数
export const handlePrintByIframe = (AttachmentComponent: React.ComponentType<any>, record: any) => {
const iframe = document.createElement('iframe');
iframe.style.position = 'fixed';
iframe.style.right = '0';
iframe.style.bottom = '0';
iframe.style.width = '0';
iframe.style.height = '0';
iframe.style.border = '0';
document.body.appendChild(iframe);
const iframeDoc = iframe.contentWindow?.document;
if (!iframeDoc) return;
iframeDoc.open();
iframeDoc.write(`
<html>
<head>
<title></title>
<style>
@page { size: A4; margin: 15mm; }
body { margin: 0; padding: 0; font-family: SimSun, STSong, serif; }
.printContainer {
width: 100%;
box-sizing: border-box;
}
.printTitle { text-align: center; font-size: 24px; font-weight: bold; margin-bottom: 25px; margin-top: 10px; }
.printTable {
width: 100%;
border-collapse: collapse;
border: 2px solid #000;
table-layout: fixed;
}
.printTable td { border: 1px solid #000; padding: 10px 12px; font-size: 15px; word-break: break-all; }
.labelCell { background-color: #f2f2f2 !important; width: 110px; font-weight: bold; text-align: center; -webkit-print-color-adjust: exact; print-color-adjust: exact; }
.contentCell { text-align: left; }
/* --- 附件1 专用样式 --- */
.attachment1 {
min-height: 260mm;
display: flex;
flex-direction: column;
}
.attachment1 .printTable { height: auto; }
.attachment1 .requirement-cell { height: 100px; vertical-align: top; }
.attachment1 .feedback-cell { vertical-align: top; padding: 0 !important; }
.attachment1 .feedback-content-wrapper {
display: flex; flex-direction: column; min-height: 550px; padding: 12px; box-sizing: border-box;
}
.attachment1 .feedback-text { flex: 1; }
.attachment1 .feedback-footer { text-align: right; margin-top: 20px; }
/* --- 附件2 专用样式 --- */
.attachment2 {
min-height: 260mm;
display: flex;
flex-direction: column;
}
.attachment2 .printTable { height: auto; }
.attachment2 .requirement-cell { height: 130px; vertical-align: top; padding: 0 !important; }
.attachment2 .feedback-cell { vertical-align: top; padding: 0 !important; }
.attachment2 .feedback-content-wrapper {
display: flex;
flex-direction: column;
height: 100%;
padding: 12px;
box-sizing: border-box;
}
.attachment2 .feedback-cell .feedback-content-wrapper {
min-height: 400px;
}
.attachment2 .feedback-text { flex: 1; }
.attachment2 .feedback-footer { text-align: right; margin-top: 10px; }
/* --- 附件3 专用样式 --- */
.attachment3 {
min-height: 260mm;
display: flex;
flex-direction: column;
}
.attachment3 .printTable { height: auto; }
.attachment3 .requirement-cell { height: 70px; vertical-align: top; padding: 0 !important; }
.attachment3 .feedback-cell { vertical-align: top; padding: 0 !important; }
.attachment3 .feedback-content-wrapper {
display: flex;
flex-direction: column;
height: 100%;
padding: 12px;
box-sizing: border-box;
}
.attachment3 .feedback-cell .feedback-content-wrapper {
min-height: 500px;
}
.attachment3 .feedback-text { flex: 1; }
</style>
</head>
<body>
<div id="print-root"></div>
</body>
</html>
`);
iframeDoc.close();
const printRoot = iframeDoc.getElementById('print-root');
if (printRoot) {
ReactDOM.render(React.createElement(AttachmentComponent, { record }), printRoot);
}
setTimeout(() => {
iframe.contentWindow?.focus();
iframe.contentWindow?.print();
setTimeout(() => {
document.body.removeChild(iframe);
}, 1000);
}, 300);
};

View File

@ -4,6 +4,32 @@
此文件目前仅保留页面基础样式(如有)。
*/
.print-ignore {
/* 页面按钮展示样式 */
.custom-steps {
display: flex !important;
flex-wrap: wrap !important;
.ant-steps-item {
flex: none !important;
width: 200px !important;
/* 固定每个步骤的宽度,你可以根据需要调整 */
margin-right: 24px !important;
/* 每个步骤之间的固定间距 */
margin-bottom: 24px !important;
/* 换行后的行间距 */
padding-inline-start: 0 !important;
// 隐藏连接线,因为换行后的连线轨迹会错乱
.ant-steps-item-tail {
display: none !important;
}
// 调整标题和描述的布局,确保紧凑
.ant-steps-item-content {
overflow: visible;
.ant-steps-item-title {
white-space: nowrap;
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,96 @@
export interface ResizeHalfOptions {
quality?: number;
}
export const resizeImageHalf = (
file: File,
options: ResizeHalfOptions = {}
): Promise<File> => {
const {
quality = 0.8
} = options;
return new Promise((resolve, reject) => {
if (!file || !file.type.includes('image')) {
reject(new Error('无效的图片文件'));
return;
}
const reader = new FileReader();
reader.onload = (e) => {
const img = new Image();
img.onload = () => {
resizeToHalfDimensions(img, file, quality, resolve, reject);
};
img.onerror = () => reject(new Error('图片加载失败'));
img.src = e.target?.result as string;
};
reader.onerror = () => reject(new Error('文件读取失败'));
reader.readAsDataURL(file);
});
};
/**
* -
*/
function resizeToHalfDimensions(
img: HTMLImageElement,
originalFile: File,
quality: number,
resolve: (file: File) => void,
reject: (error: Error) => void
) {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
if (!ctx) {
reject(new Error('浏览器不支持Canvas'));
return;
}
// 计算减半后的尺寸
const halfWidth = Math.floor(img.width / 2);
const halfHeight = Math.floor(img.height / 2);
console.log(`原图尺寸: ${img.width}x${img.height}`);
console.log(`减半后尺寸: ${halfWidth}x${halfHeight}`);
// 设置canvas尺寸为原图的一半
canvas.width = halfWidth;
canvas.height = halfHeight;
// 在canvas上绘制缩小后的图片
// 使用平滑缩放算法以获得更好的图像质量
ctx.imageSmoothingEnabled = true;
ctx.imageSmoothingQuality = 'high';
ctx.drawImage(img, 0, 0, halfWidth, halfHeight);
// 将canvas转换为Blob并创建新的File对象
canvas.toBlob(
(blob) => {
if (!blob) {
reject(new Error('图片处理失败'));
return;
}
// 创建新的文件名添加_resize_half后缀
const fileNameParts = originalFile.name.split('.');
const extension = fileNameParts.pop();
const fileNameWithoutExtension = fileNameParts.join('.');
const newFileName = `${fileNameWithoutExtension}_half.${extension}`;
// 创建压缩后的文件对象
const resizedFile = new File([blob], newFileName, {
type: originalFile.type,
lastModified: Date.now()
});
console.log(`✅ 图片尺寸减半完成: ${img.width}x${img.height}${halfWidth}x${halfHeight}`);
console.log(`文件大小: ${(originalFile.size / 1024).toFixed(1)}KB → ${(resizedFile.size / 1024).toFixed(1)}KB`);
resolve(resizedFile);
},
originalFile.type,
quality
);
}

View File

@ -0,0 +1,304 @@
import { Button } from "antd";
import ReactDOM from "react-dom";
import "./index.less";
const ComplaintForwardingProcess = () => {
// 附件1 结构(已根据您的要求优化)
const Attachment1 = () => (
<div className="printContainer attachment1">
<div className="printTitle">1</div>
<div style={{ textAlign: 'left', marginBottom: '5px' }}></div>
<table className="printTable">
<tbody>
<tr>
<td className="labelCell"></td>
<td className="contentCell"></td>
<td className="labelCell"></td>
<td className="contentCell"></td>
</tr>
<tr>
<td className="labelCell"></td>
<td className="contentCell"></td>
<td className="labelCell"></td>
<td className="contentCell"></td>
</tr>
<tr>
<td className="labelCell"></td>
<td className="contentCell"></td>
<td className="labelCell"></td>
<td className="contentCell"></td>
</tr>
<tr>
<td className="labelCell"></td>
<td colSpan={3} className="contentCell"></td>
</tr>
<tr>
<td className="labelCell"></td>
<td colSpan={3} className="requirement-cell"></td>
</tr>
<tr>
<td className="labelCell"></td>
<td colSpan={3} className="feedback-cell">
<div className="feedback-content-wrapper">
<div className="feedback-text"></div>
<div className="feedback-footer">
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
</div>
</div>
</td>
</tr>
</tbody>
</table>
<div style={{ textAlign: 'left', marginTop: '5px', marginBottom: '5px' }}>驿CYYGY0001</div>
</div>
);
// 附件2 结构恢复独立不受附件1样式影响
const Attachment2 = () => (
<div className="printContainer attachment2">
<div className="printTitle">12328</div>
<div style={{ textAlign: 'left', marginBottom: '5px' }}></div>
<table className="printTable">
<tbody>
<tr>
<td className="labelCell"></td>
<td className="contentCell"></td>
<td className="labelCell"></td>
<td className="contentCell"></td>
</tr>
<tr>
<td className="labelCell"></td>
<td className="contentCell"></td>
<td className="labelCell"></td>
<td className="contentCell"></td>
</tr>
<tr>
<td className="labelCell"></td>
<td className="contentCell"></td>
<td className="labelCell"></td>
<td className="contentCell"></td>
</tr>
<tr>
<td className="labelCell"></td>
<td colSpan={3} className="requirement-cell">
<div className="feedback-content-wrapper">
<div className="feedback-text"></div>
</div>
</td>
</tr>
<tr>
<td className="labelCell"></td>
<td colSpan={3} className="requirement-cell">
<div className="feedback-content-wrapper">
<div className="feedback-text"></div>
<div className="feedback-footer">
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
</div>
</div>
</td>
</tr>
<tr>
<td className="labelCell"></td>
<td colSpan={3} className="feedback-cell">
<div className="feedback-content-wrapper">
<div className="feedback-text"></div>
<div className="feedback-footer">
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
</div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
);
// 附件3 结构(已加入高度优化)
const Attachment3 = () => (
<div className="printContainer attachment3">
<div className="printTitle"></div>
<div style={{ textAlign: 'right', marginBottom: '5px' }}>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</div>
<table className="printTable">
<tbody>
<tr>
<td className="labelCell"></td>
<td colSpan={3} className="contentCell"></td>
</tr>
<tr>
<td className="labelCell"></td>
<td colSpan={3} className="contentCell"></td>
</tr>
<tr>
<td className="labelCell"></td>
<td className="contentCell"></td>
<td className="labelCell"></td>
<td className="contentCell"></td>
</tr>
<tr>
<td className="labelCell"></td>
<td colSpan={3} className="requirement-cell">
<div className="feedback-content-wrapper">
<div className="feedback-text"></div>
</div>
</td>
</tr>
<tr>
<td className="labelCell"></td>
<td colSpan={3} className="feedback-cell">
<div className="feedback-content-wrapper">
<div className="feedback-text"></div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
);
// 真正的打印函数,使用 Iframe 隔离
const handlePrintByIframe = (AttachmentComponent: React.ComponentType) => {
// 1. 创建隐藏的 iframe
const iframe = document.createElement('iframe');
iframe.style.position = 'fixed';
iframe.style.right = '0';
iframe.style.bottom = '0';
iframe.style.width = '0';
iframe.style.height = '0';
iframe.style.border = '0';
document.body.appendChild(iframe);
const iframeDoc = iframe.contentWindow?.document;
if (!iframeDoc) return;
// 2. 注入样式和容器
iframeDoc.open();
iframeDoc.write(`
<html>
<head>
<title></title>
<style>
@page { size: A4; margin: 15mm; }
body { margin: 0; padding: 0; font-family: SimSun, STSong, serif; }
.printContainer {
width: 100%;
box-sizing: border-box;
}
.printTitle { text-align: center; font-size: 24px; font-weight: bold; margin-bottom: 25px; margin-top: 10px; }
.printTable {
width: 100%;
border-collapse: collapse;
border: 2px solid #000;
table-layout: fixed;
}
.printTable td { border: 1px solid #000; padding: 10px 12px; font-size: 15px; word-break: break-all; }
.labelCell { background-color: #f2f2f2 !important; width: 110px; font-weight: bold; text-align: center; -webkit-print-color-adjust: exact; print-color-adjust: exact; }
.contentCell { text-align: left; }
/* --- 附件1 专用样式 --- */
.attachment1 {
min-height: 260mm;
display: flex;
flex-direction: column;
}
.attachment1 .printTable { height: auto; }
.attachment1 .requirement-cell { height: 100px; vertical-align: top; }
.attachment1 .feedback-cell { vertical-align: top; padding: 0 !important; }
.attachment1 .feedback-content-wrapper {
display: flex; flex-direction: column; min-height: 550px; padding: 12px; box-sizing: border-box;
}
.attachment1 .feedback-text { flex: 1; }
.attachment1 .feedback-footer { text-align: right; margin-top: 20px; }
/* --- 附件2 专用样式 --- */
.attachment2 {
min-height: 260mm;
display: flex;
flex-direction: column;
}
.attachment2 .printTable { height: auto; }
/* 来电内容和转办要求:默认四行 (约 130px) */
.attachment2 .requirement-cell { height: 130px; vertical-align: top; padding: 0 !important; }
.attachment2 .feedback-cell { vertical-align: top; padding: 0 !important; }
/* 公用的内容包装器:用于签名贴底 */
.attachment2 .feedback-content-wrapper {
display: flex;
flex-direction: column;
height: 100%;
padding: 12px;
box-sizing: border-box;
}
/* 关键修复:只有真正的反馈区域才需要 min-height 来撑开全页 */
.attachment2 .feedback-cell .feedback-content-wrapper {
min-height: 400px;
}
.attachment2 .feedback-text { flex: 1; }
.attachment2 .feedback-footer { text-align: right; margin-top: 10px; }
/* --- 附件3 专用样式 --- */
.attachment3 {
min-height: 260mm;
display: flex;
flex-direction: column;
}
.attachment3 .printTable { height: auto; }
/* 舆情概况:默认两行 (约 70px) */
.attachment3 .requirement-cell { height: 70px; vertical-align: top; padding: 0 !important; }
.attachment3 .feedback-cell { vertical-align: top; padding: 0 !important; }
.attachment3 .feedback-content-wrapper {
display: flex;
flex-direction: column;
height: 100%;
padding: 12px;
box-sizing: border-box;
}
.attachment3 .feedback-cell .feedback-content-wrapper {
min-height: 500px;
}
.attachment3 .feedback-text { flex: 1; }
</style>
</head>
<body>
<div id="print-root"></div>
</body>
</html>
`);
iframeDoc.close();
// 3. 将 React 组件渲染到 iframe 中
const printRoot = iframeDoc.getElementById('print-root');
if (printRoot) {
ReactDOM.render(<AttachmentComponent />, printRoot);
}
// 4. 调用打印
setTimeout(() => {
iframe.contentWindow?.focus();
iframe.contentWindow?.print();
// 5. 打印完成后清理
setTimeout(() => {
document.body.removeChild(iframe);
}, 1000);
}, 300);
};
return (
<div style={{ padding: '24px' }}>
<div style={{ display: 'flex', gap: '16px', marginBottom: '24px' }}>
<Button type="primary" onClick={() => handlePrintByIframe(Attachment1)}>1</Button>
<Button type="primary" onClick={() => handlePrintByIframe(Attachment2)}>2</Button>
<Button type="primary" onClick={() => handlePrintByIframe(Attachment3)}>3</Button>
</div>
<div style={{ color: '#999', fontSize: '14px' }}>
使 Iframe ****
2
</div>
</div>
);
};
export default ComplaintForwardingProcess;

View File

@ -16,6 +16,10 @@ const authority: PageAuthority = {
'/realEstate/index': ['/realEstate/index'],
'/ComplaintApproval/index': ['/ComplaintApproval/index'],
'/ComplaintForwardingProcess/index': ['/ComplaintForwardingProcess/index'],
'/ComplaintForwardingProcess/ComplaintForwardingProcessReview': ['/ComplaintForwardingProcess/ComplaintForwardingProcessReview'],
'/ComplaintForwardingProcess/ComplaintForwardingProcessList': ['/ComplaintForwardingProcess/ComplaintForwardingProcessList'],
'/ComplaintForwardingProcess/ComplaintForwardingProcessReport': ['/ComplaintForwardingProcess/ComplaintForwardingProcessReport'],
'/BusinessConfiguration/index': ['/BusinessConfiguration/index'],
};

View File

@ -595,3 +595,163 @@ export async function handleGetServerpartList(params?: any) {
}
// 查询业务环节配置的列表
export async function handleGetAPPROVALROUTEList(params?: any) {
const data = await requestSamember(`/BusinessProcess/GetAPPROVALROUTEList`, {
method: 'POST',
data: params,
});
if (data.Result_Code !== 100) {
return []
}
return data.Result_Data.List;
}
// 获取账号分类树
export async function getUserTypeTree(params?: any) {
const data = await requestSamember('/FrameWork/GetUserTypeTree', {
method: 'GET',
params
})
if (data.Result_Code !== 100) {
return []
}
const treeTable = wrapTreeNode(data.Result_Data.List);
return [...treeTable];
}
// 获取账号
export async function handleGetUserList(params?: any) {
const data = await requestSamember('/Platform/GetUserList', {
method: 'POST',
data: params
})
if (data.Result_Code !== 100) {
return data
}
return data.Result_Data.List
}
export async function getOnwer(params?: any) {
const data = await requestSamember('/BaseInfo/BindingOwnerUnitDDL', {
method: 'GET',
params
})
if (data.Result_Code !== 100) {
return data
}
return data.Result_Data.List
}
// 配置审批环节
export async function handleSynchroAPPROVALROUTE(params?: any) {
const data = await requestSamember('/BusinessProcess/SynchroAPPROVALROUTE', {
method: 'POST',
data: params
})
if (data.Result_Code !== 100) {
return data
}
return data
}
// 删除配置审批环节
export async function handleDeleteAPPROVALROUTE(params?: any) {
const data = await requestSamember(`/BusinessProcess/DeleteAPPROVALROUTE`, {
method: 'GET',
params
});
if (data.Result_Code !== 100) {
return data
}
return data;
}
// 获取用户建议表列表
export async function handleGetSUGGESTIONList(params?: any) {
const data = await requestSamember('/SiteManage/GetSUGGESTIONList', {
method: 'POST',
data: params
})
if (data.Result_Code !== 100) {
return data
}
return data.Result_Data.List
}
// 同步用户建议表
export async function handleSynchroSUGGESTION(params?: any) {
const data = await requestSamember('/SiteManage/SynchroSUGGESTION', {
method: 'POST',
data: params
})
if (data.Result_Code !== 100) {
return data
}
return data
}
// 删除用户建议表
export async function handleDeleteSUGGESTION(params?: any) {
const data = await requestSamember(`/SiteManage/DeleteSUGGESTION`, {
method: 'GET',
params
});
if (data.Result_Code !== 100) {
return data
}
return data;
}
// 同步审批意见
export async function handleSynchroAPPLYAPPROVE(params?: any) {
const data = await requestSamember(`/BusinessProcess/SynchroAPPLYAPPROVE`, {
method: 'POST',
data: params
});
if (data.Result_Code !== 100) {
return data
}
return data;
}
// 审批意见的流程过程
export async function handleGetAPPLYAPPROVEList(params?: any) {
const data = await requestSamember(`/BusinessProcess/GetAPPLYAPPROVEList`, {
method: 'POST',
data: params
});
if (data.Result_Code !== 100) {
return data
}
return data.Result_Data.List;
}
// 用户建议报表页面
export async function handleGetSUGGESTIONReport(params?: any) {
const data = await requestSamember(`/SiteManage/GetSUGGESTIONReport`, {
method: 'POST',
data: params
});
if (data.Result_Code !== 100) {
return data
}
return wrapTreeNode(data.Result_Data);
}