Compare commits

...

10 Commits

Author SHA1 Message Date
ylj20011123
e94956b778 💥 feat(模块): 添加了个很棒的功能 2025-04-21 19:05:23 +08:00
b021eff28f 💥 feat(模块): 添加了个很棒的功能 2025-04-17 19:50:30 +08:00
f3966ac2c9 feat(模块): 添加了个很棒的功能 2025-04-11 14:10:12 +08:00
bf17f9f44b feat(模块): 添加了个很棒的功能 2025-04-09 16:29:40 +08:00
c6da1f9557 💥 feat(模块): 添加了个很棒的功能 2025-03-26 18:59:26 +08:00
f4d966168e feat(模块): 添加了个很棒的功能 2025-03-24 18:59:20 +08:00
a96e829ac4 refactor(question): 重构问题分类树结构和展示逻辑
- 将 `treeDefaultExpandedKeys` 替换为 `treeDefaultExpandAll`,确保树结构默认全部展开
- 新增 `bigTypeList` 状态,用于存储问题分类数据
- 添加递归函数 `handleGetNewQuestion`,将问题数据拼接到对应的分类下
- 更新表格列的渲染逻辑,优化问题分类和选项的展示
2025-03-21 19:02:33 +08:00
70d4f4cb3d feat(模块): 添加了个很棒的功能 2025-03-19 19:23:47 +08:00
2a2a116a7e 💥 feat(模块): 添加了个很棒的功能 2025-03-19 10:52:42 +08:00
637f0cd1c8 💥 feat(模块): 添加了个很棒的功能 2025-03-17 19:04:26 +08:00
29 changed files with 3517 additions and 713 deletions

View File

@ -44,10 +44,26 @@ export default [
path: '/examine/record', path: '/examine/record',
name: '考核记录管理', name: '考核记录管理',
component: "@/pages/examine/record/index", component: "@/pages/examine/record/index",
},
{
path: '/examine/recordSummary',
name: '考核记录汇总',
component: "@/pages/examine/recordSummary/index",
} }
] ]
}, },
{
path: "/setting",
redirect: '',
name: '系统设置',
routes: [
{
path: '/setting/menu',
name: '菜单管理',
component: "@/pages/setting/menu/index",
},
]
}
] ]
} }

View File

@ -10,7 +10,7 @@
height: 30.9px; height: 30.9px;
} }
.username{ .username{
color: #fff; color: #000;
} }
&:hover { &:hover {
// background: #252a3d; // background: #252a3d;

View File

@ -1,14 +1,18 @@
import type { FC } from 'react'; import type { FC } from 'react';
import { connect } from 'umi'; import { connect } from 'umi';
import { Dropdown, Space } from 'antd'; import { Dropdown, Space } from 'antd';
import { LogoutOutlined } from '@ant-design/icons'; import { LogoutOutlined } from '@ant-design/icons';
import type { UserConnectedProps } from '@/models/user'; import type { UserConnectedProps } from '@/models/user';
import './index.less'; import './index.less';
import IconFont from '../IconFont';
const Avatar: FC<UserConnectedProps> = (props) => { const Avatar: FC<UserConnectedProps> = (props) => {
const { const {
user: { data }, dispatch, user: { data }, dispatch,
} = props; } = props;
console.log('user32323', data);
const handleLogout = () => { const handleLogout = () => {
dispatch?.({ dispatch?.({
@ -41,12 +45,16 @@ const Avatar: FC<UserConnectedProps> = (props) => {
menu={{ items }} menu={{ items }}
> >
<Space className="avatar-container"> <Space className="avatar-container">
<img {
alt="avatar" data.avatarUrl ? <img
src={data.avatar} alt="avatar"
className="avatar" src={data.avatarUrl}
/> className="avatar"
<span className="username">{data.name}</span> /> :
<IconFont name='icon-touxiang' size="36" ></IconFont>
}
<span className="username">{data.adminName}</span>
</Space> </Space>
</Dropdown> </Dropdown>
); );

View File

@ -0,0 +1,21 @@
/*
* @Author: cclu 1106109051@qq.com
* @Date: 2025-03-26 16:52:53
* @LastEditors: cclu 1106109051@qq.com
* @LastEditTime: 2025-03-26 16:55:11
* @FilePath: \cloudNew\src\components\IconFont\index.tsx
* @Description: ,`customMade`, koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
import { createFromIconfontCN } from '@ant-design/icons'
const IconEle = createFromIconfontCN({
// 该地址为iconfont中的项目地址根据实际进行修改
scriptUrl: '//at.alicdn.com/t/font_2794551_586f2xpreyu.js'
});
const IconFont = ({ name, style, size, className }: { name: string, style?: any, size: string | number, className?: any }) => {
// const newStyle = size ? { ...{ fontSize: `${size}px`,lineHeight:`${size}px` }, ...style } : {...style}
return <IconEle type={name} style={{ ...style, fontSize: `${size}px`, display: 'flex', alignItems: 'center' }} className={className} />
}
export default IconFont;

View File

@ -8,7 +8,7 @@ import ProForm, { ProFormText } from "@ant-design/pro-form";
import { Button, Col, FormInstance, Row, Tree } from "antd"; import { Button, Col, FormInstance, Row, Tree } from "antd";
// import { getServerpartTree } from "@/services/options"; // import { getServerpartTree } from "@/services/options";
import useRequest from "@ahooksjs/use-request"; import useRequest from "@ahooksjs/use-request";
import { getMerchantShopTree, getServerpartTree } from "./service"; import { getMerchantShopTree, getServerpartTree, handleGetAllServicePart } from "./service";
// import './style.less' // import './style.less'
// import { getMerchantShopTree } from "@/pages/Setting/Users/service"; // import { getMerchantShopTree } from "@/pages/Setting/Users/service";
@ -39,9 +39,19 @@ const LeftSelectTree = ({ setSelectedId, reload, actionRef, currentUser, width,
const { loading: treeLoading, data: treeViews } = useRequest(async () => { const { loading: treeLoading, data: treeViews } = useRequest(async () => {
let data: any = [] let data: any = []
if (currentUser?.UserPattern === 2000) { if (currentUser?.UserPattern === 2000) {
data = await getMerchantShopTree({ BusinessManId: currentUser?.BusinessManID, ShowShop: false }); // data = await getMerchantShopTree({ BusinessManId: currentUser?.BusinessManID, ShowShop: false });
} else { } else {
data = await getServerpartTree(currentUser?.provinceCode, currentUser?.CityAuthority, true, true, true, false, 1000) // data = await getServerpartTree(currentUser?.provinceCode, currentUser?.CityAuthority, true, true, true, false, 1000)
let req: any = {
page: 1,
limit: 999,
sortBy: 'sort',
sortOrder: 'ASC',
code: '510000'
}
data = await handleGetAllServicePart(req)
data.data.list[0].key = data.data.list[0].id
data = data.data.list
} }
console.log('datatree', data); console.log('datatree', data);
@ -60,11 +70,11 @@ const LeftSelectTree = ({ setSelectedId, reload, actionRef, currentUser, width,
list.push(item.children[0]) list.push(item.children[0])
} else { } else {
if (haveTest) { if (haveTest) {
if (item.value !== 424) { if (item.id !== 424) {
list.push(item) list.push(item)
} }
} else { } else {
if (item.value !== 424 && item.value !== 586) { if (item.id !== 424 && item.id !== 586) {
list.push(item) list.push(item)
} }
} }
@ -137,7 +147,7 @@ const LeftSelectTree = ({ setSelectedId, reload, actionRef, currentUser, width,
const handleFilterList = (list: any, id: any) => { const handleFilterList = (list: any, id: any) => {
let res: any = [] let res: any = []
list.forEach((item: any) => { list.forEach((item: any) => {
if (item.value === id) { if (item.id === id) {
} else { } else {
res.push(item) res.push(item)
@ -207,22 +217,17 @@ const LeftSelectTree = ({ setSelectedId, reload, actionRef, currentUser, width,
> >
{treeView && treeView.length > 0 ? <Tree {treeView && treeView.length > 0 ? <Tree
checkable checkable
treeData={isShowAllInTree ? [{ treeData={treeView}
label: '全部',
value: 0,
key: '0-0',
children: treeView
}] : treeView}
fieldNames={{ fieldNames={{
title: "label", title: "name",
key: "key" key: "id"
}} }}
blockNode blockNode
defaultExpandAll={isShowAllInTree ? false : true} defaultExpandAll={isShowAllInTree ? true : false}
defaultExpandedKeys={isShowAllInTree ? treeShowRow && treeShowRow.length > 0 ? treeShowRow : ['0-0'] : []} defaultExpandedKeys={isShowAllInTree ? [] : [510000]}
onCheck={(checkedKeys: React.Key[] | any, info) => { onCheck={(checkedKeys: React.Key[] | any, info) => {
const selectedIds = info.checkedNodes.filter((n: any) => n?.type === 1) const selectedIds = info.checkedNodes.filter((n: any) => n?.districtId > 0)
setSelectedId(selectedIds.map(n => n?.value)?.toString() || '') setSelectedId(selectedIds.map(n => n?.id) || '')
if (reload) { if (reload) {
actionRef?.current?.reload() actionRef?.current?.reload()
} }

View File

@ -59,3 +59,14 @@ export async function handleGetServerpartDDL(params: any) {
return data.Result_Data.List return data.Result_Data.List
} }
import requestNew from "@/utils/request"
// 新的拿到服务区的树的接口
export async function handleGetAllServicePart(params?: any) {
const data = await requestNew.get('/server-part/provinces', params)
if (data.code === 200) {
return data.data
}
return []
}

View File

@ -1,11 +1,4 @@
/*
* @Author: cclu 1106109051@qq.com
* @Date: 2025-02-27 15:55:46
* @LastEditors: cclu 1106109051@qq.com
* @LastEditTime: 2025-03-10 17:46:55
* @FilePath: \umi4-admin-main\src\layouts\index.tsx
* @Description: ,`customMade`, koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
import type { FC } from 'react'; import type { FC } from 'react';
import { useState, useEffect } from 'react'; import { useState, useEffect } from 'react';
import { Dropdown, Layout, Menu, Tabs, Tooltip } from 'antd'; import { Dropdown, Layout, Menu, Tabs, Tooltip } from 'antd';
@ -110,6 +103,8 @@ const BasicLayout: FC<{ user: UserModelState, global: ProfileModelState, dispatc
: item.name, : item.name,
}), }),
); );
console.log('consumableMenuconsumableMenuconsumableMenu', consumableMenu);
// const consumableMenu: MenuDataItem[] = [ // const consumableMenu: MenuDataItem[] = [
// { // {
// path: '/', // path: '/',
@ -200,12 +195,28 @@ const BasicLayout: FC<{ user: UserModelState, global: ProfileModelState, dispatc
hideInMenu: false, hideInMenu: false,
name: "考核记录管理", name: "考核记录管理",
path: "/examine/record", path: "/examine/record",
},
{
SYSTEMMODULE_DESC: "",
guid: "7",
hideInMenu: false,
name: "考核记录汇总",
path: "/examine/recordSummary",
},
{
SYSTEMMODULE_DESC: "",
guid: "6",
hideInMenu: false,
name: "菜单管理",
path: "/setting/menu",
} }
] ]
console.log('tabsRoutes', tabsRoutes); console.log('tabsRoutes', tabsRoutes);
console.log('location', location); console.log('location', location);
console.log('consumableMenu', consumableMenu); console.log('consumableMenu', consumableMenu);
console.log('oneFloorListoneFloorListoneFloorListoneFloorList', oneFloorList);
return ( return (
@ -273,10 +284,9 @@ const BasicLayout: FC<{ user: UserModelState, global: ProfileModelState, dispatc
actionsRender={() => <nav />} actionsRender={() => <nav />}
onPageChange={(location) => { onPageChange={(location) => {
console.log('location', location); console.log('location', location);
if (location?.pathname && location?.pathname !== '/') { if (location?.pathname && location?.pathname !== '/') {
const nextModule: any = oneFloorList.filter(n => location?.pathname === n?.path) const nextModule: any = oneFloorList.filter(n => location?.pathname === n?.path)
let title = '' let title = ''
if (nextModule && nextModule.length > 0) { if (nextModule && nextModule.length > 0) {
title = nextModule[0].name title = nextModule[0].name

View File

@ -242,10 +242,27 @@ const UserModel: UserModelType = {
path: '/examine/record', path: '/examine/record',
name: '考核记录管理', name: '考核记录管理',
component: "@/pages/examine/record", component: "@/pages/examine/record",
} },
] {
path: '/examine/recordSummary',
name: '考核记录汇总',
component: "@/pages/examine/recordSummary",
},
]
}, },
{
path: "/setting",
redirect: '',
name: '系统设置',
children: [
{
path: '/setting/menu',
name: '菜单管理',
component: "@/pages/setting/menu/index",
},
]
}
] ]
let indexAllMenuItemByPath: any = [] let indexAllMenuItemByPath: any = []
let indexValidMenuItemByPath: any = [] let indexValidMenuItemByPath: any = []
@ -278,6 +295,8 @@ const UserModel: UserModelType = {
'/examine/modal', '/examine/modal',
'/examine/question', '/examine/question',
'/examine/record', '/examine/record',
'/setting/menu',
'/examine/recordSummary'
], ],
rootSubmenuKeys: handleGetRootSubmenuKeys(menuRes.data), rootSubmenuKeys: handleGetRootSubmenuKeys(menuRes.data),
indexAllMenuItemById: handleGetEachDatumFromNestedDataByKey(menuRes.data, 'id'), indexAllMenuItemById: handleGetEachDatumFromNestedDataByKey(menuRes.data, 'id'),

View File

@ -14,6 +14,10 @@ const authority: PageAuthority = {
'/examine/record': [ '/examine/record': [
'/examine/record', '/examine/record',
], ],
'/examine/recordSummary': [
'/examine/recordSummary',
],
'/setting/menu': ['/setting/menu']
}; };

View File

@ -0,0 +1,147 @@
import { ConnectState } from "@/models/global";
import { FormInstance, ProForm, ProFormSwitch, ProFormText, ProFormTextArea, ProFormTreeSelect } from "@ant-design/pro-components";
import { useImperativeHandle, useRef } from "react";
import { connect } from "umi";
import { handleGetAddExamineType, handleGetEditExamineType, handleGetExamineTypeTreeList } from "../service";
import { message, Modal } from "antd";
type DetailProps = {
parentRow: any
onRef: any
openAddModal: boolean
currentUser?: any
setOpenAddModal?: any
parentActionRef?: any
setParentRow?: any
afterAdd?: any // 新增完之后 需要调用的方法
}
const AddBigType = ({ parentRow, onRef, openAddModal, currentUser, setOpenAddModal, parentActionRef, setParentRow, afterAdd }: DetailProps) => {
// 弹出框的表单实例
const modalRef = useRef<FormInstance>()
useImperativeHandle(onRef, () => ({
modalRef
}));
return (
<div>
<Modal
title={`${parentRow?.id ? '编辑' : '创建'}分类`}
open={openAddModal}
destroyOnClose
onOk={() => {
modalRef.current?.validateFields().then(async (res) => {
let req = {}
let data = {}
if (parentRow?.id) {
req = {
...parentRow,
parentId: res.parentId,
name: res.name,
description: res.description,
status: res.status,
operator: currentUser?.operator
}
data = await handleGetEditExamineType(req)
} else {
req = {
parentId: res.parentId,
name: res.name,
description: res.description,
sortOrder: "1",
status: res.status,
operator: currentUser?.operator
}
data = await handleGetAddExamineType(req)
}
console.log('req', req);
console.log('data', data);
if (data.Result_Code === 100) {
modalRef.current?.resetFields()
message.success(`${parentRow?.id ? '编辑' : '新增'}成功!`)
setOpenAddModal(false)
if (parentActionRef) {
parentActionRef.current?.reload()
}
if (setParentRow) {
setParentRow(undefined)
}
if (afterAdd) {
afterAdd()
}
} else {
message.error(data.Result_Desc)
}
})
}}
onCancel={() => {
modalRef.current?.setFieldsValue({})
if (setParentRow) {
setParentRow(undefined)
}
setOpenAddModal(false)
}}
>
<ProForm formRef={modalRef} submitter={false} request={() => {
console.log('parentRow', parentRow);
return { ...parentRow }
}}>
<ProFormTreeSelect
label={"类别层级"}
name={"parentId"}
request={async () => {
const req = {}
const data = await handleGetExamineTypeTreeList(req)
console.log('dsadas', data);
let res = [
{
name: '/',
id: 0,
children: data
}
]
return res
}}
fieldProps={{
treeDefaultExpandedKeys: [0],
fieldNames: {
label: "name",
value: "id"
},
}}
rules={[
{ required: true, message: '请选择类别层级!' }
]}
/>
<ProFormText
label={"类别名称"}
name={"name"}
rules={[
{ required: true, message: '请输入类别名称!' }
]}
/>
<ProFormTextArea
label={"类别注释"}
name={"description"}
/>
<ProFormSwitch
label={"有效状态"}
name={"status"}
initialValue={parentRow?.id ? parentRow.status : true}
/>
</ProForm>
</Modal>
</div>
)
}
export default connect(({ user }: ConnectState) => ({
currentUser: user.data
}))(AddBigType);

View File

@ -7,6 +7,7 @@ import moment from "moment";
import LeftSelectTree from "@/components/leftSelectTree/leftSelectTree"; import LeftSelectTree from "@/components/leftSelectTree/leftSelectTree";
import { handleGetAddExamineType, handleGetDeleteExamineType, handleGetEditExamineType, handleGetExamineTypeList, handleGetExamineTypeTreeList } from "./service"; import { handleGetAddExamineType, handleGetDeleteExamineType, handleGetEditExamineType, handleGetExamineTypeList, handleGetExamineTypeTreeList } from "./service";
import { ProForm, ProFormSwitch, ProFormText, ProFormTextArea, ProFormTreeSelect } from "@ant-design/pro-components"; import { ProForm, ProFormSwitch, ProFormText, ProFormTextArea, ProFormTreeSelect } from "@ant-design/pro-components";
import AddBigType from "./components/addBigType";
const examineIndex: React.FC<{ currentUser: any }> = (props) => { const examineIndex: React.FC<{ currentUser: any }> = (props) => {
@ -118,10 +119,16 @@ const examineIndex: React.FC<{ currentUser: any }> = (props) => {
// 删除分类 // 删除分类
const handleDeleteType = async (id: number) => { const handleDeleteType = async (id: number) => {
const data = await handleGetDeleteExamineType(id) let req: any = {
if (data.code === 200) { id: id
message.success(data.message) }
console.log('req', req);
const data = await handleGetDeleteExamineType(req)
if (data.Result_Code === 100) {
message.success('删除成功!')
actionRef.current?.reload() actionRef.current?.reload()
} else {
message.error(data.Result_Desc)
} }
} }
@ -190,104 +197,13 @@ const examineIndex: React.FC<{ currentUser: any }> = (props) => {
</div> </div>
</div> </div>
<Modal
title={`${currentRow?.id ? '编辑' : '创建'}分类`}
open={openAddModal}
destroyOnClose
onOk={() => {
modalRef.current?.validateFields().then(async (res) => {
let req = {}
let data = {}
if (currentRow?.id) {
req = {
...currentRow,
parentId: res.parentId,
name: res.name,
description: res.description,
status: res.status,
operator: currentUser?.operator
}
data = await handleGetEditExamineType(req)
} else {
req = {
parentId: res.parentId,
name: res.name,
description: res.description,
sortOrder: "1",
status: res.status,
operator: currentUser?.operator
}
data = await handleGetAddExamineType(req)
}
console.log('req', req);
console.log('data', data);
if (data.code === 200) {
modalRef.current?.resetFields()
message.success(data.message)
setOpenAddModal(false)
actionRef.current?.reload()
setCurrentRow(undefined)
} else {
message.error(data.message)
}
})
}}
onCancel={() => {
modalRef.current?.setFieldsValue({})
setCurrentRow(undefined)
setOpenAddModal(false)
}}
>
<ProForm formRef={modalRef} submitter={false} request={() => {
console.log('currentRow', currentRow);
return { ...currentRow }
}}>
<ProFormTreeSelect
label={"类别层级"}
name={"parentId"}
request={async () => {
const req = {}
const data = await handleGetExamineTypeList(req)
console.log('dsadas', data);
let res = [
{
name: '/',
id: 0,
children: data
}
]
return res
}}
fieldProps={{
treeDefaultExpandedKeys: [0],
fieldNames: {
label: "name",
value: "id"
},
}}
rules={[
{ required: true, message: '请选择类别层级!' }
]}
/>
<ProFormText
label={"类别名称"} <AddBigType parentRow={currentRow} openAddModal={openAddModal}
name={"name"} setOpenAddModal={setOpenAddModal}
rules={[ parentActionRef={actionRef}
{ required: true, message: '请输入类别名称!' } setParentRow={setCurrentRow} />
]}
/>
<ProFormTextArea
label={"类别注释"}
name={"description"}
/>
<ProFormSwitch
label={"有效状态"}
name={"status"}
initialValue={currentRow?.id ? currentRow.status : true}
/>
</ProForm>
</Modal>
</div> </div>
) )
} }

View File

@ -1,10 +1,13 @@
import request from "@/utils/request"
// import request from "@/utils/request"
import request from "@/utils/requestJava"
// 拿到类别列表接口 // 拿到类别列表接口
export async function handleGetExamineTypeList(params?: any) { export async function handleGetExamineTypeList(params?: any) {
const data = await request.get('/question-categories', {params}) const data = await request.get('/question-categories/getList', { params })
if (data.code === 200) { if (data.Result_Code === 100) {
return data.data return data.Result_Data.List
} }
return [] return []
} }
@ -12,26 +15,26 @@ export async function handleGetExamineTypeList(params?: any) {
// 拿树形的 // 拿树形的
export async function handleGetExamineTypeTreeList(params?: any) { export async function handleGetExamineTypeTreeList(params?: any) {
const data = await request.get('/question-categories/tree', params) const data = await request.get('/question-categories/tree', params)
if (data.code === 200) { if (data.Result_Code === 100) {
return data.data return data.Result_Data.List
} }
return [] return []
} }
// 新增类别列表接口 // 新增类别列表接口
export async function handleGetAddExamineType(params: any) { export async function handleGetAddExamineType(params: any) {
const data = await request.post('/question-categories', params) const data = await request.post('/question-categories/save', params)
return data return data
} }
// 删除类别接口 // 删除类别接口
export async function handleGetDeleteExamineType(params: any) { export async function handleGetDeleteExamineType(params: any) {
const data = await request.get(`/question-categories/delete/${params}`) const data = await request.get(`/question-categories/delete`, { params })
return data return data
} }
// 编辑分类接口 // 编辑分类接口
export async function handleGetEditExamineType(params: any) { export async function handleGetEditExamineType(params: any) {
const data = await request.post(`/question-categories/${params.id}`, params) const data = await request.post(`/question-categories/update`, params)
return data return data
} }

View File

@ -1,31 +1,37 @@
import LeftSelectTree from "@/components/leftSelectTree/leftSelectTree"; import LeftSelectTree from "@/components/leftSelectTree/leftSelectTree";
import { handleGetServerpartDDL } from "@/components/leftSelectTree/service"; import { handleGetServerpartDDL } from "@/components/leftSelectTree/service";
import { ActionType, FormInstance, ProCard, ProForm, ProFormList, ProFormSelect, ProFormSwitch, ProFormText, ProFormTextArea, ProTable } from "@ant-design/pro-components"; import { ActionType, FormInstance, ProCard, ProForm, ProFormDigit, ProFormList, ProFormSelect, ProFormSwitch, ProFormText, ProFormTextArea, ProTable } from "@ant-design/pro-components";
import { Button, Col, message, Modal, Popconfirm, Row, Space, Image, Drawer } from "antd"; import { Button, Col, message, Modal, Popconfirm, Row, Space, Image, Drawer } from "antd";
import moment from "moment"; import moment from "moment";
import { useRef, useState } from "react"; import { useEffect, useRef, useState } from "react";
import { connect } from "umi"; import { connect } from "umi";
import { handleAddTemplates, handleDeleteTemplates, handleGetQuestionList, handleGetTemplatesList, handleUpdateTemplates, handleUploadFile } from "./service"; import { handleAddTemplates, handleBatchService, handleDeleteTemplates, handleGetQuestionList, handleGetTemplatesList, handleSearchModalTree, handleUpdateTemplates, handleUploadFile } from "./service";
import { handleGetExamineTypeTreeList } from "../index/service"; import { handleGetExamineTypeTreeList } from "../index/service";
import QRCode from 'qrcode'; import QRCode from 'qrcode';
import { base64ToFile } from "@/utils/publicMethods"; import { base64ToFile } from "@/utils/publicMethods";
import RecordDetail from "../record/components/recordDetail"; import RecordDetail from "../record/components/recordDetail";
import AddBigType from "../index/components/addBigType";
import AddQuestion from "../question/components/addQuestion";
import { ArrowDownOutlined, ArrowUpOutlined } from "@ant-design/icons";
const examineModal: React.FC<{ currentUser: any }> = (props) => { const examineModal: React.FC<{ currentUser: any }> = (props) => {
const { currentUser } = props const { currentUser } = props
const actionRef = useRef<ActionType>(); const actionRef = useRef<ActionType>();
const cloneActionRef = useRef<ActionType>();
const formRef = useRef<FormInstance>(); const formRef = useRef<FormInstance>();
const modalActionRef = useRef<ActionType>(); const modalActionRef = useRef<ActionType>();
const modalFormRef = useRef<FormInstance>(); const modalFormRef = useRef<FormInstance>();
const cloneFormRef = useRef<FormInstance>();
const batchCloneForm = useRef<FormInstance>();
// 弹出框的表单实例 // 弹出框的表单实例
const modalRef = useRef<FormInstance>() const modalRef = useRef<any>()
// 树相关的属性和方法 // 树相关的属性和方法
const [selectedId, setSelectedId] = useState<string>() const [selectedId, setSelectedId] = useState<string[]>()
const [collapsible, setCollapsible] = useState<boolean>(false) const [collapsible, setCollapsible] = useState<boolean>(false)
// 显示新增点位的悬浮框 // 显示新增点位的悬浮框
@ -36,6 +42,8 @@ const examineModal: React.FC<{ currentUser: any }> = (props) => {
const [currentRow, setCurrentRow] = useState<any>() const [currentRow, setCurrentRow] = useState<any>()
// 服务区的枚举 // 服务区的枚举
const [serviceObj, setServiceObj] = useState<any>() const [serviceObj, setServiceObj] = useState<any>()
// 服务区的全部数据
const [allServiceList, setAllServiceList] = useState<any>()
// 生成的二维码的初始 // 生成的二维码的初始
const [qrCodeUrl, setQrCodeUrl] = useState<string>() const [qrCodeUrl, setQrCodeUrl] = useState<string>()
// 选择问题的悬浮框 // 选择问题的悬浮框
@ -53,25 +61,64 @@ const examineModal: React.FC<{ currentUser: any }> = (props) => {
const [previewIndex, setPreviewIndex] = useState<number>(0) const [previewIndex, setPreviewIndex] = useState<number>(0)
const [columnsStateMap, setColumnsStateMap] = useState<any>({ const [columnsStateMap, setColumnsStateMap] = useState<any>({
updatedAt: { show: false }, updatedAt: { show: false },
createdAt: { show: false }, // createdAt: { show: false },
operator: { show: false }, operator: { show: false },
}) })
// 显示新增大类的抽屉
const [openAddBigType, setOpenAddBigType] = useState<boolean>(false)
// 显示 新增问题的抽屉
const [openAddQuestion, setOpenAddQuestion] = useState<boolean>(false)
// 拿到protable的问题分类
const [bigTypeList, setBigTypeList] = useState<any>()
// 显示克隆悬浮框
const [showCloneModal, handleShowCloneModal] = useState<boolean>(false)
// 克隆的选择内容
const [selectedCloneId, setSelectedCloneId] = useState<any>()
const [selectedCloneDetail, setSelectedCloneDetail] = useState<any>()
// 默认展开行的id
const [defaultExpandRow, setDefaultExpandRow] = useState<any>()
// 点位克隆的悬浮框
const [showBatchCloneModal, setshowBatchCloneModal] = useState<boolean>(false)
// 批量克隆的loading效果
const [batchLoading, setBatchLoading] = useState<boolean>(false)
const columns: any = [ const columns: any = [
// {
// title: "巡查类型",
// dataIndex: "status",
// hideInTable: true,
// valueType: "select",
// valueEnum: {
// "1": '异常',
// "0": "正常"
// }
// },
// {
// title: "搜索内容",
// dataIndex: "anySearchParams",
// hideInTable: true,
// fieldProps: {
// }
// },
{ {
title: <div style={{ textAlign: 'center' }}></div>, title: <div style={{ textAlign: 'center' }}></div>,
dataIndex: "serverPartName", dataIndex: "serverPartName",
hideInSearch: true, hideInSearch: true,
width: 150,
ellipsis: true
},
{
title: <div style={{ textAlign: 'center' }}></div>,
dataIndex: "title",
hideInSearch: true,
width: 200, width: 200,
ellipsis: true ellipsis: true,
render: (_, record) => {
return record?.type === 'district' || record?.type === 'servicePart' ? record?.name :
record?.title
}
}, },
// {
// title: <div style={{ textAlign: 'center' }}>点位名称</div>,
// dataIndex: "title",
// hideInSearch: true,
// width: 200,
// ellipsis: true
// },
{ {
title: <div style={{ textAlign: 'center' }}></div>, title: <div style={{ textAlign: 'center' }}></div>,
dataIndex: "questions", dataIndex: "questions",
@ -85,10 +132,12 @@ const examineModal: React.FC<{ currentUser: any }> = (props) => {
let options: string = '' let options: string = ''
if (item.question.options && item.question.options.length > 0) { if (item.question.options && item.question.options.length > 0) {
item.question.options.forEach((subItem: any, subIndex: number) => { item.question.options.forEach((subItem: any, subIndex: number) => {
options += `${subIndex > 0 ? '' : ''}选项${subIndex + 1}${subItem.text}` options += `${subItem.text}`
// ${subIndex > 0 ? '' : ''} 选项${subIndex + 1}
}) })
} }
questionStr += `${index > 0 ? '' : ''}问题${index + 1}${item.question.title}${options}` questionStr += `${index > 0 ? '' : ''}问题:${item.question.title}${options}`
// ${index + 1}
}) })
} }
return questionStr || '' return questionStr || ''
@ -117,7 +166,7 @@ const examineModal: React.FC<{ currentUser: any }> = (props) => {
width: 100, width: 100,
ellipsis: true, ellipsis: true,
render: (_, record) => { render: (_, record) => {
return record?.status ? '有效' : '无效' return record?.type === 'district' || record?.type === 'servicePart' ? '' : record?.status ? '有效' : '无效'
} }
}, },
{ {
@ -163,18 +212,18 @@ const examineModal: React.FC<{ currentUser: any }> = (props) => {
align: 'center', align: 'center',
hideInSearch: true, hideInSearch: true,
fixed: "right", fixed: "right",
width: 120, width: 150,
render: (_: any, record: any) => { render: (_: any, record: any) => {
return <Space> return record?.type === 'district' || record?.type === 'servicePart' ? '' : <Space>
<a onClick={() => { <a onClick={() => {
console.log('record', record); console.log('currentRow', record);
setCurrentRow(record) setCurrentRow(record)
setShowPlaceModal(true) setShowPlaceModal(true)
}}></a> }}></a>
<a onClick={() => { {/* <a onClick={() => {
setCurrentRow(record) setCurrentRow(record)
handleShowDetail(true) handleShowDetail(true)
}}></a> }}></a> */}
<Popconfirm <Popconfirm
title={"确认删除?"} title={"确认删除?"}
onConfirm={async () => { onConfirm={async () => {
@ -183,6 +232,13 @@ const examineModal: React.FC<{ currentUser: any }> = (props) => {
> >
<a></a> <a></a>
</Popconfirm> </Popconfirm>
<a onClick={() => {
console.log('currentRow', record);
setCurrentRow(record)
setshowBatchCloneModal(true)
}}>
</a>
</Space> </Space>
} }
@ -196,25 +252,26 @@ const examineModal: React.FC<{ currentUser: any }> = (props) => {
dataIndex: "id", dataIndex: "id",
hideInTable: true, hideInTable: true,
valueType: 'treeSelect', valueType: 'treeSelect',
request: async () => { // request: async () => {
const data = await handleGetExamineTypeTreeList() // const data = await handleGetExamineTypeTreeList()
console.log('data', data); // console.log('data', data);
return data && data.length > 0 ? data.map(item => ({ // return data && data.length > 0 ? data.map(item => ({
title: item.name, // title: item.name,
value: item.id, // value: item.id,
children: item.children?.map((child: any) => ({ // children: item.children?.map((child: any) => ({
title: child.name, // title: child.name,
value: child.id, // value: child.id,
children: child.children || [] // children: child.children || []
})) || [] // })) || []
})) : [] // })) : []
}, // },
fieldProps: { fieldProps: {
multiple: false, multiple: false,
treeDefaultExpandAll: true, treeDefaultExpandAll: true,
showSearch: true, showSearch: true,
treeNodeFilterProp: 'title', treeNodeFilterProp: 'title',
placeholder: '请选择问题分类' placeholder: '请选择问题分类',
options: bigTypeList
} }
}, },
{ {
@ -246,19 +303,45 @@ const examineModal: React.FC<{ currentUser: any }> = (props) => {
// 删除模版 // 删除模版
const deleteQuestion = async (id: number) => { const deleteQuestion = async (id: number) => {
const data = await handleDeleteTemplates({ id: id }) const data = await handleDeleteTemplates({ id: id })
if (data.code === 200) { if (data.Result_Code === 100) {
message.success(data.message) message.success(data.Result_Desc)
actionRef.current?.reload() actionRef.current?.reload()
} }
} }
// 新增完类型之后的刷新
const handleReloadSearchList = async () => {
modalActionRef.current?.reload();
fetchTreeData()
}
const fetchTreeData = async () => {
const data = await handleGetExamineTypeTreeList();
const formattedData = data?.length ? data.map(item => ({
title: item.name,
value: item.id,
children: item.children?.map(child => ({
title: child.name,
value: child.id,
children: child.children || []
})) || []
})) : [];
console.log('formattedData', formattedData);
setBigTypeList(formattedData);
};
useEffect(() => {
fetchTreeData()
}, [])
return ( return (
<div style={{ backgroundColor: '#fff', display: 'flex' }}> <div style={{ backgroundColor: '#fff', display: 'flex' }}>
{/* <LeftSelectTree setSelectedId={setSelectedId} setCollapsible={setCollapsible} collapsible={collapsible} currentUser={currentUser} /> */} <LeftSelectTree setSelectedId={setSelectedId} setCollapsible={setCollapsible} collapsible={collapsible} currentUser={currentUser} />
<div style={{ <div style={{
// width: !collapsible ? 'calc(100% - 300px)' : 'calc(100% - 60px)', width: !collapsible ? 'calc(100% - 300px)' : 'calc(100% - 60px)',
width: "100%", // width: "100%",
paddingTop: 0, paddingTop: 0,
paddingBottom: 0, paddingBottom: 0,
paddingRight: 0 paddingRight: 0
@ -271,14 +354,24 @@ const examineModal: React.FC<{ currentUser: any }> = (props) => {
expandable={{ expandable={{
expandRowByClick: true expandRowByClick: true
}} }}
rowKey={(record) => {
return `${record?.id}`
}}
scroll={{ x: "100%", y: 'calc(100vh - 400px)' }} scroll={{ x: "100%", y: 'calc(100vh - 400px)' }}
headerTitle={<span style={{ color: "#1890ff", fontSize: 14, fontWeight: 600 }}></span>} headerTitle={<span style={{ color: "#1890ff", fontSize: 14, fontWeight: 600 }}></span>}
search={{ span: 6 }} search={{ span: 6 }}
request={async () => { request={async () => {
const req: any = { console.log('selectedId', selectedId);
if (!(selectedId && selectedId.length > 0)) {
return
} }
const data = await handleGetTemplatesList()
const req: any = {
serverPartIds: selectedId && selectedId.length > 0 ? selectedId : [],
}
const data = await handleSearchModalTree(req)
if (data && data.length > 0) { if (data && data.length > 0) {
return { data, success: true } return { data, success: true }
} }
@ -332,6 +425,7 @@ const examineModal: React.FC<{ currentUser: any }> = (props) => {
modalRef.current?.validateFields().then(async (res) => { modalRef.current?.validateFields().then(async (res) => {
console.log('res', res); console.log('res', res);
console.log('currentRow', currentRow); console.log('currentRow', currentRow);
console.log('selectedQuestionDetail', selectedQuestionDetail);
let req = {} let req = {}
let data = {} let data = {}
if (currentRow?.id) { if (currentRow?.id) {
@ -346,32 +440,43 @@ const examineModal: React.FC<{ currentUser: any }> = (props) => {
}) })
} else { } else {
// 判断是否有改动 一样 说明没改动 那么把老的拿进去就行 // 判断是否有改动 一样 说明没改动 那么把老的拿进去就行
if (currentRow?.questionnaireTemplateQuestions.length === res.questions.length) { // if (currentRow?.questionnaireTemplateQuestions.length === res.questions.length) {
if (currentRow?.questionnaireTemplateQuestions && currentRow?.questionnaireTemplateQuestions.length > 0) { // if (currentRow?.questionnaireTemplateQuestions && currentRow?.questionnaireTemplateQuestions.length > 0) {
currentRow?.questionnaireTemplateQuestions.forEach((item) => { // currentRow?.questionnaireTemplateQuestions.forEach((item) => {
questions.push({ // questions.push({
questionId: item.question.id, // questionId: item.question.id,
isRequired: item.question.required, // isRequired: item.question.required,
sortOrder: item.question.sortOrder // sortOrder: item.question.sortOrder
}) // })
// })
// }
// } else {
// // 不一样 说明有删除 不然走的是第一个
// if (res.questions && res.questions.length > 0) {
// res.questions.forEach((item) => {
// questions.push({
// questionId: item.id,
// isRequired: item.required,
// sortOrder: item.sortOrder
// })
// })
// }
// }
if (res.questions && res.questions.length > 0) {
res.questions.forEach((item: any) => {
questions.push({
questionId: item.questionId,
isRequired: item.isRequired,
sortOrder: item.sortOrder
}) })
} })
} else {
// 不一样 说明有删除 不然走的是第一个
if (res.questions && res.questions.length > 0) {
res.questions.forEach((item) => {
questions.push({
questionId: item.id,
isRequired: item.required,
sortOrder: item.sortOrder
})
})
}
} }
} }
req = { req = {
...currentRow, ...currentRow,
deadlineType: res.deadlineType,
deadlineFrequency: res.deadlineFrequency,
title: res.title, title: res.title,
serverPartName: serviceObj ? serviceObj[res.serverPartId] : "", serverPartName: serviceObj ? serviceObj[res.serverPartId] : "",
serverPartId: res.serverPartId, serverPartId: res.serverPartId,
@ -380,58 +485,64 @@ const examineModal: React.FC<{ currentUser: any }> = (props) => {
status: res.status, status: res.status,
description: res.description description: res.description
} }
console.log('req', req);
data = await handleUpdateTemplates(req) data = await handleUpdateTemplates(req)
} else { } else {
let questions: any = [] let questions: any = []
if (selectedQuestionDetail && selectedQuestionDetail.length > 0) { if (selectedQuestionDetail && selectedQuestionDetail.length > 0) {
selectedQuestionDetail.forEach((item) => { selectedQuestionDetail.forEach((item: any, index: number) => {
questions.push({ questions.push({
questionId: item.id, questionId: item.id,
isRequired: item.required, isRequired: item.required,
sortOrder: item.sortOrder sortOrder: index + 1
}) })
}) })
} }
req = { req = {
title: res.title, title: res.title,
deadlineType: res.deadlineType,
deadlineFrequency: res.deadlineFrequency,
placeName: "", placeName: "",
placeId: 0, placeId: 0,
serverPartName: serviceObj ? serviceObj[res.serverPartId] : "", serverPartName: serviceObj ? serviceObj[res.serverPartId] : "",
serverPartId: res.serverPartId, serverPartId: res.serverPartId,
// qrUrl: "", // qrUrl: "",
questions: questions && questions.length > 0 ? questions : "", questions: questions && questions.length > 0 ? questions : [],
operator: currentUser?.operator, operator: currentUser?.operator,
status: res.status, status: res.status,
description: res.description description: res.description
} }
console.log('reqreqreqreqreq', req);
data = await handleAddTemplates(req) data = await handleAddTemplates(req)
console.log('datadsadsa1', data.data); console.log('datadsadsa1', data);
const myQRCodeDataUrl = await QRCode.toDataURL(`pages/walkAroundManager/index?id=${data.data.id}`);
const file = base64ToFile(myQRCodeDataUrl, `wenjuan${data.data.id}.png`); const myQRCodeDataUrl = await QRCode.toDataURL(`pages/walkAroundManager/index?id=${data.Result_Data.id}`);
const file = base64ToFile(myQRCodeDataUrl, `wenjuan${data.Result_Data.id}.png`);
console.log('file', file); console.log('file', file);
const formData = new FormData(); const formData = new FormData();
formData.append("file", file, `wenjuan${data.data.id}.png`); // 确保文件名也传递 formData.append("file", file, `wenjuan${data.Result_Data.id}.png`); // 确保文件名也传递
const fileData = await handleUploadFile(formData) const fileData = await handleUploadFile(formData)
console.log('fileData', fileData); console.log('fileData', fileData);
let imgUrl: string = `https://es.robot-z.cn/${fileData.data.path}` let imgUrl: string = `https://es.eshangtech.com/${fileData.data.path}`
await handleUpdateTemplates({ await handleUpdateTemplates({
...data.data, ...data.Result_Data,
qrUrl: imgUrl qrUrl: imgUrl
}) })
setQrCodeUrl(imgUrl) setQrCodeUrl(imgUrl)
// setQrCodeUrl(myQRCodeDataUrl) // setQrCodeUrl(myQRCodeDataUrl)
} }
console.log('datadsadsa', data); console.log('datadsadsa', data);
if (data.code === 200) { if (data.Result_Code === 100) {
modalRef.current?.resetFields() modalRef.current?.resetFields()
message.success(data.message) message.success('新增成功!')
setShowPlaceModal(false) setShowPlaceModal(false)
actionRef.current?.reload() actionRef.current?.reload()
setCurrentRow(undefined) setCurrentRow(undefined)
} else { } else {
message.error(data.message) message.error(data.Result_Desc)
} }
}) })
@ -443,37 +554,58 @@ const examineModal: React.FC<{ currentUser: any }> = (props) => {
setQrCodeUrl(undefined) setQrCodeUrl(undefined)
}} }}
> >
<ProForm formRef={modalRef} submitter={false} request={() => { <ProForm formRef={modalRef} submitter={false} request={async () => {
console.log('currentRow', currentRow); console.log('currentRow', currentRow);
let questionsList: any = [] let questionsList: any = []
let keyList: any = [] let keyList: any = []
if (currentRow?.questionnaireTemplateQuestions && currentRow?.questionnaireTemplateQuestions.length > 0) { if (currentRow?.questionnaireTemplateQuestions && currentRow?.questionnaireTemplateQuestions.length > 0) {
console.log('1'); currentRow?.questionnaireTemplateQuestions.forEach((item: any) => {
currentRow?.questionnaireTemplateQuestions.forEach((item) => {
console.log('2');
if (item.question) { if (item.question) {
let obj = JSON.parse(JSON.stringify(item.question)) let obj = JSON.parse(JSON.stringify(item.question))
let str: string = '' // let str: string = ''
if (obj.options && obj?.options.length > 0) { // if (obj.options && obj?.options.length > 0) {
obj.options.forEach((subItem: any, index: number) => { // obj.options.forEach((subItem: any, index: number) => {
str += `${index > 0 ? '' : ''}选项${index + 1}${subItem.text}` // str += `${index > 0 ? '' : ''}选项${index + 1}${subItem.text}`
// })
// }
// obj.text = obj.title
// obj.mark = JSON.stringify(obj.options)
// obj.showText = str
keyList.push(obj.id)
// questionsList.push(obj)
let markStr: string = JSON.stringify(item.question.options)
let showText: string = ''
if (item.question.options && item.question.options.length > 0) {
item.question.options.forEach((subItem: any, subIndex: number) => {
if (showText) {
showText += `,选项${subIndex + 1}${subItem.text}`
} else {
showText = `选项${subIndex + 1}${subItem.text}`
}
}) })
} }
obj.text = obj.title questionsList.push({
obj.mark = JSON.stringify(obj.options) questionId: item.questionId,
obj.showText = str isRequired: item.isRequired,
keyList.push(obj.id) sortOrder: item.sortOrder,
questionsList.push(obj) text: item.question.title,
mark: markStr,
showText: showText
})
} }
}) })
} }
// setSelectedQuestionDetail
setSelectedQuestionId(keyList) setSelectedQuestionId(keyList)
console.log('questionsList', questionsList);
return { return {
...currentRow, ...currentRow,
serverPartId: currentRow.serverPartId.toString(), serverPartId: currentRow.serverPartId.toString(),
@ -497,6 +629,7 @@ const examineModal: React.FC<{ currentUser: any }> = (props) => {
obj[item.value] = item.label obj[item.value] = item.label
}) })
} }
setAllServiceList(data)
setServiceObj(obj) setServiceObj(obj)
return data return data
}} }}
@ -520,13 +653,38 @@ const examineModal: React.FC<{ currentUser: any }> = (props) => {
}]} }]}
/> />
</Col> </Col>
<Col span={8}> <Col span={4}>
<ProFormSwitch <ProFormSwitch
label={"有效状态"} label={"有效状态"}
name={"status"} name={"status"}
initialValue={currentRow?.id ? currentRow.status : true} initialValue={currentRow?.id ? currentRow.status : true}
/> />
</Col> </Col>
<Col span={4}>
<Button type="primary" onClick={() => {
handleShowCloneModal(true)
}}></Button>
</Col>
<Col span={8}>
<ProFormSelect
label={"任务时限类型"}
name={"deadlineType"}
options={[
{ label: "每日任务", value: 1 },
{ label: "每月任务", value: 2 }
]}
/>
</Col>
<Col span={8}>
<ProFormDigit
label={"巡查频次"}
name={"deadlineFrequency"}
initialValue={1}
/>
</Col>
<Col span={8}></Col>
<Col span={20}> <Col span={20}>
<ProFormList <ProFormList
name="questions" name="questions"
@ -535,20 +693,90 @@ const examineModal: React.FC<{ currentUser: any }> = (props) => {
creatorButtonProps={{ creatorButtonProps={{
position: 'bottom', position: 'bottom',
creatorButtonText: '添加选项', creatorButtonText: '添加选项',
style: { display: 'none' }, style: { display: 'none' }
onClick: (e) => {
console.log('e', e);
}
}} }}
copyIconProps={false} copyIconProps={false}
style={{ width: '100%' }} style={{ width: '100%' }}
itemContainerStyle={{ width: '100%' }} itemContainerStyle={{ width: '100%' }}
itemRender={({ listDom, action }, { record, index }) => ( itemRender={({ listDom, action }, { record, index, move }) => {
<div style={{ width: '100%', display: 'flex', alignItems: 'flex-start', marginBottom: '10px' }}> // console.log('modalRef', modalRef);
<div style={{ flex: 1, width: '100%' }}>{listDom}</div> // let list: any = modalRef?.current?.getFieldValue("questions") || []
<div style={{ marginLeft: '8px', marginTop: '30px' }}>{action}</div> // console.log('newidas2', list);
</div> return (
)} <div style={{
width: '100%',
display: 'flex',
alignItems: 'flex-start',
marginBottom: '10px',
padding: '12px',
backgroundColor: '#fafafa',
borderRadius: '4px'
}}>
<div style={{ flex: 1, width: '100%' }}>{listDom}</div>
<div style={{
marginLeft: '8px',
display: 'flex',
gap: '8px'
}}>
{/* 上移按钮 */}
<Button
icon={<ArrowUpOutlined />}
disabled={index === 0}
onClick={() => {
const currentList = modalRef?.current?.getFieldValue("questions") || []
console.log('currentList', currentList);
if (index > 0) {
// 交换位置
[currentList[index], currentList[index - 1]] =
[currentList[index - 1], currentList[index]];
// 更新 sortOrder
currentList.forEach((item, i) => {
item.sortOrder = i + 1;
});
console.log('currentList', currentList);
modalRef.current?.setFieldsValue({
questions: currentList
});
}
}}
/>
{/* 下移按钮 */}
<Button
icon={<ArrowDownOutlined />}
disabled={index === currentRow?.questionnaireTemplateQuestions.length - 1}
onClick={() => {
const currentList = modalRef?.current?.getFieldValue("questions") || []
if (index < currentList.length - 1) {
// 交换位置
[currentList[index], currentList[index + 1]] =
[currentList[index + 1], currentList[index]];
// 更新 sortOrder
currentList.forEach((item, i) => {
item.sortOrder = i + 1;
});
modalRef.current?.setFieldsValue({
questions: currentList
});
}
}}
/>
{/* 删除按钮(保留原有) */}
{action}
</div>
</div>
);
}}
// itemRender={({ listDom, action }, { record, index }) => (
// <div style={{ width: '100%', display: 'flex', alignItems: 'flex-start', marginBottom: '10px' }}>
// <div style={{ flex: 1, width: '100%' }}>{listDom}</div>
// <div style={{ marginLeft: '8px', marginTop: '30px' }}>{action}</div>
// </div>
// )}
> >
<Row gutter={6} style={{ width: '100%', margin: 0 }}> <Row gutter={6} style={{ width: '100%', margin: 0 }}>
<Col span={8}> <Col span={8}>
@ -603,6 +831,7 @@ const examineModal: React.FC<{ currentUser: any }> = (props) => {
width={1400} width={1400}
title={`点位`} title={`点位`}
open={selectQuestionModal} open={selectQuestionModal}
destroyOnClose destroyOnClose
onOk={() => { onOk={() => {
console.log('selectedQuestionDetail', selectedQuestionDetail); console.log('selectedQuestionDetail', selectedQuestionDetail);
@ -619,6 +848,8 @@ const examineModal: React.FC<{ currentUser: any }> = (props) => {
showQuestion.push({ text: item.title, showText: str, mark: item.options ? JSON.stringify(item.options) : "" }) showQuestion.push({ text: item.title, showText: str, mark: item.options ? JSON.stringify(item.options) : "" })
}) })
} }
console.log('showQuestion', showQuestion);
modalRef.current?.setFieldsValue({ modalRef.current?.setFieldsValue({
questions: showQuestion questions: showQuestion
}) })
@ -632,7 +863,9 @@ const examineModal: React.FC<{ currentUser: any }> = (props) => {
actionRef={modalActionRef} actionRef={modalActionRef}
formRef={modalFormRef} formRef={modalFormRef}
columns={modalColumns} columns={modalColumns}
rowKey={"id"} rowKey={(record) => {
return `${record?.id}`
}}
scroll={{ y: 'calc(100vh - 500px)' }} scroll={{ y: 'calc(100vh - 500px)' }}
request={async (params) => { request={async (params) => {
console.log('查询参数:', params); console.log('查询参数:', params);
@ -654,11 +887,184 @@ const examineModal: React.FC<{ currentUser: any }> = (props) => {
setSelectedQuestionId(selectedRowKeys) setSelectedQuestionId(selectedRowKeys)
} }
}} }}
toolbar={{
actions: [
<Button type="primary" onClick={(e) => {
setOpenAddBigType(true)
}}>
</Button>,
<Button type="primary" onClick={(e) => {
setOpenAddQuestion(true)
}}>
</Button>
]
}}
/>
</Modal>
<Modal
width={1500}
title={'克隆点位'}
open={showCloneModal}
destroyOnClose
onOk={() => {
console.log('selectedCloneId', selectedCloneId);
console.log('selectedCloneDetail', selectedCloneDetail);
if (selectedCloneDetail && selectedCloneDetail.length > 0) {
let res: any = []
let detailList: any = []
let idList: any = []
let list: any = selectedCloneDetail[0].questionnaireTemplateQuestions
if (list && list.length > 0) {
list.forEach((item: any) => {
detailList.push(item)
idList.push(item.question.id.toString())
if (item.question) {
let obj = JSON.parse(JSON.stringify(item.question))
let str: string = ''
if (obj.options && obj?.options.length > 0) {
obj.options.forEach((subItem: any, index: number) => {
str += `${index > 0 ? '' : ''}选项${index + 1}${subItem.text}`
})
}
obj.text = obj.title
obj.mark = JSON.stringify(obj.options)
obj.showText = str
res.push(obj)
}
})
}
setSelectedQuestionDetail(detailList)
console.log('idList', idList);
setSelectedQuestionId(idList)
modalRef.current?.setFieldsValue({
questions: res
})
handleShowCloneModal(false)
} else {
handleShowCloneModal(false)
}
}}
onCancel={() => {
handleShowCloneModal(false)
}}
>
<ProTable
actionRef={cloneActionRef}
formRef={cloneFormRef}
rowKey={(record) => {
return `${record?.id}`
}}
columns={[
{
title: <div style={{ textAlign: 'center' }}></div>,
dataIndex: "serverPartName",
// hideInSearch: true,
width: 200,
ellipsis: true,
render: (_, record) => {
return record?.type === 'district' || record?.type === 'servicePart' ? record?.name :
record?.title
}
},
{
title: <div style={{ textAlign: 'center' }}></div>,
dataIndex: "questions",
hideInSearch: true,
width: 400,
ellipsis: true,
render: (_, record) => {
let questionStr: string = ""
if (record?.questionnaireTemplateQuestions && record?.questionnaireTemplateQuestions.length > 0) {
record?.questionnaireTemplateQuestions.forEach((item: any, index: number) => {
let options: string = ''
if (item.question.options && item.question.options.length > 0) {
item.question.options.forEach((subItem: any, subIndex: number) => {
options += `${subItem.text}`
// ${subIndex > 0 ? '' : ''} 选项${subIndex + 1}
})
}
questionStr += `${index > 0 ? '' : ''}问题:${item.question.title}${options}`
// ${index + 1}
})
}
return questionStr || ''
// return record?.questions && record?.questions.length > 0 ? JSON.stringify(record?.questions) : ''
}
},
]}
request={async (params) => {
let selectId: string[] = []
console.log('cloneFormRef', cloneFormRef);
console.log('params', params);
if (allServiceList && allServiceList.length > 0) {
allServiceList.forEach((item: any) => {
selectId.push(item.value)
})
}
console.log('selectId', selectId);
const req: any = {
serverPartIds: selectId,
any: params?.serverPartName ? [
{
key: "serverPartName",
value: params?.serverPartName
}
] : undefined
}
const data = await handleSearchModalTree(req)
console.log('dasdsa', data);
let defaultRow: any = []
if (data && data.length > 0) {
data.forEach((item: any) => {
defaultRow.push(item.id.toString())
if (item.children && item.children.length > 0) {
item.children.forEach((subItem: any) => {
defaultRow.push(subItem.id.toString())
})
}
})
console.log('defaultRow', defaultRow);
setDefaultExpandRow(defaultRow)
return { data, success: true }
}
setDefaultExpandRow(defaultRow)
return { data: [], success: true }
}}
expandable={{
expandedRowKeys: defaultExpandRow,
defaultExpandAllRows: true
}}
scroll={{ x: '100%', y: '500px' }}
rowSelection={{
type: "radio",
defaultSelectedRowKeys: selectedCloneId,
getCheckboxProps: (record) => ({
disabled: record.type === "district" || record.type === "servicePart",
}),
onChange: (selectedRowKeys, selectedRows) => {
setSelectedCloneDetail(selectedRows)
setSelectedCloneId(selectedRowKeys)
}
}}
/> />
</Modal> </Modal>
</div> </div>
<Drawer <Drawer
title={false} title={false}
closeIcon={false} closeIcon={false}
@ -671,6 +1077,121 @@ const examineModal: React.FC<{ currentUser: any }> = (props) => {
> >
<RecordDetail parentRow={currentRow} show={showDetail} detailType={'modal'} /> <RecordDetail parentRow={currentRow} show={showDetail} detailType={'modal'} />
</Drawer> </Drawer>
<AddBigType openAddModal={openAddBigType} setOpenAddModal={setOpenAddBigType} afterAdd={handleReloadSearchList} />
<AddQuestion showQuestionModal={openAddQuestion} setShowQuestionModal={setOpenAddQuestion} afterAdd={handleReloadSearchList} />
{/* 批量克隆点位 */}
<Modal
width={400}
title={"批量克隆"}
open={showBatchCloneModal}
confirmLoading={batchLoading}
onOk={() => {
batchCloneForm.current?.validateFields().then(async (res: any) => {
console.log('res', res);
console.log('currentRow', currentRow);
console.log('currentUser', currentUser);
console.log('serviceObj', serviceObj);
let list: any = res.serviceList
let serverParts: any = []
if (list && list.length > 0) {
list.forEach((item: any) => {
serverParts.push({
serverPartName: serviceObj[item],
serverPartId: item
})
})
}
let question: any = []
if (currentRow?.questionnaireTemplateQuestions && currentRow?.questionnaireTemplateQuestions.length > 0) {
currentRow.questionnaireTemplateQuestions.forEach((item: any) => {
question.push({
questionId: item.questionId,
isRequired: item.isRequired,
sortOrder: item.sortOrder
})
})
}
let req: any = {
title: currentRow?.title,
placeName: currentRow?.placeName,
placeId: currentRow?.placeId,
serverParts: serverParts,
questions: question,
operator: currentUser?.operator,
status: currentRow?.status,
description: currentRow?.description
}
console.log('req', req);
const data = await handleBatchService(req)
console.log('datadatadatadata', data);
if (data.Result_Code === 100) {
message.success('批量克隆成功!')
actionRef.current?.reload()
batchCloneForm.current?.resetFields()
setshowBatchCloneModal(false)
setCurrentRow(undefined)
} else {
message.error(data.Result_Desc)
}
})
// setshowBatchCloneModal(false)
}}
onCancel={() => {
setshowBatchCloneModal(false)
batchCloneForm.current?.resetFields()
}}
>
<ProForm
formRef={batchCloneForm}
submitter={false}
>
<Row>
<Col span={24}>
<ProFormSelect
label={"服务区"}
name={"serviceList"}
fieldProps={{
mode: "multiple",
showSearch: true,
filterOption: (input, option) => {
return (option?.label ?? '').toLowerCase().includes(input.toLowerCase());
},
optionFilterProp: "label"
}}
request={async () => {
const req = {
ProvinceCode: currentUser?.provinceCode,
StatisticsType: 1000
}
const data = await handleGetServerpartDDL(req)
let obj: any = {}
if (data && data.length > 0) {
data.forEach((item: any) => {
obj[item.value] = item.label
})
}
setAllServiceList(data)
setServiceObj(obj)
return data
}}
rules={[
{
required: true,
message: "请选择目标服务区"
}
]}
/>
</Col>
</Row>
</ProForm>
</Modal>
</div > </div >
) )
} }

View File

@ -1,45 +1,61 @@
import requestOld from "@/utils/requestOld" import requestOld from "@/utils/requestOld"
import request from "@/utils/request" // import request from "@/utils/request"
import requestNode from "@/utils/request"
import request from "@/utils/requestJava"
// 拿到模版id列表 去绑定服务区的多个站点信息 的列表接口 // 拿到模版id列表 去绑定服务区的多个站点信息 的列表接口
export async function handleGetTemplatesList(params?: any) { export async function handleGetTemplatesList(params?: any) {
const data = await request.get('/questionnaire-templates', {params}) const data = await request.get('/questionnaire-templates', { params })
if (data.code === 200) { if (data.Result_Code === 200) {
return data.data return data.Result_Data.List
} }
return [] return []
} }
// 新建模版 // 新建模版
export async function handleAddTemplates(params?: any) { export async function handleAddTemplates(params?: any) {
const data = await request.post('/questionnaire-templates', params) const data = await request.post('/questionnaire-templates/save', params)
return data return data
} }
// 更新模版 // 更新模版
export async function handleUpdateTemplates(params?: any) { export async function handleUpdateTemplates(params?: any) {
const data = await request.post(`/questionnaire-templates/${params.id}`, params) const data = await request.post(`/questionnaire-templates/update`, params)
return data return data
} }
// 删除模版 // 删除模版
export async function handleDeleteTemplates(params?: any) { export async function handleDeleteTemplates(params?: any) {
const data = await request.get(`/questionnaire-templates/delete/${params.id}`) const data = await request.get(`/questionnaire-templates/delete`, { params })
return data return data
} }
export async function handleGetQuestionList(params?: any) { export async function handleGetQuestionList(params?: any) {
const data = await request.get(`/questions?categoryId=${params.categoryId}`,) const data = await request.get('/questions/getList', params)
if (data.code === 200) { if (data.Result_Code === 100) {
return data.data return data.Result_Data.List
} }
return [] return []
} }
// 上传图片的oss接口 // 上传图片的oss接口
export async function handleUploadFile(params?: any) { export async function handleUploadFile(params?: any) {
const data = await request.post(`/oss/upload`, params) const data = await requestNode.post(`/oss/upload`, params)
return data
}
// 模版树的查询
export async function handleSearchModalTree(params?: any) {
const data = await request.post(`/questionnaire-templates/search/tree`, params)
if (data.Result_Code === 100) {
return data.Result_Data.List
}
return []
}
// 批量克隆服务区
export async function handleBatchService(params?: any) {
const data = await request.post(`/questionnaire-templates/batchSave`, params)
return data return data
} }

View File

@ -0,0 +1,252 @@
import { ConnectState } from "@/models/global";
import { FormInstance, ProForm, ProFormDigit, ProFormList, ProFormRadio, ProFormSwitch, ProFormText, ProFormTextArea, ProFormTreeSelect } from "@ant-design/pro-components";
import { useImperativeHandle, useRef, useState } from "react";
import { connect } from "umi";
import { Col, message, Modal, Row } from "antd";
import { handleGetExamineTypeList, handleGetExamineTypeTreeList } from "../../index/service";
import { handleAddQuestion, handleEditQuestion } from "../service";
type DetailProps = {
parentRow: any
onRef: any
currentUser?: any
showQuestionModal: boolean
setShowQuestionModal?: any
parentActionRef?: any
setCurrentRow?: any
afterAdd?: any
}
const addQuestion = ({ parentRow, onRef, currentUser, showQuestionModal, setShowQuestionModal, parentActionRef, setCurrentRow, afterAdd }: DetailProps) => {
// 弹出框的表单实例
const modalRef = useRef<FormInstance>()
const [categoryIdObj, setCategoryIdObj] = useState<any>()
useImperativeHandle(onRef, () => ({
modalRef,
categoryIdObj
}));
return (
<div>
<Modal
width={800}
title={`${parentRow?.id ? '编辑' : '创建'}问题`}
open={showQuestionModal}
destroyOnClose
onOk={() => {
modalRef.current?.validateFields().then(async (res) => {
let req = {}
let data = {}
if (parentRow?.id) {
req = {
...parentRow,
categoryId: res.categoryId,// 题目id
type: `${res.selectType === 1 ? 'radio' : 'checked'}_choice`,// 题目类型
title: res.title,// 问题内容
required: res.required,// 是否必填
options: res.options,
status: res.status,
operator: currentUser?.operator,
description: `${res.categoryId && categoryIdObj ? categoryIdObj[res.categoryId] : ''}`,
category: undefined
}
console.log('req', req);
data = await handleEditQuestion(req)
} else {
req = {
categoryId: res.categoryId,// 题目id
type: `${res.selectType === 1 ? 'radio' : 'checked'}_choice`,// 题目类型
title: res.title,// 问题内容
required: res.required,// 是否必填
options: res.options,
sortOrder: 1,
status: res.status,
operator: currentUser?.operator,
defaultScore: 0,
images: [],
description: `${res.categoryId && categoryIdObj ? categoryIdObj[res.categoryId] : ''}`
}
data = await handleAddQuestion(req)
}
console.log('datadsadsa', data);
if (data.Result_Code === 100) {
if (modalRef) {
modalRef.current?.resetFields()
}
message.success(data.Result_Desc)
setShowQuestionModal(false)
if (parentActionRef) {
parentActionRef.current?.reload()
}
if (setCurrentRow) {
setCurrentRow(undefined)
}
if (afterAdd) {
afterAdd()
}
} else {
message.error(data.Result_Desc)
}
})
}}
onCancel={() => {
if (modalRef) {
modalRef.current?.resetFields()
}
setShowQuestionModal(false)
if (setCurrentRow) {
setCurrentRow(undefined)
}
}}
>
<ProForm formRef={modalRef} submitter={false} request={() => {
console.log('currentRow', parentRow);
return {
...parentRow,
selectType: parentRow.type.split('_')[0] === 'checked' ? 2 : 1
}
}}>
<Row gutter={8}>
<Col span={12}>
<ProFormTreeSelect
label={"问题分类"}
name={"categoryId"}
rules={[
{ required: true, message: '请选择问题分类!' }
]}
request={async () => {
const req = {}
const data = await handleGetExamineTypeTreeList(req)
console.log('dsadas', data);
let res = [
{
name: '/',
id: 0,
children: data
}
]
let obj: any = {}
const labelList = await handleGetExamineTypeList()
if (labelList && labelList.length > 0) {
labelList.forEach((item: any) => {
obj[item.id] = item.name
})
}
console.log('objobjobjobj', obj);
setCategoryIdObj(obj)
return data
}}
fieldProps={{
treeDefaultExpandAll: true,
// treeDefaultExpandedKeys: [0],
fieldNames: {
label: "name",
value: "id"
},
showSearch: true,
filterTreeNode: (input, treeNode) => {
return (treeNode?.name ?? '').toLowerCase().indexOf(input.toLowerCase()) >= 0;
},
treeNodeFilterProp: 'name'
}}
/>
</Col>
<Col span={12}>
<ProFormText
label={"问题内容"}
name={"title"}
rules={[
{ required: true, message: '请输入问题!' }
]}
/>
</Col>
<Col span={6}>
<ProFormSwitch
label={"是否必填"}
name={"required"}
initialValue={parentRow?.id ? parentRow.required : true}
/>
</Col>
<Col span={6}>
<ProFormSwitch
label={"有效状态"}
name={"status"}
initialValue={parentRow?.id ? parentRow.status : true}
/>
</Col>
<Col span={6}>
<ProFormRadio.Group
label={"题目类型"}
name={"selectType"}
fieldProps={{
options: [{ label: "单选", value: 1 }, { label: "多选", value: 2 }],
}}
initialValue={1}
/>
</Col>
<Col span={24}>
<ProFormList
name="options"
label="选项内容"
initialValue={[]}
creatorButtonProps={{
position: 'bottom',
creatorButtonText: '添加选项'
}}
copyIconProps={false}
style={{ width: '100%' }}
itemContainerStyle={{ width: '100%' }}
itemRender={({ listDom, action }, { record, index }) => (
<div style={{ width: '100%', display: 'flex', alignItems: 'flex-start', marginBottom: '10px' }}>
<div style={{ flex: 1, width: '100%' }}>{listDom}</div>
<div style={{ marginLeft: '8px', marginTop: '30px' }}>{action}</div>
</div>
)}
>
<Row gutter={6} style={{ width: '100%', margin: 0 }}>
<Col span={16}>
<ProFormTextArea
name="text"
label="选项"
fieldProps={{
style: { width: '100%' }
}}
/>
</Col>
<Col span={8}>
<ProFormDigit
name="mark"
label="分值"
fieldProps={{
style: { width: '100%' },
defaultValue: 0
}}
/>
</Col>
</Row>
</ProFormList>
</Col>
</Row>
</ProForm>
</Modal>
</div>
)
}
export default connect(({ user }: ConnectState) => ({
currentUser: user.data
}))(addQuestion);

View File

@ -4,14 +4,17 @@ import { Button, Col, message, Modal, Popconfirm, Row, Space } from "antd";
import { useRef, useState } from "react"; import { useRef, useState } from "react";
import { connect } from "umi"; import { connect } from "umi";
import { handleAddQuestion, handleDeleteQuestion, handleEditQuestion, handleGetQuestionList } from "./service"; import { handleAddQuestion, handleDeleteQuestion, handleEditQuestion, handleGetQuestionList } from "./service";
import { handleGetExamineTypeList, handleGetExamineTypeTreeList } from "../index/service"; import { handleGetAddExamineType, handleGetExamineTypeList, handleGetExamineTypeTreeList } from "../index/service";
import moment from "moment"; import moment from "moment";
import AddQuestion from "./components/addQuestion";
const examineQuestion: React.FC<{ currentUser: any }> = (props) => { const examineQuestion: React.FC<{ currentUser: any }> = (props) => {
const { currentUser } = props const { currentUser } = props
const actionRef = useRef<ActionType>(); const actionRef = useRef<ActionType>();
const formRef = useRef<FormInstance>(); const formRef = useRef<FormInstance>();
const addQuestionRef = useRef<any>()
// 显示新增问题的悬浮框 // 显示新增问题的悬浮框
const [showQuestionModal, setShowQuestionModal] = useState<boolean>(false) const [showQuestionModal, setShowQuestionModal] = useState<boolean>(false)
// 当前点击选中的问题行 // 当前点击选中的问题行
@ -23,10 +26,35 @@ const examineQuestion: React.FC<{ currentUser: any }> = (props) => {
const [columnsStateMap, setColumnsStateMap] = useState<any>({ const [columnsStateMap, setColumnsStateMap] = useState<any>({
createdAt: { show: false } createdAt: { show: false }
}) })
// 拿到protable的问题分类
const [bigTypeList, setBigTypeList] = useState<any>()
const columns: any = [ const columns: any = [
{
title: "问题内容",
dataIndex: "title",
hideInTable: true,
},
{
title: "问题分类",
dataIndex: "id",
hideInTable: true,
valueType: 'treeSelect',
fieldProps: {
multiple: false,
treeDefaultExpandAll: true,
showSearch: true,
treeNodeFilterProp: 'title',
placeholder: '请选择问题分类',
fieldNames: {
label: "name",
value: "id"
},
options: bigTypeList || []
}
},
{ {
title: <div style={{ textAlign: 'center' }}></div>, title: <div style={{ textAlign: 'center' }}></div>,
dataIndex: "description", dataIndex: "description",
@ -50,7 +78,7 @@ const examineQuestion: React.FC<{ currentUser: any }> = (props) => {
ellipsis: true, ellipsis: true,
render: (_, record) => { render: (_, record) => {
let questionType: string = record?.type && record?.type.split('_') && record?.type.split('_').length > 0 ? record?.type.split('_')[0] : '' let questionType: string = record?.type && record?.type.split('_') && record?.type.split('_').length > 0 ? record?.type.split('_')[0] : ''
return questionType === 'radio' ? '单选' : "多选" return questionType === 'radio' ? '单选' : questionType === 'checked' ? "多选" : ""
} }
}, },
{ {
@ -61,7 +89,7 @@ const examineQuestion: React.FC<{ currentUser: any }> = (props) => {
align: 'center', align: 'center',
ellipsis: true, ellipsis: true,
render: (_, record) => { render: (_, record) => {
return record?.required ? '是' : "否" return record?.isNoChildren ? record?.required ? '是' : "否" : ''
} }
}, },
{ {
@ -74,7 +102,7 @@ const examineQuestion: React.FC<{ currentUser: any }> = (props) => {
let str: string = '' let str: string = ''
if (record.options && record?.options.length > 0) { if (record.options && record?.options.length > 0) {
record.options.forEach((item: any, index: number) => { record.options.forEach((item: any, index: number) => {
str += `${index > 0 ? '' : ''}选项${index + 1}${item.text}` str += `${item.text}`
}) })
} }
return str || '' return str || ''
@ -118,7 +146,7 @@ const examineQuestion: React.FC<{ currentUser: any }> = (props) => {
hideInSearch: true, hideInSearch: true,
width: 100, width: 100,
render: (_: any, record: any) => { render: (_: any, record: any) => {
return <Space> return record?.isNoChildren ? <Space>
<a onClick={() => { <a onClick={() => {
console.log('record', record); console.log('record', record);
setCurrentRow(record) setCurrentRow(record)
@ -132,8 +160,7 @@ const examineQuestion: React.FC<{ currentUser: any }> = (props) => {
> >
<a></a> <a></a>
</Popconfirm> </Popconfirm>
</Space> : ""
</Space>
} }
} }
] ]
@ -141,12 +168,87 @@ const examineQuestion: React.FC<{ currentUser: any }> = (props) => {
// 删除分类 // 删除分类
const deleteQuestion = async (id: number) => { const deleteQuestion = async (id: number) => {
const data = await handleDeleteQuestion({ id: id }) const data = await handleDeleteQuestion({ id: id })
if (data.code === 200) { if (data.Result_Code === 100) {
message.success(data.message) message.success('删除成功!')
actionRef.current?.reload() actionRef.current?.reload()
} else {
message.error(data.Result_Desc)
} }
} }
// 递归 处理 将问题拼在列表的对应层 不一定是第二层
const handleGetNewQuestion = (typeList: any, list: any) => {
console.log('typeList', typeList);
console.log('list', list);
if (list && list.length > 0) {
list.forEach((item: any) => {
item.isNoChildren = true
})
}
// 递归查找并插入匹配项的函数
const findAndInsert = (nodes: any[], itemsToInsert: any[]) => {
for (const node of nodes) {
// 在当前节点的子节点中查找匹配
if (node.children) {
findAndInsert(node.children, itemsToInsert);
}
// 检查当前节点是否匹配list中的categoryId
const matchedItems = itemsToInsert.filter(item => item.categoryId === node.id);
console.log('node', node);
console.log('matchedItems', matchedItems);
if (matchedItems.length > 0) {
// 如果匹配到初始化children数组如果不存在
if (!node.children) {
node.children = [];
}
// 将匹配到的项添加到children中
node.children.push(...matchedItems);
// 从待插入列表中移除已处理的项
itemsToInsert = itemsToInsert.filter(item => !matchedItems.includes(item));
}
}
};
// 创建list的副本以避免修改原数组
const itemsToInsert = [...list];
// 开始递归处理
findAndInsert(typeList, itemsToInsert);
return typeList
// let res: any = []
// if (typeList && typeList.length > 0) {
// typeList.forEach((item: any) => {
// if (item.children && item.children.length > 0) {
// let newRes = handleGetNewQuestion(item.children, list)
// item.children = newRes
// res.push(item)
// } else {
// if (list && list.length > 0) {
// list.forEach((subItem: any) => {
// if (subItem.categoryId === item.id) {
// if (item.children && item.children.length > 0) {
// subItem.isNoChildren = true
// item.children.push(subItem)
// } else {
// subItem.isNoChildren = true
// item.children = [subItem]
// }
// }
// })
// }
// res.push(item)
// }
// })
// }
// return res
}
return ( return (
<div> <div>
<div style={{ <div style={{
@ -165,13 +267,35 @@ const examineQuestion: React.FC<{ currentUser: any }> = (props) => {
}} }}
headerTitle={<span style={{ color: "#1890ff", fontSize: 14, fontWeight: 600 }}></span>} headerTitle={<span style={{ color: "#1890ff", fontSize: 14, fontWeight: 600 }}></span>}
search={{ span: 6 }} search={{ span: 6 }}
rowKey={(record: any) => {
return `${record?.id}-${record?.categoryId}`
}}
scroll={{ x: "100%", y: 'calc(100vh - 400px)' }} scroll={{ x: "100%", y: 'calc(100vh - 400px)' }}
request={async (params) => { request={async (params) => {
const data = await handleGetQuestionList() console.log('params', params);
console.log('data', data);
if (data && data.length > 0) { // 拿到分类数据 将 问题数据 拼到对应的分类下面去
return { data, success: true } const typeReq = {
any: [{
"key": ""
}]
}
params?.id
let typeData = await handleGetExamineTypeTreeList(typeReq)
let bigType = JSON.parse(JSON.stringify(typeData))
console.log('bigType', bigType);
setBigTypeList(bigType)
const req = {
any: params?.title ? [{ key: "title", value: params?.title }] : undefined
}
const data = await handleGetQuestionList(req)
console.log('data3231231231', data);
let res = await handleGetNewQuestion(typeData, data)
console.log('res432423', res);
if (res && res.length > 0) {
return { data: res, success: true }
} }
return { data: [], success: true } return { data: [], success: true }
}} }}
@ -191,196 +315,14 @@ const examineQuestion: React.FC<{ currentUser: any }> = (props) => {
/> />
</div> </div>
<AddQuestion onRef={addQuestionRef} parentRow={currentRow}
<Modal showQuestionModal={showQuestionModal}
width={800} setShowQuestionModal={setShowQuestionModal}
title={`${currentRow?.id ? '编辑' : '创建'}问题`} parentActionRef={actionRef}
open={showQuestionModal} setCurrentRow={setCurrentRow}
destroyOnClose />
onOk={() => {
modalRef.current?.validateFields().then(async (res) => {
let req = {}
let data = {}
if (currentRow?.id) {
req = {
...currentRow,
categoryId: res.categoryId,// 题目id
type: `${res.selectType === 1 ? 'radio' : 'checked'}_choice`,// 题目类型
title: res.title,// 问题内容
required: res.required,// 是否必填
options: res.options,
status: res.status,
operator: currentUser?.operator,
description: `${res.categoryId ? categoryIdObj[res.categoryId] : ''}`
}
data = await handleEditQuestion(req)
} else {
req = {
categoryId: res.categoryId,// 题目id
type: `${res.selectType === 1 ? 'radio' : 'checked'}_choice`,// 题目类型
title: res.title,// 问题内容
required: res.required,// 是否必填
options: res.options,
sortOrder: 1,
status: res.status,
operator: currentUser?.operator,
defaultScore: 0,
images: [],
description: `${res.categoryId ? categoryIdObj[res.categoryId] : ''}`
}
data = await handleAddQuestion(req)
}
console.log('datadsadsa', data);
if (data.code === 200) {
modalRef.current?.resetFields()
message.success(data.message)
setShowQuestionModal(false)
actionRef.current?.reload()
setCurrentRow(undefined)
} else {
message.error(data.message)
}
})
}}
onCancel={() => {
modalRef.current?.resetFields()
setShowQuestionModal(false)
setCurrentRow(undefined)
}}
>
<ProForm formRef={modalRef} submitter={false} request={() => {
console.log('currentRow', currentRow);
return {
...currentRow,
selectType: currentRow.type.split('_')[0] === 'checked' ? 2 : 1
}
}}>
<Row gutter={8}>
<Col span={12}>
<ProFormTreeSelect
label={"问题分类"}
name={"categoryId"}
rules={[
{ required: true, message: '请选择问题分类!' }
]}
request={async () => {
const req = {}
const data = await handleGetExamineTypeTreeList(req)
console.log('dsadas', data);
let res = [
{
name: '/',
id: 0,
children: data
}
]
let obj: any = {}
const labelList = await handleGetExamineTypeList()
if (labelList && labelList.length > 0) {
labelList.forEach((item) => {
obj[item.id] = item.name
})
}
setCategoryIdObj(obj)
return res
}}
fieldProps={{
treeDefaultExpandedKeys: [0],
fieldNames: {
label: "name",
value: "id"
},
showSearch: true,
filterTreeNode: (input, treeNode) => {
return (treeNode?.name ?? '').toLowerCase().indexOf(input.toLowerCase()) >= 0;
},
treeNodeFilterProp: 'name'
}}
/>
</Col>
<Col span={12}>
<ProFormText
label={"问题内容"}
name={"title"}
rules={[
{ required: true, message: '请输入问题!' }
]}
/>
</Col>
<Col span={6}>
<ProFormSwitch
label={"是否必填"}
name={"required"}
initialValue={currentRow?.id ? currentRow.required : true}
/>
</Col>
<Col span={6}>
<ProFormSwitch
label={"有效状态"}
name={"status"}
initialValue={currentRow?.id ? currentRow.status : true}
/>
</Col>
<Col span={6}>
<ProFormRadio.Group
label={"题目类型"}
name={"selectType"}
fieldProps={{
options: [{ label: "单选", value: 1 }, { label: "多选", value: 2 }],
}}
initialValue={1}
/>
</Col>
<Col span={24}>
<ProFormList
name="options"
label="选项内容"
initialValue={[]}
creatorButtonProps={{
position: 'bottom',
creatorButtonText: '添加选项'
}}
copyIconProps={false}
style={{ width: '100%' }}
itemContainerStyle={{ width: '100%' }}
itemRender={({ listDom, action }, { record, index }) => (
<div style={{ width: '100%', display: 'flex', alignItems: 'flex-start', marginBottom: '10px' }}>
<div style={{ flex: 1, width: '100%' }}>{listDom}</div>
<div style={{ marginLeft: '8px', marginTop: '30px' }}>{action}</div>
</div>
)}
>
<Row gutter={6} style={{ width: '100%', margin: 0 }}>
<Col span={16}>
<ProFormTextArea
name="text"
label="选项"
fieldProps={{
style: { width: '100%' }
}}
/>
</Col>
<Col span={8}>
<ProFormDigit
name="mark"
label="分值"
fieldProps={{
style: { width: '100%' },
defaultValue: 0
}}
/>
</Col>
</Row>
</ProFormList>
</Col>
</Row>
</ProForm>
</Modal>
</div > </div >
) )
} }

View File

@ -1,29 +1,30 @@
import request from "@/utils/request" // import request from "@/utils/request"
import request from "@/utils/requestJava"
// 拿到问题列表接口 // 拿到问题列表接口
export async function handleGetQuestionList(params?: any) { export async function handleGetQuestionList(params?: any) {
const data = await request.get('/questions', {params}) const data = await request.post('/questions/search/many', params)
if (data.code === 200) { if (data.Result_Code === 100) {
return data.data return data.Result_Data.List
} }
return [] return []
} }
// 新增问题接口 // 新增问题接口
export async function handleAddQuestion(params?: any) { export async function handleAddQuestion(params?: any) {
const data = await request.post('/questions', params) const data = await request.post('/questions/save', params)
return data return data
} }
// 编辑问题接口 // 编辑问题接口
export async function handleEditQuestion(params?: any) { export async function handleEditQuestion(params?: any) {
const data = await request.post(`/questions/${params.id}`, params) const data = await request.post(`/questions/update`, params)
return data return data
} }
// 删除问题 // 删除问题
export async function handleDeleteQuestion(params?: any) { export async function handleDeleteQuestion(params?: any) {
const data = await request.get(`/questions/delete/${params.id}`) const data = await request.get(`/questions/delete`, { params })
return data return data
} }

View File

@ -0,0 +1,26 @@
{
// 第一层 片区
value: 81, // (id 变成value),
... // 别的一样没事
childnre: [{
// 第二层 服务区
value: 528, // (id变value)
SPRegionTypeName: "", // 片区名称
SPRegionTypeId: "", // 片区id
... // 别的一样
childnre: [{
// 第三层 实际业务
serverPartName: "", // 服务区名称
serverPartId: "", // 服务区id
SPRegionTypeName: "", // 片区名称
SPRegionTypeId: "", // 片区id
serverPartShopName:"" ,// 门店名称(可能会有)
serverPartShopId:"",// 门店id (可能会有)
... // 实际业务内容
}
]
}]
}

View File

@ -1,6 +1,5 @@
/* 打印样式 */ /* 打印样式 */
@media print { @media print {
/* 隐藏不需要打印的元素 */ /* 隐藏不需要打印的元素 */
.ant-pro-card-header, .ant-pro-card-header,
.ant-pro-table-search, .ant-pro-table-search,
@ -21,7 +20,7 @@
td { td {
border: 1px solid #000; border: 1px solid #000;
padding: 8px; padding: 8px;
text-align: left; text-align: center;
} }
/* 页面设置 */ /* 页面设置 */
@ -53,4 +52,21 @@
margin: 20px 0; margin: 20px 0;
white-space: pre-wrap; white-space: pre-wrap;
} }
/* 确保iframe内的打印内容可见 */
iframe {
display: block !important;
visibility: visible !important;
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
}
/* 确保打印内容可见 */
.print-content, .print-content * {
visibility: visible !important;
display: block !important;
}
} }

View File

@ -1,27 +1,36 @@
import { ConnectState } from "@/models/global"; import { ConnectState } from "@/models/global";
import { ActionType, ProTable } from "@ant-design/pro-components"; import { ActionType, ProTable } from "@ant-design/pro-components";
import ProForm, { FormInstance, ProFormList, ProFormText } from "@ant-design/pro-form"; import ProForm, { FormInstance, ProFormList, ProFormSelect, ProFormText, ProFormTextArea, ProFormUploadButton } from "@ant-design/pro-form";
import { Col, Row, Image, Button, Input, Modal } from "antd"; import { Col, Row, Image, Button, Input, Modal } from "antd";
import moment from "moment"; import moment from "moment";
import { useEffect, useRef, useState } from "react"; import { useEffect, useImperativeHandle, useRef, useState } from "react";
import { connect } from "umi"; import { connect } from "umi";
import { handleGetExamineTypeTreeList } from "../../index/service"; import { handleGetExamineTypeTreeList } from "../../index/service";
import session from "@/utils/session"; import session from "@/utils/session";
import { TextAreaRef } from "antd/lib/input/TextArea"; import { TextAreaRef } from "antd/lib/input/TextArea";
import './printStyle.css'; import './printStyle.css';
import { handleGetDealerList, handleUploadFile } from "../service";
type DetailProps = { type DetailProps = {
parentRow: any; // 父级数据 parentRow: any; // 父级数据
show: boolean;// 抽屉是否显示 show: boolean;// 抽屉是否显示
detailType: string; // 详情的类型 detailType: string; // 详情的类型
currentUser: any
onRef?: any
showError: boolean;// 是不是处理异常
} }
const RecordDetail = ({ parentRow, show, detailType }: DetailProps) => { const RecordDetail = ({ parentRow, show, detailType, currentUser, onRef, showError }: DetailProps) => {
const formRef = useRef<FormInstance>(); const formRef = useRef<FormInstance>();
console.log('currentUser', currentUser);
const actionRef = useRef<ActionType>(); const actionRef = useRef<ActionType>();
const tableFormRef = useRef<FormInstance>(); const tableFormRef = useRef<FormInstance>();
const errorStatusFormRef = useRef<FormInstance>();
const errorStatusFormReflast = useRef<FormInstance>();
// 表格数据 // 表格数据
const [proTableData, setTableData] = useState<any>([]) const [proTableData, setTableData] = useState<any>([])
@ -34,11 +43,15 @@ const RecordDetail = ({ parentRow, show, detailType }: DetailProps) => {
const footerRef = useRef<TextAreaRef>(null) const footerRef = useRef<TextAreaRef>(null)
// 考核分类的对象格式 // 考核分类的对象格式
const [examineObj, setExamineObj] = useState<any>() const [examineObj, setExamineObj] = useState<any>()
// 文件列表
const [fileList, setFileList] = useState<any>()
// 选人的列表数据
const [selectPersonList, setSelectPersonList] = useState<any>()
const columns: any = [ const columns: any = [
{ {
title: "考核分类", title: "考核分类",
dataIndex: "name" dataIndex: "parentName"
}, },
{ {
title: "考核子类", title: "考核子类",
@ -52,10 +65,11 @@ const RecordDetail = ({ parentRow, show, detailType }: DetailProps) => {
dataIndex: "title", dataIndex: "title",
hideInTable: detailType === 'modal', hideInTable: detailType === 'modal',
render: (_, record) => { render: (_, record) => {
let questionType: string = record?.question.type.split('_')[0]
let str: string = '' let str: string = ''
if (record.choiceResponse && record?.choiceResponse.length > 0) { if (record.choiceResponse && record?.choiceResponse.length > 0) {
record.choiceResponse.forEach((item: any, index: number) => { record.choiceResponse.forEach((item: any, index: number) => {
str += `${index > 0 ? '' : ''}答案${index + 1}${item}` str += `${item.text ? item.text : item}`
}) })
} }
return str || "" return str || ""
@ -90,6 +104,8 @@ const RecordDetail = ({ parentRow, show, detailType }: DetailProps) => {
placeName: parentRow.template?.title || '', placeName: parentRow.template?.title || '',
submittedAt: moment(parentRow?.submittedAt).format('YYYY-MM-DD HH:mm:ss') submittedAt: moment(parentRow?.submittedAt).format('YYYY-MM-DD HH:mm:ss')
} }
console.log('formRes', obj);
setFormRes(obj) setFormRes(obj)
formRef.current?.setFieldsValue(obj) formRef.current?.setFieldsValue(obj)
} }
@ -121,7 +137,7 @@ const RecordDetail = ({ parentRow, show, detailType }: DetailProps) => {
} }
// 把需要打印的数组拿到一层中来 // 把需要打印的数组拿到一层中来
const handleGetTableRes = (list: any) => { const handleGetTableRes = (list: any, fieldObj?: any) => {
console.log('list', list); console.log('list', list);
let res: any = []; let res: any = [];
@ -132,13 +148,14 @@ const RecordDetail = ({ parentRow, show, detailType }: DetailProps) => {
item.question.forEach((questionItem: any) => { item.question.forEach((questionItem: any) => {
res.push({ res.push({
...questionItem, ...questionItem,
parentId: item.parentId ? item.parentId : "-" parentId: item.parentId ? item.parentId : "-",
parentName: fieldObj && item.parentId ? fieldObj[item.parentId] : "-"
}); });
}); });
} }
if (item.children && item.children.length > 0) { if (item.children && item.children.length > 0) {
// 如果有子项,递归处理子项并合并结果 // 如果有子项,递归处理子项并合并结果
const childResults = handleGetTableRes(item.children); const childResults = handleGetTableRes(item.children, fieldObj);
if (childResults && childResults.length > 0) { if (childResults && childResults.length > 0) {
res = [...res, ...childResults]; res = [...res, ...childResults];
} }
@ -161,61 +178,100 @@ const RecordDetail = ({ parentRow, show, detailType }: DetailProps) => {
// 从categoryId获取分类名称 // 从categoryId获取分类名称
if (item.question && item.question.categoryId) { if (item.question && item.question.categoryId) {
// 这里需要根据实际情况获取分类名称 // 这里需要根据实际情况获取分类名称
// 可以从缓存或其他地方获取
// const categoryInfo = getCategoryInfo(item.question.categoryId);
item.name = examineObj && item.parentId ? examineObj[item.parentId] : "" item.name = examineObj && item.parentId ? examineObj[item.parentId] : ""
item.categoryName = item?.question.title || ''; item.categoryName = item?.question.title || '';
item.parentCategoryName = item?.name || ''; item.parentCategoryName = item?.name || '';
} }
}); });
// 按照考核分类和考核子类排序,确保相同的分类和子类相邻
processedData.sort((a, b) => {
// 先按考核分类排序
if (a.parentCategoryName !== b.parentCategoryName) {
return a.parentCategoryName.localeCompare(b.parentCategoryName);
}
// 再按考核子类排序
return a.categoryName.localeCompare(b.categoryName);
});
// 计算合并信息 // 计算合并信息
let currentCategory = ''; let currentCategory = '';
let currentSubCategory = ''; let currentSubCategory = '';
let categorySpan = 0; let categoryStartIndex = 0;
let subCategorySpan = 0; let subCategoryStartIndex = 0;
processedData.forEach((item: any, index: number) => { processedData.forEach((item: any, index: number) => {
// 初始化合并标记
item._categoryMerged = false;
item._subCategoryMerged = false;
// 处理考核分类合并 // 处理考核分类合并
if (item.parentCategoryName === currentCategory) { if (index === 0 || item.parentCategoryName !== currentCategory) {
categorySpan++; // 如果是新的分类,更新前一个分类的合并信息
item._categoryMerged = true; // 标记为已合并 if (index > 0) {
} else { const span = index - categoryStartIndex;
// 更新前一个分类的合并信息 if (span > 1) { // 只有当跨度大于1时才需要合并
if (categorySpan > 0 && index > 0) { processedData[categoryStartIndex]._categoryRowSpan = span;
processedData[index - categorySpan]._categoryRowSpan = categorySpan + 1; // 标记除第一行外的其他行为已合并
for (let i = categoryStartIndex + 1; i < index; i++) {
processedData[i]._categoryMerged = true;
}
}
} }
// 记录新分类的开始位置
categoryStartIndex = index;
currentCategory = item.parentCategoryName; currentCategory = item.parentCategoryName;
categorySpan = 0; // 重置子类信息,因为新的分类开始了
item._categoryRowSpan = 1; // 默认占一行 currentSubCategory = '';
subCategoryStartIndex = index;
} }
// 处理考核子类合并 // 处理考核子类合并
if (item.categoryName === currentSubCategory) { if (index === 0 || item.categoryName !== currentSubCategory || item.parentCategoryName !== currentCategory) {
subCategorySpan++; // 如果是新的子类或新的分类,更新前一个子类的合并信息
item._subCategoryMerged = true; // 标记为已合并 if (index > 0 && currentSubCategory) {
} else { const span = index - subCategoryStartIndex;
// 更新前一个子类的合并信息 if (span > 1) { // 只有当跨度大于1时才需要合并
if (subCategorySpan > 0 && index > 0) { processedData[subCategoryStartIndex]._subCategoryRowSpan = span;
processedData[index - subCategorySpan]._subCategoryRowSpan = subCategorySpan + 1; // 标记除第一行外的其他行为已合并
for (let i = subCategoryStartIndex + 1; i < index; i++) {
processedData[i]._subCategoryMerged = true;
}
}
} }
// 记录新子类的开始位置
subCategoryStartIndex = index;
currentSubCategory = item.categoryName; currentSubCategory = item.categoryName;
subCategorySpan = 0;
item._subCategoryRowSpan = 1; // 默认占一行
} }
}); });
// 处理最后一组数据的合并信息 // 处理最后一组数据的合并信息
if (categorySpan > 0 && processedData.length > 0) { const lastIndex = processedData.length - 1;
const targetIndex = processedData.length - categorySpan - 1; if (lastIndex >= 0) {
if (targetIndex >= 0 && targetIndex < processedData.length && processedData[targetIndex]) { // 处理最后一个分类的合并
processedData[targetIndex]._categoryRowSpan = categorySpan + 1; const categorySpan = lastIndex - categoryStartIndex + 1;
if (categorySpan > 1) {
processedData[categoryStartIndex]._categoryRowSpan = categorySpan;
for (let i = categoryStartIndex + 1; i <= lastIndex; i++) {
processedData[i]._categoryMerged = true;
}
} else {
// 如果只有一行,不需要合并
processedData[categoryStartIndex]._categoryRowSpan = 1;
processedData[categoryStartIndex]._categoryMerged = false;
} }
}
if (subCategorySpan > 0 && processedData.length > 0) { // 处理最后一个子类的合并
const targetIndex = processedData.length - subCategorySpan - 1; const subCategorySpan = lastIndex - subCategoryStartIndex + 1;
if (targetIndex >= 0 && targetIndex < processedData.length && processedData[targetIndex]) { if (subCategorySpan > 1) {
processedData[targetIndex]._subCategoryRowSpan = subCategorySpan + 1; processedData[subCategoryStartIndex]._subCategoryRowSpan = subCategorySpan;
for (let i = subCategoryStartIndex + 1; i <= lastIndex; i++) {
processedData[i]._subCategoryMerged = true;
}
} else {
// 如果只有一行,不需要合并
processedData[subCategoryStartIndex]._subCategoryRowSpan = 1;
processedData[subCategoryStartIndex]._subCategoryMerged = false;
} }
} }
@ -250,73 +306,58 @@ const RecordDetail = ({ parentRow, show, detailType }: DetailProps) => {
// 打印表格方法 // 打印表格方法
const handlePrintTable = () => { const handlePrintTable = () => {
// 创建打印内容
const printWindow = window.open('', '_blank');
if (!printWindow) {
Modal.error({ title: '打印失败', content: '无法创建打印窗口,请检查浏览器设置。' });
return;
}
// 获取表格数据 // 获取表格数据
const tableData = proTableData || []; const tableData = proTableData || [];
// 构建HTML内容 // 创建一个iframe用于打印这样可以避免CSS冲突
const printIframe = document.createElement('iframe');
printIframe.style.position = 'absolute';
printIframe.style.width = '0';
printIframe.style.height = '0';
printIframe.style.border = '0';
document.body.appendChild(printIframe);
// 构建打印内容
let printContent = ` let printContent = `
<html> <!DOCTYPE html>
<head> <html>
<title></title> <head>
<style> <title></title>
body { font-family: Arial, sans-serif; margin: 20px; } <style>
.print-header, .print-footer { margin: 20px 0; white-space: pre-wrap; } /* 重置样式 */
table { width: 100%; border-collapse: collapse; margin: 20px 0; } * { margin: 0; padding: 0; box-sizing: border-box; }
th, td { border: 1px solid #ddd; padding: 8px; text-align: left; } body { font-family: Arial, sans-serif; padding: 32px; }
th { background-color: #f2f2f2; }
.info-section { margin-bottom: 20px; } /* 表格样式 */
.info-item { margin-bottom: 10px; } table { width: 100%; border-collapse: collapse; margin: 8px 0; }
.info-label { font-weight: bold; display: inline-block; width: 100px; } th, td { border: 1px solid #000; padding: 8px; text-align: center; }
.image-container { display: flex; flex-wrap: wrap; margin-top: 10px; } th { background-color: #f2f2f2; font-weight: bold; }
.image-item { margin-right: 10px; margin-bottom: 10px; }
@media print { /* 打印设置 */
button { display: none; } @page { margin: 1cm; }
}
</style> /* 确保所有内容可见 */
</head> .print-content { visibility: visible !important; }
<body> </style>
</head>
<body>
<div class="print-content">
`; `;
// 加一下页眉
// 添加自定义页眉
if (headerContent) {
printContent += `<div class="print-header">${headerContent}</div>`;
}
// 添加基本信息
printContent += ` printContent += `
<div class="info-section"> <div style="width:100%;text-align:center;font-size: 32px;font-weight: bold;margin-bottom: 20px">
<div class="info-item"><span class="info-label">:</span> ${formRes?.serverPartName || ''}</div> <div>${parentRow?.serverPartName || '-'}</div>
<div class="info-item"><span class="info-label">:</span> ${formRes?.placeName || ''}</div> <div></div>
<div class="info-item"><span class="info-label">:</span> ${formRes?.uploadResult || ''}</div>
<div class="info-item"><span class="info-label">:</span> ${formRes?.userName || ''}</div>
<div class="info-item"><span class="info-label">:</span> ${formRes?.submittedAt || ''}</div>
</div> </div>
`; <div style="width:100%;display:flex;align-items: center;justify-content: space-between;">
<div>${formRes?.uploadResult || '-'}</div>
// 添加图片信息 <div>${formRes?.placeName || '-'}</div>
if (formRes?.imgsList && formRes.imgsList.length > 0) { </div>
printContent += ` <div style="width:100%;display:flex;align-items: center;justify-content: space-between;">
<div class="info-section"> <div>${parentRow?.userName || '-'}</div>
<div class="info-label">:</div> <div>${parentRow?.createdAt ? moment(parentRow?.createdAt).format('YYYY-MM-DD HH:mm:ss') : '-'}</div>
<div class="image-container"> </div>
`; `
formRes.imgsList.forEach((item: string) => {
printContent += `<div class="image-item"><img src="${item}" style="width: 150px; height: 150px;" /></div>`;
});
printContent += `
</div>
</div>
`;
}
// 添加表格 // 添加表格
printContent += ` printContent += `
@ -325,12 +366,14 @@ const RecordDetail = ({ parentRow, show, detailType }: DetailProps) => {
<tr> <tr>
<th></th> <th></th>
<th></th> <th></th>
<th></th> <th>${detailType !== 'modal' ? '考核结果' : '考核选项'}</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
`; `;
console.log('tableData', tableData);
// 添加表格数据行 // 添加表格数据行
tableData.forEach((record: any) => { tableData.forEach((record: any) => {
printContent += '<tr>'; printContent += '<tr>';
@ -339,65 +382,114 @@ const RecordDetail = ({ parentRow, show, detailType }: DetailProps) => {
if (record._categoryMerged) { if (record._categoryMerged) {
// 如果已合并,不显示单元格 // 如果已合并,不显示单元格
} else { } else {
printContent += `<td ${record._categoryRowSpan && record._categoryRowSpan > 1 ? `rowspan="${record._categoryRowSpan}"` : ''}>${record.parentCategoryName || ''}</td>`; printContent += `<td style="width: 100px" ${record._categoryRowSpan && record._categoryRowSpan > 1 ? `rowspan="${record._categoryRowSpan}"` : ''}>${record.parentName || ''}</td>`;
} }
// 考核子类列 - 处理合并单元格 // 考核子类列 - 处理合并单元格
if (record._subCategoryMerged) { if (record._subCategoryMerged) {
// 如果已合并,不显示单元格 // 如果已合并,不显示单元格
} else { } else {
printContent += `<td ${record._subCategoryRowSpan && record._subCategoryRowSpan > 1 ? `rowspan="${record._subCategoryRowSpan}"` : ''}>${record.question?.title || ''}</td>`; printContent += `<td style="width: 100px" ${record._subCategoryRowSpan && record._subCategoryRowSpan > 1 ? `rowspan="${record._subCategoryRowSpan}"` : ''}>${record.question?.title || ''}</td>`;
} }
// 考核结果列 // 考核结果列
let resultStr = ''; let resultStr = '';
if (record.choiceResponse && record.choiceResponse.length > 0) { if (record.choiceResponse && record.choiceResponse.length > 0) {
record.choiceResponse.forEach((item: any, index: number) => { record.choiceResponse.forEach((item: any, index: number) => {
resultStr += `${index > 0 ? '' : ''}答案${index + 1}${item}`; resultStr += `${item.text ? item.text : item}`;
// ${index > 0 ? '' : ''}结果${index + 1}
}); });
} }
printContent += `<td>${resultStr || ''}</td>`; printContent += `<td style="text-align:left">${resultStr || ''}</td>`;
printContent += '</tr>'; printContent += '</tr>';
}); });
// if (parentRow?.situation !== 1) {
// 加一个页脚
printContent += `
<table>
<tbody>
${formRes.errorStatus >= 1 ? `<tr>
<td style="width: 100px"></td>
<td>${formRes.suggestPerson.STAFF_NAME || '-'}</td>
<td style="width: 100px"></td>
<td>${formRes.suggestTime || '-'}</td>
</tr>
<tr>
<td style="width: 100px"></td>
<td colspan="3">${formRes.suggestion || '-'}</td>
</tr>`: ""
}
${formRes.errorStatus === 2 ? `
<tr>
<td style="width: 100px"></td>
<td>${formRes.person.STAFF_NAME || '-'}</td>
<td style="width: 100px"></td>
<td>${formRes.feedbackTime || '-'}</td>
</tr>
<tr>
<td style="width: 100px"></td>
<td colspan="3">${formRes.feedbackContent || '-'}</td>
</tr>
`:
""
}
</tbody>
</table>
`
// }
printContent += ` printContent += `
</tbody> </tbody>
</table> </table>
`;
// 添加自定义页脚
if (footerContent) {
printContent += `<div class="print-footer">${footerContent}</div>`;
}
// 添加打印按钮
printContent += `
<div style="text-align: center; margin-top: 20px;">
<button onclick="window.print();" style="padding: 8px 16px;"></button>
<button onclick="window.close();" style="padding: 8px 16px; margin-left: 10px;"></button>
</div> </div>
</body>
</html>
`; `;
printContent += ` console.log('printContent', printContent);
</body>
</html>
`;
// 写入打印窗口并打印 // 获取iframe的文档对象
printWindow.document.open(); const iframeDocument = printIframe.contentWindow?.document;
printWindow.document.write(printContent); if (iframeDocument) {
printWindow.document.close(); // 打开文档进行写入
iframeDocument.open();
// 等待图片加载完成后打印 iframeDocument.write(printContent);
setTimeout(() => { iframeDocument.close();
printWindow.focus();
// 自动打印可以取消注释下面的代码
// printWindow.print();
}, 500);
// 等待内容渲染完成后打印
setTimeout(() => {
try {
// 使用iframe的打印功能
printIframe.contentWindow?.print();
} catch (error) {
console.error('打印失败:', error);
} finally {
// 打印完成后移除iframe
setTimeout(() => {
document.body.removeChild(printIframe);
}, 100);
}
}, 500); // 增加等待时间确保内容完全渲染
} else {
console.error('无法获取iframe文档对象');
}
}; };
useImperativeHandle(onRef, () => ({
errorStatusFormRef,
errorStatusFormReflast,
formRes,
selectPersonList,
fileList
}));
return ( return (
<div> <div>
<ProForm <ProForm
@ -444,58 +536,216 @@ const RecordDetail = ({ parentRow, show, detailType }: DetailProps) => {
readonly readonly
/> />
</Col> </Col>
<Col span={16}>
<ProFormList {
label={"附件列表"} formRes?.imgsList && formRes?.imgsList.length > 0 ?
name={"imgsList"} <Col span={24}>
copyIconProps={false} <div style={{ width: "100%", height: '170px', overflowX: "auto", display: 'flex', alignItems: 'center' }}>
deleteIconProps={false} {
readonly formRes?.imgsList && formRes?.imgsList.length > 0 ?
style={{ width: '100%' }} formRes?.imgsList.map((item: string) => {
itemContainerStyle={{ width: '100%' }} return <Image key={item} style={{ width: "150px", height: "150px", marginRight: "12px" }} src={item} />
itemRender={({ listDom, action }, { record, index }) => ( })
<div style={{ width: '100%', display: 'flex', alignItems: 'flex-start', marginBottom: '10px' }}> : ''
<div style={{ flex: 1, width: '100%' }}>{listDom}</div> }
<div style={{ marginLeft: '8px', marginTop: '30px' }}>{action}</div>
</div> </div>
)} </Col>
>
<div style={{ width: "300px", height: '170px', overflowX: "auto", display: 'flex', alignItems: 'center' }}> // <Col span={16}>
{ // <ProFormList
formRes?.imgsList && formRes?.imgsList.length > 0 ? // label={"附件列表"}
formRes?.imgsList.map((item: string) => { // name={"imgsList"}
return <Image style={{ width: "150px", height: "150px", marginRight: "12px" }} src={item} /> // copyIconProps={false}
}) // deleteIconProps={false}
: '' // readonly
} // alwaysShowItemLabel
</div> // style={{ width: '100%' }}
</ProFormList> // itemContainerStyle={{ width: '100%' }}
</Col> // itemRender={({ listDom, action }, { record, index }) => (
// <div style={{ width: '100%', display: 'flex', alignItems: 'flex-start', marginBottom: '10px' }}>
// <div style={{ flex: 1, width: '100%' }}>{listDom}</div>
// <div style={{ marginLeft: '8px', marginTop: '30px' }}>{action}</div>
// </div>
// )}
// >
// <div style={{ width: "300px", height: '170px', overflowX: "auto", display: 'flex', alignItems: 'center' }}>
// {
// formRes?.imgsList && formRes?.imgsList.length > 0 ?
// formRes?.imgsList.map((item: string) => {
// return <Image key={item} style={{ width: "150px", height: "150px", marginRight: "12px" }} src={item} />
// })
// : ''
// }
// </div>
// </ProFormList>
// </Col>
: ""
}
</> </>
} }
</Row> </Row>
</ProForm> </ProForm>
{
showError && formRes && formRes.errorStatus >= 0 ?
<ProForm
formRef={errorStatusFormRef}
submitter={false}
layout={'horizontal'}
request={() => {
console.log('formRes', formRes);
return {
...formRes,
personIndex: formRes.person.STAFF_ID
}
}}
>
<Row gutter={8}>
<Col span={8}>
<ProFormSelect
label={"异常处理人"}
name={"personIndex"}
request={async () => {
const req = {
SERVERPART_ID: formRes.serverPartId,
PROVINCE_CODE: currentUser.provinceCode,
}
const data = await handleGetDealerList(req)
console.log('data', data)
let res = data.Data.List
setSelectPersonList(res)
return res
}}
rules={[
{ required: true, message: "请选择处理人" }
]}
fieldProps={{
fieldNames: {
"label": "STAFF_NAME",
"value": "STAFF_ID"
}
}}
disabled={formRes.errorStatus > 0}
/>
</Col>
<Col span={8}>
<ProFormTextArea
label={"整改意见"}
name={"suggestion"}
rules={[
{ required: true, message: "请输入整改意见" }
]}
disabled={formRes.errorStatus > 0}
/>
</Col>
</Row>
</ProForm> : ""
}
{
showError && formRes && formRes.errorStatus >= 1 ?
<ProForm
formRef={errorStatusFormReflast}
submitter={false}
layout={'horizontal'}
request={() => {
console.log('formRes', formRes);
let list: any = []
if (formRes.feedbackImgList && formRes.feedbackImgList.length > 0) {
formRes.feedbackImgList.forEach((item: string, index: number) => {
list.push({
uid: '',
name: `image${index}.png`,
status: 'done',
url: item,
})
})
}
setFileList(list)
return {
...formRes,
}
}}
>
<Row gutter={8}>
<Col span={8}>
<ProFormUploadButton
label={"反馈图片"}
listType="picture-card"
name={"feedbackImgList"}
fileList={fileList}
rules={[{
required: true,
message: "请上传反馈图片"
}]}
disabled={formRes.errorStatus > 1}
fieldProps={{
customRequest: async (info: any) => {
console.log('info', info);
const formData = new FormData();
formData.append('file', info.file, typeof info.file !== 'string' ? info.file?.name : '');
const data = await handleUploadFile(formData)
console.log('data', data);
let url = `https://es.robot-z.cn/${data.data.path}`
let file = JSON.parse(JSON.stringify(fileList))
file.push({
uid: '',
// name: `${data.data.filename}.png`,
name: `${info.file.name}.png`,
status: 'done',
url: url,
})
setFileList(file)
},
onChange: (info: any) => {
if (info.file.status === 'removed') {
console.log('info', info);
setFileList(info.fileList)
}
}
}}
/>
</Col>
<Col span={8}>
<ProFormTextArea
label={"反馈内容"}
name={"feedbackContent"}
rules={[
{ required: true, message: "请输入反馈内容" }
]}
disabled={formRes.errorStatus > 1}
/>
</Col>
</Row>
</ProForm> : ""
}
<ProTable <ProTable
actionRef={actionRef} actionRef={actionRef}
formRef={tableFormRef} formRef={tableFormRef}
search={false} search={false}
columns={columns} columns={columns}
bordered
toolbar={{ toolbar={{
actions: [ actions: [
<Button type="primary" onClick={(e) => { <Button type="primary" onClick={(e) => {
setPrintModalVisible(true) handlePrintTable()
}}> }}>
</Button> </Button>
] ]
}} }}
scroll={{ x: "100%", y: 'calc(100vh - 400px)' }} scroll={{ x: "100%", y: 'calc(100vh - 300px)' }}
pagination={false} pagination={false}
options={false} options={false}
request={async () => { request={async () => {
let res = detailType === 'modal' ? parentRow?.questionnaireTemplateQuestions let res = detailType === 'modal' ? parentRow?.questionnaireTemplateQuestions
|| [] : parentRow?.questionResponses || [] || [] : parentRow?.questionResponses || []
console.log('parentRow', parentRow);
console.log('res', res); console.log('res', res);
let obj: any = {} let obj: any = {}
@ -513,14 +763,21 @@ const RecordDetail = ({ parentRow, show, detailType }: DetailProps) => {
} }
setExamineObj(obj) setExamineObj(obj)
console.log('resresres', res);
let tableData = await handeGetTableData(typeData, res) let tableData = await handeGetTableData(typeData, res)
console.log('tableData', tableData); console.log('tableData', tableData);
// 拿到整个数据之后 递归取出tableData中有 question 数据的那一层数据变为一个数组 // 拿到整个数据之后 递归取出tableData中有 question 数据的那一层数据变为一个数组
let tableRes = await handleGetTableRes(tableData) let tableRes = await handleGetTableRes(tableData, obj)
console.log('tableRes', tableRes); console.log('tableRes', tableRes);
// 处理表格数据,为合并单元格做准备 // 处理表格数据,为合并单元格做准备
let processedData = processTableDataForMerge(tableRes); let processedData = processTableDataForMerge(tableRes);
console.log('processedData', processedData);
setTableData(processedData) setTableData(processedData)
if (processedData && processedData.length > 0) { if (processedData && processedData.length > 0) {
@ -582,39 +839,9 @@ const RecordDetail = ({ parentRow, show, detailType }: DetailProps) => {
}} }}
/> />
<Modal {/* 移除打印设置弹窗 */}
title="打印设置"
open={printModalVisible}
onOk={() => {
handlePrintTable();
setPrintModalVisible(false);
}}
onCancel={() => setPrintModalVisible(false)}
width={600}
>
<div style={{ marginBottom: 16 }}>
<div style={{ marginBottom: 8 }}></div>
<Input.TextArea
ref={headerRef}
rows={4}
value={headerContent}
onChange={(e) => setHeaderContent(e.target.value)}
placeholder="请输入要在打印页面顶部显示的内容"
/>
</div>
<div>
<div style={{ marginBottom: 8 }}></div>
<Input.TextArea
ref={footerRef}
rows={4}
value={footerContent}
onChange={(e) => setFooterContent(e.target.value)}
placeholder="请输入要在打印页面底部显示的内容"
/>
</div>
</Modal>
</div> </div >
) )
} }

View File

@ -2,17 +2,19 @@ import { ConnectState } from "@/models/global";
import { ActionType, FormInstance, ProTable } from "@ant-design/pro-components"; import { ActionType, FormInstance, ProTable } from "@ant-design/pro-components";
import { useRef, useState } from "react"; import { useRef, useState } from "react";
import { connect } from "umi"; import { connect } from "umi";
import { handleDeleteRecord, handleGetRecordList } from "./service"; import { handleDeleteRecord, handleGetRecordList, handleGetRecordTreeList, handleUpdateExtend } from "./service";
import moment from "moment"; import moment from "moment";
import { Button, Drawer, Image, message, Popconfirm, Space } from "antd"; import { Button, Drawer, Image, message, Popconfirm, Space } from "antd";
import { handleGetServerpartDDL } from "@/components/leftSelectTree/service"; import { handleGetServerpartDDL } from "@/components/leftSelectTree/service";
import RecordDetail from "./components/recordDetail"; import RecordDetail from "./components/recordDetail";
import LeftSelectTree from "@/components/leftSelectTree/leftSelectTree";
const examineRecord: React.FC<{ currentUser: any }> = (props) => { const examineRecord: React.FC<{ currentUser: any }> = (props) => {
const { currentUser } = props const { currentUser } = props
const actionRef = useRef<ActionType>(); const actionRef = useRef<ActionType>();
const formRef = useRef<FormInstance>(); const formRef = useRef<FormInstance>();
const recordDetailRef = useRef<any>()
// 显示的附件数据 // 显示的附件数据
const [showImgList, setShowImgList] = useState<string[]>([]) const [showImgList, setShowImgList] = useState<string[]>([])
// 预览图片 // 预览图片
@ -23,59 +25,101 @@ const examineRecord: React.FC<{ currentUser: any }> = (props) => {
const [currentRow, setCurrentRow] = useState<any>() const [currentRow, setCurrentRow] = useState<any>()
// 显示详情抽屉 // 显示详情抽屉
const [showDetail, setShowDetail] = useState<boolean>(false) const [showDetail, setShowDetail] = useState<boolean>(false)
// 判断是否点了出现的是异常处理的抽屉
const [showAbnormal, setShowAbnormal] = useState<boolean>(false)
// 树相关的属性和方法
const [selectedId, setSelectedId] = useState<string[]>()
const [columnsStateMap, setColumnsStateMap] = useState<any>({
score: { show: false }
})
const [collapsible, setCollapsible] = useState<boolean>(false)
const columns: any = [ const columns: any = [
{ {
title: "统计日期", title: "统计日期",
dataIndex: "staticDate", dataIndex: "staticDate",
hideInTable: true, hideInTable: true,
valueType: "dateRange", valueType: "date",
initialValue: [moment().startOf('M'), moment()], initialValue: moment().subtract('1', 'd'),
search: {
transform: (value: any) => {
return {
startTime: moment(value[0]).format('YYYY-MM-DD'),
endTime: moment(value[1]).format('YYYY-MM-DD')
};
},
},
fieldProps: {
picker: "day",
format: 'YYYY-MM-DD',
}
}, },
// {
// title: "统计日期",
// dataIndex: "staticDate",
// hideInTable: true,
// valueType: "dateRange",
// initialValue: [moment().startOf('M'), moment()],
// search: {
// transform: (value: any) => {
// return {
// startTime: moment(value[0]).format('YYYY-MM-DD'),
// endTime: moment(value[1]).format('YYYY-MM-DD')
// };
// },
// },
// fieldProps: {
// picker: "day",
// format: 'YYYY-MM-DD',
// }
// },
// {
// title: "服务区",
// dataIndex: "serverPartId",
// hideInTable: true,
// valueType: "select",
// request: async () => {
// const req = {
// ProvinceCode: currentUser?.provinceCode,
// StatisticsType: 1000
// }
// const data = await handleGetServerpartDDL(req)
// return data
// }
// },
{ {
title: "服务区", title: "巡查类型",
dataIndex: "serverPartId", dataIndex: "inspectionType",
hideInTable: true, hideInTable: true,
valueType: "select", valueType: "select",
request: async () => { valueEnum: {
const req = { "1": '异常',
ProvinceCode: currentUser?.provinceCode, "0": "正常"
StatisticsType: 1000
}
const data = await handleGetServerpartDDL(req)
return data
} }
}, },
{ {
title: <div style={{ textAlign: 'center' }}></div>, title: <div style={{ textAlign: 'center' }}></div>,
dataIndex: "serverPartName", dataIndex: "serverPartName",
hideInSearch: true, hideInSearch: true,
width: 150, width: 200,
ellipsis: true, ellipsis: true,
render: (_, record) => { render: (_, record) => {
return record?.template.serverPartName ? record?.template.serverPartName : "-" return record?.type === 'district' || record?.type === 'servicePart' ? record?.name :
record?.template ? record?.template.title : "-"
// return record?.name ? record?.name : record?.template && record?.serverPartName ? record?.serverPartName : "-"
} }
}, },
// {
// title: <div style={{ textAlign: 'center' }}>站点名称</div>,
// dataIndex: "placeName",
// hideInSearch: true,
// width: 150,
// ellipsis: true,
// render: (_, record) => {
// return record?.template && record?.template.title ? record?.template.title : "-"
// }
// },
{ {
title: <div style={{ textAlign: 'center' }}></div>, title: <div style={{ textAlign: 'center' }}></div>,
dataIndex: "placeName", dataIndex: "placeName",
hideInSearch: true, hideInSearch: true,
width: 150, width: 100,
align: 'center',
ellipsis: true, ellipsis: true,
render: (_, record) => { render: (_, record) => {
return record?.template.title ? record?.template.title : "-" let res: any = record.extend ? JSON.parse(record.extend) : "-"
return <span style={{ color: res.situation === 1 ? "red" : "" }}>{res.situation === 1 ? '异常' : res.situation === 0 ? '正常' : ''}</span>
} }
}, },
{ {
@ -111,6 +155,14 @@ const examineRecord: React.FC<{ currentUser: any }> = (props) => {
return str || '' return str || ''
} }
}, },
{
title: <div style={{ textAlign: 'center' }}></div>,
dataIndex: "score",
hideInSearch: true,
valueType: 'digit',
width: 100,
align: 'center',
},
{ {
title: <div style={{ textAlign: 'center' }}></div>, title: <div style={{ textAlign: 'center' }}></div>,
dataIndex: "createdAt", dataIndex: "createdAt",
@ -130,6 +182,26 @@ const examineRecord: React.FC<{ currentUser: any }> = (props) => {
ellipsis: true, ellipsis: true,
align: 'center', align: 'center',
}, },
{
title: <div style={{ textAlign: 'center' }}></div>,
dataIndex: "errorStatus",
hideInSearch: true,
width: 100,
ellipsis: true,
align: 'center',
render: (_, record) => {
let res: any = record.extend ? JSON.parse(record.extend) : "-"
return <span style={{ color: res.errorStatus === 0 ? "red" : res.errorStatus === 1 ? "#1677ff" : "" }}>{
res.errorStatus === 0
? "待处理"
: res.errorStatus === 1
? "处理中"
: res.errorStatus === 2
? "已处理"
: "-"
}</span>
}
},
{ {
title: <div style={{ textAlign: 'center' }}></div>, title: <div style={{ textAlign: 'center' }}></div>,
dataIndex: "placeName", dataIndex: "placeName",
@ -153,15 +225,33 @@ const examineRecord: React.FC<{ currentUser: any }> = (props) => {
align: 'center', align: 'center',
fixed: "right", fixed: "right",
hideInSearch: true, hideInSearch: true,
width: 100, width: 150,
render: (_: any, record: any) => { render: (_: any, record: any) => {
return <Space> let res: any = record.extend ? JSON.parse(record.extend) : "-"
<a onClick={() => {
setCurrentRow(record) return record?.type === 'district' || record?.type === 'servicePart' ? '' : <Space>
{
res.situation === 1 ?
<a onClick={
() => {
setCurrentRow({
...record,
...res
})
setShowAbnormal(true)
setShowDetail(true)
}
}></a> : ""
}
< a onClick={() => {
setCurrentRow({
...record,
...res
})
setShowDetail(true) setShowDetail(true)
}}> }}>
</a> </a >
<Popconfirm <Popconfirm
title={"确认删除?"} title={"确认删除?"}
onConfirm={async () => { onConfirm={async () => {
@ -171,7 +261,7 @@ const examineRecord: React.FC<{ currentUser: any }> = (props) => {
<a></a> <a></a>
</Popconfirm> </Popconfirm>
</Space> </Space >
} }
} }
] ]
@ -179,17 +269,20 @@ const examineRecord: React.FC<{ currentUser: any }> = (props) => {
// 删除记录 // 删除记录
const deleteRecord = async (id: any) => { const deleteRecord = async (id: any) => {
const data = await handleDeleteRecord({ id: id }) const data = await handleDeleteRecord({ id: id })
if (data.code === 200) { if (data.Result_Code === 100) {
message.success(data.message) message.success(data.Result_Desc)
actionRef.current?.reload() actionRef.current?.reload()
} }
} }
return ( return (
<div style={{ backgroundColor: '#fff', display: 'flex' }}> <div style={{ backgroundColor: '#fff', display: 'flex' }}>
<LeftSelectTree setSelectedId={setSelectedId} setCollapsible={setCollapsible} collapsible={collapsible} currentUser={currentUser} />
<div style={{ <div style={{
// width: !collapsible ? 'calc(100% - 300px)' : 'calc(100% - 60px)', width: !collapsible ? 'calc(100% - 300px)' : 'calc(100% - 60px)',
width: "100%", // width: "100%",
paddingTop: 0, paddingTop: 0,
paddingBottom: 0, paddingBottom: 0,
paddingRight: 0 paddingRight: 0
@ -202,19 +295,33 @@ const examineRecord: React.FC<{ currentUser: any }> = (props) => {
expandable={{ expandable={{
expandRowByClick: true expandRowByClick: true
}} }}
rowKey={(record) => {
return `${record?.id}-${record?.code}`
}}
scroll={{ x: "100%", y: 'calc(100vh - 400px)' }} scroll={{ x: "100%", y: 'calc(100vh - 400px)' }}
headerTitle={<span style={{ color: "#1890ff", fontSize: 14, fontWeight: 600 }}></span>} headerTitle={<span style={{ color: "#1890ff", fontSize: 14, fontWeight: 600 }}></span>}
search={{ span: 6 }} search={{ span: 6 }}
request={async (params) => { request={async (params) => {
// selectedId
console.log('params', params); console.log('params', params);
console.log('selectedId', selectedId);
if (!(selectedId && selectedId.length > 0)) {
return
}
const req: any = { const req: any = {
startTime: params?.startTime ? `${params?.startTime}T00:00:00` : "", serverPartIds: selectedId && selectedId.length > 0 ? selectedId : [],
endTime: params?.endTime ? `${params?.endTime}T23:59:59` : "", startTime: params?.staticDate ? `${params?.staticDate}` : "",
serverPartId: params?.serverPartId ? params?.serverPartId : "" endTime: params?.staticDate ? `${params?.staticDate}` : "",
// serverPartId: params?.serverPartId ? params?.serverPartId : undefined,
extend: params?.inspectionType ? [{
key: "situation",
value: params?.inspectionType
}] : undefined
} }
console.log('req', req); console.log('req', req);
const data = await handleGetRecordList(req) const data = await handleGetRecordTreeList(req)
console.log('data', data); console.log('data', data);
if (data && data.length > 0) { if (data && data.length > 0) {
@ -225,6 +332,10 @@ const examineRecord: React.FC<{ currentUser: any }> = (props) => {
toolbar={{ toolbar={{
}} }}
columnsState={{
value: columnsStateMap,
onChange: setColumnsStateMap,
}}
> >
</ProTable> </ProTable>
</div> </div>
@ -258,12 +369,92 @@ const examineRecord: React.FC<{ currentUser: any }> = (props) => {
closeIcon={false} closeIcon={false}
onClose={() => { onClose={() => {
setShowDetail(false) setShowDetail(false)
setShowAbnormal(false)
}} }}
open={showDetail} open={showDetail}
destroyOnClose destroyOnClose
width={'60%'} width={'60%'}
footer={showAbnormal && currentRow?.errorStatus !== 2 ? <div style={{ width: "100%", boxSizing: 'border-box', padding: "0 24px", display: 'flex', justifyContent: 'flex-end' }}>
<Button type="primary" onClick={() => {
if (recordDetailRef.current?.formRes.errorStatus === 0) {
recordDetailRef.current?.errorStatusFormRef.current?.validateFields().then(async (res: any) => {
console.log('res', res);
let extendObj = JSON.parse(recordDetailRef.current?.formRes.extend)
let personObj: any = {}
let personList: any = recordDetailRef.current?.selectPersonList
if (personList && personList.length > 0) {
personList.forEach((item: any) => {
if (item.STAFF_ID === res.personIndex) {
personObj = item
}
})
}
const req = {
...extendObj,
person: personObj,
errorStatus: 1,
suggestion: res.suggestion,
suggestTime: moment().format('YYYY-MM-DD'), // 选处理人的时间
suggestPerson: {
STAFF_NAME: currentUser.adminName,
STAFF_ID: currentUser.id,
MEMBERSHIP_NAME: currentUser.adminName,
MEMBERSHIP_ID: currentUser.id,
},
};
const data = await handleUpdateExtend(recordDetailRef.current?.formRes.id, { extend: JSON.stringify(req) })
if (data.Result_Code === 100) {
message.success('提交成功')
setShowDetail(false)
setShowAbnormal(false)
actionRef.current?.reload()
setCurrentRow(undefined)
} else {
message.error(data.Result_Desc)
}
})
}
if (recordDetailRef.current?.formRes.errorStatus === 1) {
recordDetailRef.current?.errorStatusFormReflast.current?.validateFields().then(async (res: any) => {
console.log('res', res);
let extendObj = JSON.parse(recordDetailRef.current?.formRes.extend)
let fileList: any = []
if (recordDetailRef.current?.fileList && recordDetailRef.current?.fileList.length > 0) {
recordDetailRef.current?.fileList.forEach((item: any) => {
fileList.push(item.url)
})
}
const req = {
...extendObj,
feedbackImgList: fileList,
feedbackContent: res.feedbackContent,
feedbackTime: moment().format('YYYY-MM-DD HH:mm:ss'), // 反馈时间
errorStatus: 2,
};
const data = await handleUpdateExtend(recordDetailRef.current?.formRes.id, { extend: JSON.stringify(req) })
if (data.Result_Code === 100) {
message.success('提交成功')
setShowDetail(false)
setShowAbnormal(false)
actionRef.current?.reload()
setCurrentRow(undefined)
} else {
message.error(data.Result_Desc)
}
})
}
}}></Button>
</div> : false}
> >
<RecordDetail parentRow={currentRow} show={showDetail} /> <RecordDetail onRef={recordDetailRef} parentRow={currentRow} show={showDetail} showError={showAbnormal} />
</Drawer> </Drawer>
</div> </div>
) )

View File

@ -1,10 +1,21 @@
import request from "@/utils/request" // import request from "@/utils/request"
import request from "@/utils/requestJava"
import requestMap from '@/utils/requestMap'
// 拿到记录 // 拿到记录
export async function handleGetRecordList(params?: any) { export async function handleGetRecordList(params?: any) {
const data = await request.post('/questionnaire-templates/search', params) const data = await request.post('/questionnaire-responses/search/many', params)
if (data.code === 200) { if (data.Result_Code === 100) {
return data.data return data.Result_Data.List
}
return []
}
// 拿到记录的树型结构
export async function handleGetRecordTreeList(params?: any) {
const data = await request.post('/questionnaire-responses/tree', params)
if (data.Result_Code === 100) {
return data.Result_Data.List
} }
return [] return []
} }
@ -17,5 +28,33 @@ export async function handleDeleteRecord(params?: any) {
} }
// 拿到处理人列表
export async function handleGetDealerList(params?: any) {
const data = await requestMap.get(`handler_ajax.ashx?SERVERPART_ID=${params?.SERVERPART_ID}&PROVINCE_CODE=${params?.PROVINCE_CODE}&action_type=GetDealerList`)
return data
}
// 删除记录
export async function handleUpdateExtend(id: any, params?: any) {
const data = await request.post(`questionnaire-responses/${id}/extend`, params)
return data
}
// 上传图片
export async function handleUploadFile(params?: any) {
const data = await request.post(`/oss/upload`, params)
return data
}
// 拿到考核记录的汇总数据
export async function handleGetResponsesSummary(params?: any) {
const data = await request.post(`/questionnaire-responses/statistic`, params)
if (data.Result_Code === 100) {
return data.Result_Data.List
}
return data
}

View File

@ -0,0 +1,674 @@
import { ConnectState } from "@/models/global";
import { ActionType, FormInstance, ProTable } from "@ant-design/pro-components";
import { useRef, useState } from "react";
import { connect } from "umi";
import moment from "moment";
import { Button, Drawer, Image, message, Popconfirm, Space } from "antd";
import LeftSelectTree from "@/components/leftSelectTree/leftSelectTree";
import { handleGetRecordTreeList, handleGetResponsesSummary } from "../record/service";
const recordSummary: React.FC<{ currentUser: any }> = (props) => {
const { currentUser } = props
const actionRef = useRef<ActionType>();
const formRef = useRef<FormInstance>();
const drawerActionRef = useRef<ActionType>();
const drawerFormRef = useRef<FormInstance>();
const recordDetailRef = useRef<any>()
// 显示的附件数据
const [showImgList, setShowImgList] = useState<string[]>([])
// 预览图片
const [imagePreviewVisible, setImagePreviewVisible] = useState<boolean>(false)
// 预览的索引
const [previewIndex, setPreviewIndex] = useState<number>(0)
// 当行数据
const [currentRow, setCurrentRow] = useState<any>()
// 显示详情抽屉
const [showDetail, setShowDetail] = useState<boolean>(false)
// 显示类型 1 巡查次数 2 正常 3 异常 4 待处理 5 处理中 6 已处理
const [showType, setShowType] = useState<number>(0)
// 外侧表格的搜索条件
const [searchParams, setSearchParams] = useState<any>()
// 判断是否点了出现的是异常处理的抽屉
const [showAbnormal, setShowAbnormal] = useState<boolean>(false)
// 树相关的属性和方法
const [selectedId, setSelectedId] = useState<string[]>()
const [columnsStateMap, setColumnsStateMap] = useState<any>({
score: { show: false }
})
const [collapsible, setCollapsible] = useState<boolean>(false)
const columns: any = [
{
title: "统计日期",
dataIndex: "staticDate",
hideInTable: true,
valueType: "dateRange",
initialValue: [moment().startOf('M'), moment()],
search: {
transform: (value: any) => {
return {
startTime: moment(value[0]).startOf('m').format('YYYY-MM-DD'),
endTime: moment(value[1]).format('YYYY-MM-DD')
};
},
},
fieldProps: {
picker: "day",
format: 'YYYY-MM-DD',
}
},
{
title: <div style={{ textAlign: 'center' }}></div>,
dataIndex: "serverPartName",
hideInSearch: true,
width: 200,
ellipsis: true,
render: (_, record) => {
return record?.type === 'district' || record?.type === 'servicePart' ? record?.name :
record?.template ? record?.template.title : "-"
}
},
{
title: <div style={{ textAlign: 'center' }}></div>,
dataIndex: "inspectionNumber",
align: 'center',
hideInSearch: true,
width: 150,
ellipsis: true,
render: (_, record) => {
return record?.template?.id && record?.inspectionNumber > 0 ? <span>
<a style={{ color: record?.inspectionNumber === record?.allDay ? '' : 'red' }} onClick={() => {
setCurrentRow(record)
setShowType(1)
setShowDetail(true)
}}>
{record?.inspectionNumber || '-'}
</a> / {record?.allDay}
</span > : <span><span style={{ color: record?.inspectionNumber === record?.allDay ? '' : 'red' }}>{record?.inspectionNumber || '-'} </span><span>/ {record?.allDay}</span></span>
}
},
{
title: <div style={{ textAlign: 'center' }}></div>,
dataIndex: "inspectionRate",
align: 'center',
hideInSearch: true,
width: 150,
ellipsis: true,
render: (_, record) => {
return record?.allDay ?
<span style={{ color: record?.inspectionNumber === record?.allDay ? '' : 'red' }}>
{((record?.inspectionNumber / record?.allDay) * 100).toFixed(2) + '%'}
</span> : ''
}
},
{
title: <div style={{ textAlign: 'center' }}></div>,
dataIndex: "normalNumber",
align: 'center',
hideInSearch: true,
width: 150,
ellipsis: true,
render: (_, record) => {
return record?.template?.id && record?.normalNumber > 0 ? <a onClick={() => {
setCurrentRow(record)
setShowType(2)
setShowDetail(true)
}}>
{record?.normalNumber || '-'}
</a> : <span>{record?.normalNumber || '-'}</span>
}
},
{
title: <div style={{ textAlign: 'center' }}></div>,
dataIndex: "errorNumber",
align: 'center',
hideInSearch: true,
width: 150,
ellipsis: true,
render: (_, record) => {
return record?.template?.id && record?.errorNumber > 0 ? <a onClick={() => {
setCurrentRow(record)
setShowType(3)
setShowDetail(true)
}}>
{record?.errorNumber || '-'}
</a> : <span>{record?.errorNumber || '-'}</span>
}
},
{
title: <div style={{ textAlign: 'center' }}></div>,
dataIndex: "pendingProcessNumber",
align: 'center',
hideInSearch: true,
width: 150,
ellipsis: true,
render: (_, record) => {
return record?.template?.id && record?.pendingProcessNumber > 0 ? <a onClick={() => {
setCurrentRow(record)
setShowType(4)
setShowDetail(true)
}}>
{record?.pendingProcessNumber || '-'}
</a> : <span>{record?.pendingProcessNumber || '-'}</span>
}
},
{
title: <div style={{ textAlign: 'center' }}></div>,
dataIndex: "processingNumber",
align: 'center',
hideInSearch: true,
width: 150,
ellipsis: true,
render: (_, record) => {
return record?.template?.id && record?.processingNumber > 0 ? <a onClick={() => {
setCurrentRow(record)
setShowType(5)
setShowDetail(true)
}}>
{record?.processingNumber || '-'}
</a> : <span>{record?.processingNumber || '-'}</span>
}
},
{
title: <div style={{ textAlign: 'center' }}></div>,
dataIndex: "processedNumber",
align: 'center',
hideInSearch: true,
width: 150,
ellipsis: true,
render: (_, record) => {
return record?.template?.id && record?.processedNumber > 0 ? <a onClick={() => {
setCurrentRow(record)
setShowType(6)
setShowDetail(true)
}}>
{record?.processedNumber || '-'}
</a> : <span>{record?.processedNumber || '-'}</span>
}
},
{
title: <div style={{ textAlign: 'center' }}></div>,
dataIndex: "processedRate",
align: 'center',
hideInSearch: true,
width: 150,
ellipsis: true,
render: (_, record) => {
return record?.errorNumber ? <span style={{ color: record?.processedNumber === record?.errorNumber ? '' : 'red' }}>
{((record?.processedNumber / record?.errorNumber) * 100).toFixed(2)}%
</span> : ""
}
}
]
const drawerColumns: any = [
// {
// title: "巡查类型",
// dataIndex: "inspectionType",
// hideInTable: true,
// valueType: "select",
// valueEnum: {
// "1": '异常',
// "0": "正常"
// }
// },
{
title: <div style={{ textAlign: 'center' }}></div>,
dataIndex: "createdAt",
hideInSearch: true,
width: 150,
ellipsis: true,
fixed: 'left',
align: 'center',
render: (_, record) => {
return record?.createdAt ? moment(record?.createdAt).format('YYYY-MM-DD HH:mm:ss') : '-'
}
},
{
title: <div style={{ textAlign: 'center' }}></div>,
dataIndex: "serverPartName",
hideInSearch: true,
width: 200,
ellipsis: true,
render: (_, record) => {
return record?.type === 'district' || record?.type === 'servicePart' ? record?.name :
record?.template ? record?.template.title : "-"
}
},
{
title: <div style={{ textAlign: 'center' }}></div>,
dataIndex: "placeName",
hideInSearch: true,
width: 100,
align: 'center',
ellipsis: true,
render: (_, record) => {
let res: any = record.extend ? JSON.parse(record.extend) : "-"
return <span style={{ color: res.situation === 1 ? "red" : "" }}>{res.situation === 1 ? '异常' : res.situation === 0 ? '正常' : ''}</span>
}
},
{
title: <div style={{ textAlign: 'center' }}></div>,
dataIndex: "uploadResult",
hideInSearch: true,
width: 200,
ellipsis: true,
render: (_, record) => {
let extendObj = record?.extend ? JSON.parse(record?.extend) : ""
return extendObj?.uploadResult ? extendObj?.uploadResult : "-"
}
},
{
title: <div style={{ textAlign: 'center' }}></div>,
dataIndex: "uploadResult",
hideInSearch: true,
width: 350,
ellipsis: true,
render: (_, record) => {
let str: string = ''
if (record?.questionResponses && record?.questionResponses.length > 0) {
record?.questionResponses.forEach((item: any, index: number) => {
let anwers: string = ''
if (item.choiceResponse && item.choiceResponse.length > 0) {
item.choiceResponse.forEach((subItem: string, subIndex: number) => {
anwers += `${subIndex > 0 ? '' : ''}${subItem}`
})
}
str += `${index > 0 ? '' : ''}考核内容:${item.question.title},考核结果:${anwers}`
})
}
return str || ''
}
},
{
title: <div style={{ textAlign: 'center' }}></div>,
dataIndex: "score",
hideInSearch: true,
valueType: 'digit',
width: 100,
align: 'center',
},
{
title: <div style={{ textAlign: 'center' }}></div>,
dataIndex: "userName",
hideInSearch: true,
width: 100,
ellipsis: true,
align: 'center',
},
{
title: <div style={{ textAlign: 'center' }}></div>,
dataIndex: "errorStatus",
hideInSearch: true,
width: 100,
ellipsis: true,
align: 'center',
render: (_, record) => {
let res: any = record.extend ? JSON.parse(record.extend) : "-"
return <span style={{ color: res.errorStatus === 0 ? "red" : res.errorStatus === 1 ? "#1677ff" : "" }}>{
res.errorStatus === 0
? "待处理"
: res.errorStatus === 1
? "处理中"
: res.errorStatus === 2
? "已处理"
: "-"
}</span>
}
},
{
title: <div style={{ textAlign: 'center' }}></div>,
dataIndex: "placeName",
hideInSearch: true,
width: 150,
ellipsis: true,
align: 'center',
render: (_, record) => {
let extendObj = record?.extend ? JSON.parse(record?.extend) : ""
let imgList = extendObj.imgsList
return imgList && imgList.length > 0 ?
<Button type="primary" onClick={() => {
setShowImgList(imgList)
setImagePreviewVisible(true)
}}></Button> : "-"
}
}
]
return (
<div style={{ backgroundColor: '#fff', display: 'flex' }}>
<LeftSelectTree setSelectedId={setSelectedId} setCollapsible={setCollapsible} collapsible={collapsible} currentUser={currentUser} />
<div style={{
width: !collapsible ? 'calc(100% - 300px)' : 'calc(100% - 60px)',
// width: "100%",
paddingTop: 0,
paddingBottom: 0,
paddingRight: 0
}}>
<ProTable
actionRef={actionRef}
formRef={formRef}
columns={columns}
bordered
expandable={{
expandRowByClick: true,
defaultExpandAllRows: true
}}
rowKey={(record) => {
return `${record?.id}-${record?.code}`
}}
scroll={{ x: "100%", y: 'calc(100vh - 400px)' }}
headerTitle={<span style={{ color: "#1890ff", fontSize: 14, fontWeight: 600 }}></span>}
search={{ span: 6 }}
request={async (params) => {
if (!(selectedId && selectedId.length > 0)) {
return
}
const req: any = {
serverPartIds: selectedId && selectedId.length > 0 ? selectedId : [],
startTime: params?.startTime ? `${params?.startTime}` : "",
endTime: params?.endTime ? `${params?.endTime}` : "",
// serverPartId: params?.serverPartId ? params?.serverPartId : undefined,
extend: params?.inspectionType ? [{
key: "situation",
value: params?.inspectionType
}] : undefined
}
let allDay: number = moment(params?.endTime).diff(params?.startTime, 'd') + 1
console.log('allDay', allDay);
setSearchParams(params)
console.log('req', req);
// const data = await handleGetRecordTreeList(req)
const data = await handleGetResponsesSummary(req)
console.log('dat3a333', data);
// 处理一下 根据第三层的实际巡查记录 汇总到第二层来
if (data && data.length > 0) {
data.forEach((item: any) => {
if (item.children && item.children.length > 0) {
item.children.forEach((subItem: any) => {
if (subItem.children && subItem.children.length > 0) {
// 点位的id数据 肯定要求不重复的
let templateIdList: number[] = []
// 遍历完之后 要替换的
let newChildren: any = []
// 因为一天时间内 多条记录 需要被计算为一条 那么这里就要加上时间的对象来筛选
let dateObj: any = {}
subItem.children.forEach((thirdItem: any) => {
let extendObj: any = {}
if (thirdItem.extend) {
extendObj = JSON.parse(thirdItem.extend)
}
let oldDateList = dateObj[moment(thirdItem.createdAt).format('YYYY-MM-DD')]
if (!oldDateList || (oldDateList && oldDateList.indexOf(thirdItem.template.id) === -1)) {
// templateIdList 是判断 newChildren 里面已经有几个点位id了
if (templateIdList.indexOf(thirdItem.template.id) === -1) {
templateIdList.push(thirdItem.template.id)
newChildren.push({
template: thirdItem.template,
templateId: thirdItem.template.Id,
inspectionNumber: 1,
normalNumber: extendObj?.situation === 0 ? 1 : 0,
errorNumber: extendObj?.situation === 1 ? 1 : 0,
pendingProcessNumber: extendObj?.errorStatus === 0 ? 1 : 0,
processingNumber: extendObj?.errorStatus === 1 ? 1 : 0,
processedNumber: extendObj?.errorStatus === 2 ? 1 : 0,
})
} else {
newChildren.forEach((fourthItem: any) => {
if (fourthItem.template.id === thirdItem.template.id) {
fourthItem.inspectionNumber += 1
if (extendObj?.situation === 0) {
fourthItem.normalNumber += 1
} else if (extendObj?.situation === 1) {
fourthItem.errorNumber += 1
}
if (extendObj?.errorStatus === 0) {
fourthItem.pendingProcessNumber += 1
} else if (extendObj?.errorStatus === 1) {
fourthItem.processingNumber += 1
} else if (extendObj?.errorStatus === 2) {
fourthItem.processedNumber += 1
}
}
})
}
if (oldDateList && oldDateList.length > 0) {
oldDateList.push(thirdItem.template.id)
} else {
dateObj[moment(thirdItem.createdAt).format('YYYY-MM-DD')] = [thirdItem.template.id]
}
}
console.log('dateObj', dateObj);
})
subItem.children = newChildren
}
})
}
})
data.forEach((item: any) => {
let inspectionNumberItemSum: number = 0
let normalNumberItemSum: number = 0
let errorNumberItemSum: number = 0
let pendingProcessNumberItemSum: number = 0
let processingNumberItemSum: number = 0
let processedNumberItemSum: number = 0
// 这一次时间段应该查询的次数(天数)
let allItemDaySum: number = 0
if (item.children && item.children.length > 0) {
item.children.forEach((subItem: any) => {
let inspectionNumberSum: number = 0
let normalNumberSum: number = 0
let errorNumberSum: number = 0
let pendingProcessNumberSum: number = 0
let processingNumberSum: number = 0
let processedNumberSum: number = 0
// 这一次时间段应该查询的次数(天数)
let allSubItemDaySum: number = 0
if (subItem.children && subItem.children.length > 0) {
subItem.children.forEach((thirdItem: any) => {
thirdItem.allDay = allDay
allSubItemDaySum += thirdItem.allDay
inspectionNumberSum += thirdItem.inspectionNumber
normalNumberSum += thirdItem.normalNumber
errorNumberSum += thirdItem.errorNumber
pendingProcessNumberSum += thirdItem.pendingProcessNumber
processingNumberSum += thirdItem.processingNumber
processedNumberSum += thirdItem.processedNumber
})
}
subItem.allDay = allSubItemDaySum
subItem.inspectionNumber = inspectionNumberSum
subItem.normalNumber = normalNumberSum
subItem.errorNumber = errorNumberSum
subItem.pendingProcessNumber = pendingProcessNumberSum
subItem.processingNumber = processingNumberSum
subItem.processedNumber = processedNumberSum
inspectionNumberItemSum += subItem.inspectionNumber
normalNumberItemSum += subItem.normalNumber
errorNumberItemSum += subItem.errorNumber
pendingProcessNumberItemSum += subItem.pendingProcessNumber
processingNumberItemSum += subItem.processingNumber
processedNumberItemSum += subItem.processedNumber
allItemDaySum += subItem.allDay
})
item.allDay = allItemDaySum
item.inspectionNumber = inspectionNumberItemSum
item.normalNumber = normalNumberItemSum
item.errorNumber = errorNumberItemSum
item.pendingProcessNumber = pendingProcessNumberItemSum
item.processingNumber = processingNumberItemSum
item.processedNumber = processedNumberItemSum
}
})
}
console.log('resresres', data);
if (data && data.length > 0) {
return { data, success: true }
}
return { data: [], success: true }
}}
toolbar={{
}}
columnsState={{
value: columnsStateMap,
onChange: setColumnsStateMap,
}}
>
</ProTable>
</div>
{
showImgList && showImgList.length > 0 && <div style={{ display: 'none' }}>
<Image.PreviewGroup
preview={{
visible: imagePreviewVisible,
onVisibleChange: vis => {
setImagePreviewVisible(vis)
},
current: previewIndex
}}>
{
showImgList.map((n) =>
<Image src={n} key={n} />
)
}
</Image.PreviewGroup>
</div>
}
<Drawer
title={false}
closeIcon={false}
onClose={() => {
setCurrentRow(undefined)
setShowType(0)
setShowDetail(false)
setShowImgList([])
setImagePreviewVisible(false)
}}
open={showDetail}
destroyOnClose
width={'60%'}
>
<ProTable
actionRef={drawerActionRef}
formRef={drawerFormRef}
columns={drawerColumns}
bordered
expandable={{
expandRowByClick: true,
defaultExpandAllRows: true
}}
rowKey={(record) => {
return `${record?.id}-${record?.code}`
}}
scroll={{ x: "100%", y: 'calc(100vh - 400px)' }}
headerTitle={<span style={{ color: "#1890ff", fontSize: 14, fontWeight: 600 }}></span>}
search={{ span: 6 }}
request={async (params) => {
console.log('searchParams', searchParams);
console.log('currentRow', currentRow);
if (!currentRow?.template?.serverPartId) {
return
}
// showType 1 巡查次数 2 正常 3 异常 4 待处理 5 处理中 6 已处理
const req: any = {
serverPartIds: [currentRow?.template?.serverPartId],
startTime: searchParams?.startTime ? `${searchParams?.startTime}T00:00:00` : "",
endTime: searchParams?.endTime ? `${searchParams?.endTime}T23:59:59` : "",
extend: showType === 1 ? undefined : showType === 2 ? [
{
key: "situation",
value: 0,
},
] : showType === 3 ? [
{
key: "situation",
value: 1,
},
] : showType === 4 ? [
{
key: "errorStatus",
value: 0,
}
] :
showType === 5 ? [
{
key: "errorStatus",
value: 1,
}
] :
showType === 6 ? [
{
key: "errorStatus",
value: 2,
}
] : undefined
}
console.log('req', req);
const data = await handleGetRecordTreeList(req)
console.log('data', data);
if (data && data.length > 0) {
// 最后结果直接输出第三层的数据 好看点
let res: any = []
data.forEach((item: any) => {
if (item.children && item.children.length > 0) {
item.children.forEach((subItem: any) => {
if (subItem.children && subItem.children.length > 0) {
subItem.children.forEach((thirdItem: any) => {
res.push(thirdItem)
})
}
})
}
})
return { data: res, success: true }
}
return { data: [], success: true }
}}
>
</ProTable>
</Drawer>
</div>
)
}
export default connect(({ user }: ConnectState) => ({
currentUser: user.data
}))(recordSummary);

View File

@ -0,0 +1,213 @@
import { ProForm, ProFormDigit, ProFormRadio, ProFormText, ProFormTextArea, ProFormTreeSelect } from "@ant-design/pro-components";
import { Col, FormInstance, message, Modal, Row } from "antd";
import { useRef, useState } from "react";
import { connect } from "umi";
import { ConnectState } from "@/models/global";
import { handleAddMenus, handleGetMenuList } from "../service";
type DetailProps = {
currentUser?: any // 用户详情的公参
showDrawer: boolean // 显示悬浮框的判断
parentRow?: any // 点击的行 编辑的时候有用
setShowDrawer: any // 改变悬浮框现实状态
parentTableRef?: any // 父级表格实例
}
const AddMenu = ({ currentUser, showDrawer, parentRow, setShowDrawer, parentTableRef }: DetailProps) => {
console.log('showDrawer', showDrawer);
// 表单实例
const formRef = useRef<FormInstance>();
// 当前的菜单类型
const [currentMenuType, setCurrentMenuType] = useState<number>(1)
return (
<Modal
title={parentRow?.id ? '更新菜单' : '新建菜单'}
width={600}
open={showDrawer}
destroyOnClose
onOk={() => {
formRef.current?.validateFields().then(async (res: any) => {
console.log('res', res);
const req: any = {
parentId: res.parentId || 1,
menuCode: res.menuCode,
menuName: res.menuName,
menuIcon: res.menuIcon || "",
menuPath: res.menuPath || "",
component: "",
permission: "",
menuType: res.menuType,
sortOrder: res.sortOrder,
hidden: res.hidden === 1 ? false : res.hidden === 2 ? true : '',
operator: currentUser.adminName
}
const data = await handleAddMenus(req)
console.log('datadsa', data);
if (data.code === 200) {
message.success(data.message)
if (parentTableRef) {
parentTableRef.current.reload()
}
formRef.current?.resetFields()
setShowDrawer(false)
}
})
}}
onCancel={() => {
setShowDrawer(false)
}}
>
<ProForm
formRef={formRef}
submitter={false}
layout="horizontal"
labelCol={{ span: 6 }}
>
<Row>
<Col span={24}>
<ProFormText
label={currentMenuType === 1 ? "菜单名称" : "模块名称"}
name={"menuName"}
rules={[
{ required: true, message: `请输入${currentMenuType === 1 ? '菜单' : '模块'}名称` }
]}
/>
</Col>
<Col span={24}>
<ProFormText
label={currentMenuType === 1 ? "菜单编码" : "模块编码"}
name={"menuCode"}
rules={[
{ required: true, message: `请输入${currentMenuType === 1 ? '菜单' : '模块'}编码` }
]}
/>
</Col>
<Col span={24}>
<ProFormTreeSelect
label={"上级菜单"}
name={"parentId"}
request={async () => {
const req: any = {
}
const data = await handleGetMenuList()
let res: any = []
if (data && data.length > 0) {
res = data
}
return res
}}
fieldProps={{
fieldNames: {
label: "menuName",
value: "id"
}
}}
/>
</Col>
{
currentMenuType === 1 ?
<Col span={24}>
<ProFormText
name="menuIcon"
label="菜单图标"
placeholder="请输入菜单图标字段"
initialValue={"<ProfileOutlined />"}
fieldProps={
{
addonAfter: <a href='https://ant.design/components/icon-cn/' target='_blank'></a>
}
}
/>
</Col> : ""
}
{
currentMenuType === 2 ?
<Col span={24}>
<ProFormText
name="menuPath"
label="模块地址"
placeholder="请输入模块地址"
/>
</Col> : ''
}
<Col span={24}>
<ProFormDigit
name="sortOrder"
label="索引"
placeholder="请输入索引"
min={0}
max={9999}
fieldProps={{ precision: 0 }}
/>
</Col>
<Col span={24}>
<ProFormRadio.Group
name="hidden"
label="状态"
initialValue={1}
options={[
{
label: '有效',
value: 1,
},
{
label: '隐藏',
value: 2,
}]}
/>
</Col>
<Col span={24}>
<ProFormRadio.Group
name="menuType"
label="菜单类型"
initialValue={1}
rules={[
{ required: true, message: "请选择菜单类型" }
]}
options={[
{
label: '菜单',
value: 1,
},
{
label: '模块',
value: 2,
}
]}
fieldProps={{
onChange: (e: any) => {
console.log('e', e);
setCurrentMenuType(Number(e.target.value))
}
}}
/>
</Col>
{/* <Col span={24}>
<ProFormTextArea
name="DESC"
label="备注说明"
placeholder="请输入说明"
/>
</Col> */}
</Row>
</ProForm>
</Modal>
);
}
export default connect(({ user }: ConnectState) => ({
currentUser: user.data
}))(AddMenu);

View File

@ -0,0 +1,191 @@
import { ActionType, FormInstance, ProTable } from "@ant-design/pro-components";
import { Button, Space } from "antd";
import { useRef, useState } from "react";
import { connect } from "umi";
import { handleGetMenuList } from "./service";
import AddMenu from "./components/addMenu";
import moment from "moment";
const menuIndex: React.FC<{ currentUser: any }> = (props) => {
const { currentUser } = props
const actionRef = useRef<ActionType>();
const formRef = useRef<FormInstance>();
// 打开新增的悬浮框
const [openAddModal, setOpenAddModal] = useState<boolean>(false)
// 行数据
const [currentRow, setCurrentRow] = useState<any>()
// 表格默认隐藏字段
const [columnsStateMap, setColumnsStateMap] = useState<any>({
menuCode: { show: false },
createdAt: { show: false },
})
// 表格组件
const columns: any = [
{
title: <div style={{ textAlign: 'center' }}></div>,
dataIndex: "menuName",
width: 300,
hideInSearch: true,
},
{
title: <div style={{ textAlign: 'center' }}></div>,
dataIndex: "menuCode",
width: 300,
hideInSearch: true,
},
{
title: <div style={{ textAlign: 'center' }}></div>,
dataIndex: "menuIcon",
width: 120,
hideInSearch: true,
},
{
title: <div style={{ textAlign: 'center' }}></div>,
dataIndex: "sortOrder",
align: 'center',
width: 100,
hideInSearch: true,
},
{
title: <div style={{ textAlign: 'center' }}></div>,
dataIndex: 'hidden',
hideInSearch: true,
width: 100,
render: (_, record) => {
return record?.hidden ? '隐藏' : '显示'
}
},
{
title: <div style={{ textAlign: 'center' }}></div>,
dataIndex: 'createdAt',
width: 200,
hideInSearch: true,
render: (_, record) => {
return record?.createdAt ? moment(record?.createdAt).format('YYYY-MM-DD HH:mm:ss') : ""
}
},
{
title: <div style={{ textAlign: 'center' }}></div>,
dataIndex: 'option',
valueType: 'option',
hideInDescriptions: true,
width: 100,
render: (_, record) => {
return <Space>
</Space>
}
}
]
// 表格子集的columns
const childrenColumns: any = [
{
title: "模块名称",
dataIndex: "menuName",
width: 300,
hideInSearch: true,
},
{
title: "模块地址",
dataIndex: "",
width: 120,
hideInSearch: true,
},
{
title: "模块索引",
dataIndex: "id",
width: 180,
hideInSearch: true,
},
{
title: '状态',
dataIndex: 'hidden',
hideInSearch: true,
width: 180,
},
{
title: '说明',
dataIndex: 'desc',
width: 200,
hideInSearch: true
},
{
title: '操作',
dataIndex: 'option',
valueType: 'option',
hideInDescriptions: true,
width: 260,
render: (_, record) => { }
}
]
// 模块列表
const expandedRowRender = (data: any) => {
console.log('data', data);
return (data && data.length > 0 ?
<ProTable
rowKey="id"
columns={childrenColumns}
search={false}
headerTitle={false}
options={false}
dataSource={data}
pagination={false}
/>
: [])
}
return (
<div>
<ProTable
actionRef={actionRef}
formRef={formRef}
columns={columns}
bordered
rowKey={(record) => {
return `${record?.id}`
}}
scroll={{ x: "100%", y: 'calc(100vh - 400px)' }}
headerTitle={<span style={{ color: "#1890ff", fontSize: 14, fontWeight: 600 }}></span>}
request={async (params) => {
const req: any = {
}
const data = await handleGetMenuList()
console.log('data', data);
if (data && data.length > 0) {
return { data, success: true }
}
return { data: [], success: true }
}}
toolbar={{
actions: [
<Button type="primary" onClick={(e) => {
setOpenAddModal(true)
}}>
</Button>
]
}}
expandable={{
expandRowByClick: true,
// expandedRowRender
}}
columnsState={{
value: columnsStateMap,
onChange: setColumnsStateMap,
}}
/>
<AddMenu currentUser={currentUser} showDrawer={openAddModal} parentRow={currentRow} setShowDrawer={setOpenAddModal} parentTableRef={actionRef} />
</div>
)
}
export default connect(({ user }: ConnectState) => ({
currentUser: user.data
}))(menuIndex);

View File

@ -0,0 +1,16 @@
import request from "@/utils/request"
// 拿到菜单列表数据
export async function handleGetMenuList(params?: any) {
const data = await request.get('/menus/getAll', { params })
if (data.code === 200) {
return data.data
}
return []
}
// 新增菜单
export async function handleAddMenus(params?: any) {
const data = await request.post('/menus', params)
return data
}

View File

@ -12,7 +12,9 @@ const { UMI_APP_BASEURL } = process.env;
// const instance = axios.create({ baseURL: 'http://home.robot-z.cn:7001/' }); // const instance = axios.create({ baseURL: 'http://home.robot-z.cn:7001/' });
// 修改baseURL为完整的API地址确保在生产环境中正确访问 // 修改baseURL为完整的API地址确保在生产环境中正确访问
const instance = axios.create({ baseURL: 'https://es.robot-z.cn' }); // const instance = axios.create({ baseURL: 'https://es.robot-z.cn' });
const instance = axios.create({ baseURL: 'https://es.eshangtech.com' });

159
src/utils/requestJava.ts Normal file
View File

@ -0,0 +1,159 @@
import axios from 'axios';
import { getDvaApp } from 'umi';
import { notification } from 'antd';
import type { AxiosRequestHeaders } from 'axios/index';
import CryptoJS from "crypto-js";
const { UMI_APP_BASEURL } = process.env;
// const instance = axios.create({ baseURL: UMI_APP_BASEURL });
// const instance = axios.create({ baseURL: 'https://api.eshangtech.com/EShangApiMain' });
// const instance = axios.create({ baseURL: 'http://home.robot-z.cn:7001/' });
// 修改baseURL为完整的API地址确保在生产环境中正确访问
// const instance = axios.create({ baseURL: 'https://es.robot-z.cn' });
const instance = axios.create({ baseURL: 'http://10.104.1.161:8070/platform' });
instance.interceptors.request.use(
(config) => {
// 对data数据进行加密
// if (config.data) {
// config.data = preprocessData(JSON.stringify(config.data)); // 调用预处理函数
//
const isUpload = config.url?.includes("/oss/upload");
let token = localStorage.getItem('Authorization') || ''
config.headers = {
...config.headers,
Authorization: token ? `Bearer ${token}` : "",
"Content-Type": isUpload ? "multipart/form-data" : "application/json;charset=utf-8",
} as AxiosRequestHeaders;
return config;
},
(error) => Promise.reject(error),
);
instance.interceptors.response.use(
//状态码为2xx的时候执行
(response) => {
const { data } = response;
if (data.Result_Code !== 100) {
notification.error({
message: data.message,
});
}
const timestamp = getFormattedDate()
return data
},
//状态码不是2xx的时候执行
(error) => {
const { response } = error;
if (response && response.status === 401) {
// // 清除本地存储的token
// localStorage.removeItem('Authorization');
// // 重定向到登录页
// window.location.href = '/user/login';
// notification.error({
// message: response?.data?.message || '请求失败',
// description: error.message
// });
} else {
notification.error({
message: response?.data?.message || '请求失败',
description: error.message
});
}
return Promise.reject({
Result_Code: response?.status || 500,
Result_Desc: response?.data?.message || '请求失败'
});
},
);
// 加密
const encryptAESECB = (data: string, key: string) => {
// const cipher = CryptoJS.createCipheriv('aes-128-ecb', key, null); // ECB 模式不需要 IV
const newKey = CryptoJS.enc.Utf8.parse(key); // 密钥必须是 16 字节
const cipher = CryptoJS.AES.encrypt(data, newKey, {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7
});
let encrypted = cipher.ciphertext.toString(CryptoJS.enc.Hex);
// let encrypted = cipher.update(data, 'utf8', 'hex');
// encrypted += cipher.final('hex');
return encrypted;
}
// 解密
const decryptAESECB = (data: string, key: string) => {
// const decipher = CryptoJS.createDecipheriv('aes-128-ecb', key, null);
// let decrypted = decipher.update(data, 'hex', 'utf8');
// decrypted += decipher.final('utf8');
const newKey = CryptoJS.enc.Utf8.parse(key);
const encryptedData = CryptoJS.enc.Hex.parse(data);
// 解密操作
const decrypted = CryptoJS.AES.decrypt({ ciphertext: encryptedData }, newKey, {
mode: CryptoJS.mode.ECB, // ECB 模式
padding: CryptoJS.pad.Pkcs7 // PKCS7 填充方式
});
// 将解密后的结果转为 UTF-8 字符串
const decryptedText = decrypted.toString(CryptoJS.enc.Utf8);
return decryptedText;
}
// md5 签名
const md5 = (key: string, data: string, timestamp: string) => {
const text = "s" + key + data + timestamp;
return CryptoJS.MD5(text).toString(CryptoJS.enc.Hex);
}
// 生成签名戳
const getFormattedDate = () => {
const date = new Date();
const year = date.getFullYear(); // 获取年份 (yyyy)
const month = String(date.getMonth() + 1).padStart(2, '0'); // 获取月份 (MM)
const day = String(date.getDate()).padStart(2, '0'); // 获取日期 (dd)
const hours = String(date.getHours()).padStart(2, '0'); // 获取小时 (HH)
return `es0${year}${month}${day}${hours}0es`; // 拼接成 yyyyMMddHH 格式
}
// 加密方法
const preprocessData = (data: string) => {
console.log('data', data);
// YYYYMMDD
let timestamp = getFormattedDate()
console.log('timestamp', timestamp);
// 秒为单位的时间戳
let timeSecond = parseInt((new Date().getTime() / 1000).toString())
console.log('timeSecond', timeSecond);
// 数据的加密
let encryptionData = encryptAESECB(data, timestamp)
console.log('encryptionData', encryptionData);
// md5签名方法
let md5Data = md5(timestamp, encryptionData, timestamp)
console.log('md5Data', md5Data);
let res = {
data: encryptionData,
timestamp: timeSecond,
sign: md5Data
}
console.log('res', res);
return res
}
export default instance;

158
src/utils/requestMap.ts Normal file
View File

@ -0,0 +1,158 @@
import axios from 'axios';
import { getDvaApp } from 'umi';
import { notification } from 'antd';
import type { AxiosRequestHeaders } from 'axios/index';
import CryptoJS from "crypto-js";
const { UMI_APP_BASEURL } = process.env;
// const instance = axios.create({ baseURL: UMI_APP_BASEURL });
// const instance = axios.create({ baseURL: 'https://api.eshangtech.com/EShangApiMain' });
// const instance = axios.create({ baseURL: 'http://home.robot-z.cn:7001/' });
// 修改baseURL为完整的API地址确保在生产环境中正确访问
const instance = axios.create({ baseURL: 'https://mp.eshangtech.com/Coop.Merchant/Handler/' });
instance.interceptors.request.use(
(config) => {
// 对data数据进行加密
// if (config.data) {
// config.data = preprocessData(JSON.stringify(config.data)); // 调用预处理函数
// }
console.log('config', config);
const isUpload = config.url?.includes("/oss/upload");
config.headers = {
...config.headers,
Authorization: `Bearer ${localStorage.getItem('Authorization') || ''}`,
"Content-Type": "text/plain; charset=utf-8",
} as AxiosRequestHeaders;
return config;
},
(error) => Promise.reject(error),
);
instance.interceptors.response.use(
//状态码为2xx的时候执行
(response) => {
const { data } = response;
if (data.code !== 200) {
// notification.error({
// message: data.message,
// });
}
const timestamp = getFormattedDate()
return data
},
//状态码不是2xx的时候执行
(error) => {
const { response } = error;
if (response && response.status === 401) {
// // 清除本地存储的token
// localStorage.removeItem('Authorization');
// // 重定向到登录页
// window.location.href = '/user/login';
// notification.error({
// message: response?.data?.message || '请求失败',
// description: error.message
// });
} else {
// notification.error({
// message: response?.data?.message || '请求失败',
// description: error.message
// });
}
return Promise.reject({
code: response?.status || 500,
message: response?.data?.message || '请求失败'
});
},
);
// 加密
const encryptAESECB = (data: string, key: string) => {
// const cipher = CryptoJS.createCipheriv('aes-128-ecb', key, null); // ECB 模式不需要 IV
const newKey = CryptoJS.enc.Utf8.parse(key); // 密钥必须是 16 字节
const cipher = CryptoJS.AES.encrypt(data, newKey, {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7
});
let encrypted = cipher.ciphertext.toString(CryptoJS.enc.Hex);
// let encrypted = cipher.update(data, 'utf8', 'hex');
// encrypted += cipher.final('hex');
return encrypted;
}
// 解密
const decryptAESECB = (data: string, key: string) => {
// const decipher = CryptoJS.createDecipheriv('aes-128-ecb', key, null);
// let decrypted = decipher.update(data, 'hex', 'utf8');
// decrypted += decipher.final('utf8');
const newKey = CryptoJS.enc.Utf8.parse(key);
const encryptedData = CryptoJS.enc.Hex.parse(data);
// 解密操作
const decrypted = CryptoJS.AES.decrypt({ ciphertext: encryptedData }, newKey, {
mode: CryptoJS.mode.ECB, // ECB 模式
padding: CryptoJS.pad.Pkcs7 // PKCS7 填充方式
});
// 将解密后的结果转为 UTF-8 字符串
const decryptedText = decrypted.toString(CryptoJS.enc.Utf8);
return decryptedText;
}
// md5 签名
const md5 = (key: string, data: string, timestamp: string) => {
const text = "s" + key + data + timestamp;
return CryptoJS.MD5(text).toString(CryptoJS.enc.Hex);
}
// 生成签名戳
const getFormattedDate = () => {
const date = new Date();
const year = date.getFullYear(); // 获取年份 (yyyy)
const month = String(date.getMonth() + 1).padStart(2, '0'); // 获取月份 (MM)
const day = String(date.getDate()).padStart(2, '0'); // 获取日期 (dd)
const hours = String(date.getHours()).padStart(2, '0'); // 获取小时 (HH)
return `es0${year}${month}${day}${hours}0es`; // 拼接成 yyyyMMddHH 格式
}
// 加密方法
const preprocessData = (data: string) => {
console.log('data', data);
// YYYYMMDD
let timestamp = getFormattedDate()
console.log('timestamp', timestamp);
// 秒为单位的时间戳
let timeSecond = parseInt((new Date().getTime() / 1000).toString())
console.log('timeSecond', timeSecond);
// 数据的加密
let encryptionData = encryptAESECB(data, timestamp)
console.log('encryptionData', encryptionData);
// md5签名方法
let md5Data = md5(timestamp, encryptionData, timestamp)
console.log('md5Data', md5Data);
let res = {
data: encryptionData,
timestamp: timeSecond,
sign: md5Data
}
console.log('res', res);
return res
}
export default instance;