/* eslint-disable */ import React, { useState, useReducer, useContext, useEffect, useRef } from "react"; import { ConfigProvider, Button, Menu, Form, Input, Space, Upload, Table, message, Spin, Modal, Rate, Badge, Image } from "antd-v4"; import zhCN from "antd/es/locale/zh_CN"; import { MessageOutlined, QuestionOutlined, UnorderedListOutlined, CheckOutlined, SearchOutlined, DeleteOutlined, CloseCircleOutlined, MinusOutlined, ExportOutlined, BulbOutlined } from "@ant-design/icons"; import BraftEditor from "braft-editor"; import "braft-editor/dist/index.css"; import Draggable from "react-draggable"; import styles from "./index.less"; import * as commonServices from "@/services/services"; import commonConfig from "@/utils/config"; import BackIcon from "@/assets/back.svg"; import CopyAllIcon from "@/assets/copyallWhite.svg"; import AiAssistant from "./components/AiAssistant"; import myContext from "./utils/MyContext"; import WorkOrderSystemService from "../WorkOrderSystem/WorkOrderSystemService"; ExportOutlined; const reducer = (state, action) => { const [type, payload] = action; switch (type) { case "set": return { ...state, ...payload }; case "setPage": const { page, pageSize, dispatch } = payload; state.historyListPagination = { ...state.historyListPagination, page, pageSize }; feedbackEvent.handleGetHistoryData({ props: state, dispatch }); return { ...state }; default: return { ...state, ...payload }; } }; // 初始化参数 const initData = { modalVisible: false, detailModalVisible: false, bMin: false, // 是否最小化 detailModalId: "detailModalId_" + Math.random() .toString(36) .substring(2), replyModalOpen: false, currentMenuKey: "problemFeed", // currentMenuKey: "historyList", historyListData: [], historyListPagination: {}, pageLoading: false, detailData: {}, rateArr: ["差评", "不满意", "一般", "满意", "非常满意"], imageVisible: false, imageSrc: null }; // 获取动态初始化参数 const handleGetInitDataAdd = ({ currentPane: xlycurrentPane } = {}) => { // 当前pane // const currentPane = props.app.currentPane; const currentPane = xlycurrentPane || JSON.parse(localStorage.getItem("xlybusinesscurrentPane")) || {}; const { title: sModuleName } = currentPane; // 年月日 const date = new Date(); const year = date.getFullYear(); const month = date.getMonth() < 9 ? `0${date.getMonth() + 1}` : date.getMonth() + 1; const day = date.getDate() < 10 ? `0${date.getDate()}` : date.getDate(); // 获取历史列表 return { currentDate: `${year}-${month}-${day}`, currentPane, initialValues: { sTitle: `【${sModuleName}】【${year}-${month}-${day}】`, sContentMemo: BraftEditor.createEditorState("

") } }; }; const _this = {}; // 主入口 const ProblemFeedback = props => { const [state, dispatch] = useReducer(reducer, { ...props, ...initData, ...handleGetInitDataAdd() }); const [showBtn, setShowBtn] = useState(true); // 按钮是否显示 // postmessage接收 useEffect(() => { const recMessage = e => { if (e && e.data) { const { payload, action, currentPane, app } = e.data; if (payload === "commonFeedback") { switch (action) { case "openModal": let addState = {}; if (window.lastAction === 0) { addState = { ...handleGetInitDataAdd({ currentPane }), app }; } dispatch([ "set", { modalVisible: true, bMin: false, ...addState } ]); break; default: break; } } } }; window.addEventListener("message", recMessage); return () => { window.removeEventListener("message", recMessage); }; }, []); // 如果路由是commonFeedback,设置一些样式 useEffect( () => { if (props.formRoute === "/commonFeedback") { setShowBtn(false); document .querySelector("#root") .style.setProperty("background", "transparent", "important"); document .querySelector("html") .style.setProperty("background", "transparent", "important"); document .querySelector("body") .style.setProperty("background", "transparent", "important"); } }, [props.formRoute] ); // 弹窗显示隐藏时触发操作 useEffect( () => { const addState = showBtn ? handleGetInitDataAdd() : ""; dispatch(["set", { ...addState, currentMenuKey: "problemFeed" }]); if (state.modalVisible === false) { window.parent.postMessage( { payload: "commonFeedback", action: "closeModal" }, "*" ); window.lastAction = 0; } }, [state.modalVisible] ); // 弹窗最小化时触发操作 useEffect( () => { if (state.bMin === true) { window.parent.postMessage( { payload: "commonFeedback", action: "closeModal" }, "*" ); window.lastAction = 1; } }, [state.bMin] ); // 获取历史数据 useEffect(() => { state.historyListPagination.onChange = (page, pageSize) => { dispatch(["setPage", { page, pageSize, dispatch }]); }; feedbackEvent.handleGetHistoryData({ props: state, dispatch }); }, []); // websocket const socketRef = useRef(null); const reconnectTimeoutRef = useRef(null); const [unReadCount, setUnReadCount] = useState(0); const useDefaultWs = useRef(location.pathname !== "/commonFeedback"); useEffect( () => { if ( props.app && props.app.userinfo && props.app.userinfo.sId && socketRef.current === null ) { if (useDefaultWs.current) { console.log("=====检测到是小羚羊项目管理系统,不打开新的websocket。"); } else { const { app } = props; const { userinfo } = app; let url = `${commonConfig.feedback_ws_host}websocket/${ userinfo.sId }?sLoginType=webLogin`; const connectWebSocket = () => { const newSocket = new WebSocket(url); newSocket.onopen = () => { console.log("=====WebSocket 连接已打开"); }; newSocket.onmessage = msg => { const rtmsg = JSON.parse(msg.data); if (rtmsg.action === "workMsg") { setUnReadCount(Number(rtmsg.msg.iCount)); } console.log("=====接收到消息:", rtmsg); }; newSocket.onerror = () => { console.log("=====WebSocket 连接出错"); }; newSocket.onclose = () => { console.log("=====WebSocket 连接已关闭"); if (!location.pathname.includes("/login")) { // 断线重连 reconnectTimeoutRef.current = setTimeout( connectWebSocket, 3000 ); } }; // 设置 WebSocket 对象 socketRef.current = newSocket; }; connectWebSocket(); return () => { if (reconnectTimeoutRef.current !== null) { clearTimeout(reconnectTimeoutRef.current); } if ( socketRef.current && socketRef.current.readyState === WebSocket.OPEN ) { socketRef.current.close(); } }; } } }, [props.app] ); // project项目时ws消息来源 useEffect( () => { if (props.app.workMsg && useDefaultWs.current) { setUnReadCount(Number(props.app.workMsg.iCount)); } }, [props.app.workMsg] ); useEffect( () => { window.parent.postMessage( { payload: "commonFeedback", action: "unReadCountChange", iCount: unReadCount, color: unReadCount == 0 ? "#52c41a" : unReadCount < 10 ? "#faad14" : "" }, "*" ); }, [unReadCount] ); // 设置按钮活动范围 const [positionOffset, setPositionOffset] = useState({ x: 0, y: -200 }); const [tempClient, setTempClient] = useState([0, 0]); const [bounds, setBounds] = useState(null); const btnRef = useRef(null); useEffect(() => { if (props.formRoute !== "/commonFeedback") { const handleResize = () => { setShowBtn(false); const btnWith = 32; const btnHeight = 32; const feedbackBtnRecord = sessionStorage.getItem("feedbackBtnRecord") || "0,0"; const diffX = Number(feedbackBtnRecord.split(",")[0]); const diffY = Number(feedbackBtnRecord.split(",")[1]); let newX = positionOffset.x + diffX; let newY = positionOffset.y + diffY; newX = Math.max(-window.innerWidth + btnWith, newX); newX = Math.min(newX, 0); newY = Math.max(-window.innerHeight + btnHeight + 53, newY); newY = Math.min(newY, 0); sessionStorage.setItem( "feedbackBtnRecord", `${newX - positionOffset.x},${newY - positionOffset.y}` ); const positionOffsetNew = { x: newX, y: newY }; setPositionOffset(positionOffsetNew); const { x: offsetX, y: offsetY } = positionOffsetNew; setBounds({ top: -(window.innerHeight + offsetY - 53 - btnHeight), // 按钮上方可移动范围 left: -(window.innerWidth + offsetX - btnWith), // 按钮左边可移动用范围 right: -offsetX, // 按钮右边可移动用范围 bottom: -offsetY // 按钮下边可移动范围 }); setTimeout(() => { setShowBtn(true); hideEdgeBtn(); }, 100); }; handleResize(); window.addEventListener("resize", handleResize); return () => { window.removeEventListener("resize", handleResize); }; } }, []); // 记录按钮位置 const recordBtnPostion = (diffX, diffY) => { const feedbackBtnRecord = sessionStorage.getItem("feedbackBtnRecord"); let feedbackBtnRecordNew = `${diffX},${diffY}`; if (feedbackBtnRecord) { feedbackBtnRecordNew = `${Number(feedbackBtnRecord.split(",")[0]) + diffX},${Number(feedbackBtnRecord.split(",")[1]) + diffY}`; } sessionStorage.setItem("feedbackBtnRecord", feedbackBtnRecordNew); }; // 隐藏边缘按钮 const timer = useRef(null); const [badgeClass, setBadgeClass] = useState(null); const hideEdgeBtn = () => { if (_this.draging) { return; } const feedbackBtnRecord = sessionStorage.getItem("feedbackBtnRecord") || "0,0"; const diffX = Number(feedbackBtnRecord.split(",")[0]); const diffY = Number(feedbackBtnRecord.split(",")[1]); clearTimeout(timer.current); timer.current = null; if (Math.abs(diffX) < 1) { setTimeout(() => { document.querySelector("html").style.overflowX = "hidden"; setBadgeClass(styles.hideBtnToRight); }, 1500); } else if (window.innerWidth - Math.abs(diffX) - 32 < 1) { setTimeout(() => { document.querySelector("html").style.overflowX = "hidden"; setBadgeClass(styles.hideBtnToLeft); }, 1500); } else if (Math.abs(diffY - 200) < 1) { setTimeout(() => { document.querySelector("html").style.overflowY = "hidden"; setBadgeClass(styles.hideBtnToBottom); }, 1500); } }; return ( {showBtn && (
{ const { x, y } = btnRef.current.getBoundingClientRect(); setTempClient([x, y]); }} onDrag={() => { _this.draging = true; clearTimeout(timer.current); timer.current = null; setBadgeClass(""); }} onStop={() => { _this.draging = false; const { x, y } = btnRef.current.getBoundingClientRect(); const diffX = x - tempClient[0]; const diffY = y - tempClient[1]; recordBtnPostion(diffX, diffY); hideEdgeBtn(); if (Math.abs(diffX) <= 2 && Math.abs(diffY) <= 2) { dispatch(["set", { modalVisible: true, bMin: false }]); } }} >
{ clearTimeout(timer.current); timer.current = null; setBadgeClass(""); document.querySelector("html").style.overflowX = "auto"; document.querySelector("html").style.overflowY = "auto"; }} onMouseLeave={() => { hideEdgeBtn(); }} >
)} {state.detailModalVisible && }
); }; // 对话框 function XlyModal() { const { props, dispatch } = useContext(myContext); const { modalVisible, currentMenuKey, pageLoading, currentDate, currentPane, bMin } = props; const { title: sModuleName } = currentPane; return ( <> {modalVisible ? ( 问题反馈 {`菜单名称: ${sModuleName} ${Array(10) .fill("\xa0") .join("")} 创建时间: ${currentDate}`} ); } // 历史列表 function HistoryList() { const { props, dispatch } = useContext(myContext); const { app } = props; const { userinfo } = app; const { sType } = userinfo; const { historyListData, historyListPagination, detailModalVisible, currentMenuKey } = props; const headerColumnAdd = sType === "sysadmin" ? [ { title: "制单人", dataIndex: "sMakePerson", width: 100 }, { title: "部门", dataIndex: "sDepartName", width: 100 } ] : []; const headerColumn = [ { title: "行号", dataIndex: "iRowNum", width: 60 }, { title: "标题", dataIndex: "sTitle", width: 280 }, ...headerColumnAdd, { title: "模块名称", dataIndex: "sModuleName", width: 120 }, { title: "内容", dataIndex: "sDeal", width: 80, render: (_, record) => ( ) }, { title: "未读回复", dataIndex: "iReplyCount", width: 80, render: count => ( ) }, { title: "创建日期", dataIndex: "tCreateDate", width: 150 }, { title: "状态", dataIndex: "sStatus", width: 100 } ]; const pagination = { pageSize: commonConfig.pageSize, size: "large", pageSizeOptions: commonConfig.pageSizeOptions, showSizeChanger: true, showQuickJumper: true, hideOnSinglePage: false, ...historyListPagination }; useEffect( () => { if (detailModalVisible === false && currentMenuKey === "historyList") { feedbackEvent.handleGetHistoryData({ props, dispatch, ...form.getFieldsValue() }); } }, [detailModalVisible] ); const [form] = Form.useForm(); const onFinish = values => { feedbackEvent.handleGetHistoryData({ props, dispatch, ...values }); }; const onReset = () => { form.resetFields(); feedbackEvent.handleGetHistoryData({ props, dispatch }); }; return (
{ if (ref) { ref.querySelector( ".ant-table-container" ).style.height = `calc(100vh - 250px)`; ref.querySelector( ".ant-table-body" ).style.height = `calc(100vh - 250px - 30px - 15px)`; } }} >
index % 2 === 1 ? "dark-record-row" : "" } /> ); } // 详情 const DetailModal = () => { const { props, dispatch } = useContext(myContext); const { detailModalVisible, detailData, rateArr, detailModalId, bMin } = props; const { fileData = [], replyData = [], sId, sMakePerson, sTitle, sContentMemo, sStatus, sModuleName, tCreateDate } = detailData; const [form] = Form.useForm(); const [formState] = useState({ sContentMemo: BraftEditor.createEditorState(sContentMemo) }); const [fileList, setFileList] = useState([]); const [fileAdd, setFileAdd] = useState([]); const [newContentOpen, setNewContentOpen] = useState(false); useEffect( () => { const temp = {}; replyData.forEach((item, index) => { const { tCreateDate, sMakePerson: sMakePersonTemp, sResult, sContentMemo: sContentMemoTemp } = item; temp[`reply${index}`] = `${index + 1}. ${tCreateDate} 由 ^${sMakePersonTemp}^ ${sResult.replace( "已", "" )}`; if (sContentMemoTemp) { temp[`replyContentMemo${index}`] = BraftEditor.createEditorState( sContentMemoTemp ); } }); form.setFieldsValue({ ...formState, ...temp }); }, [replyData] ); useEffect( () => { setFileList( [...fileData].map(item => { const { sId: uid, sPictureName: name, sPicturePath } = item; return { uid, name, url: `${ commonConfig.feedback_host }file/download?savePathStr=${sPicturePath}&sModelsId=100`, status: "done", item }; }) ); }, [fileData] ); useEffect( () => { if (fileAdd.length) { setFileAdd([]); feedbackEvent.handleUploadAdd( fileAdd, { sId, sMakePerson }, _fileData => { setFileList([ ...fileList, ..._fileData.map(item => { const { sId: uid, sPictureName: name, sPicturePath } = item; return { uid, name, url: `${ commonConfig.feedback_host }file/download?savePathStr=${sPicturePath}&sModelsId=100`, status: "done", item }; }) ]); } ); } }, [fileAdd] ); // 富文本看点击图片事件 useEffect(() => { const showImage = e => { if (e.target && e.target.nodeName === "IMG") { const { src } = e.target; if (src) { dispatch(["set", { imageVisible: true, imageSrc: src }]); } } }; document.addEventListener("dblclick", showImage); return () => { document.removeEventListener("dblclick", showImage); }; }, []); const onFinish = () => { setNewContentOpen(true); }; const onFinish1 = values => { feedbackEvent.handleEvaluate({ ...values, sMakePerson }, sId, () => { setNewContentOpen(false); dispatch(["set", { detailModalVisible: false }]); }); }; const enabled = sStatus !== "已关闭"; const uploadProps = { accept: "*/*", multiple: true, fileList, disabled: !enabled, beforeUpload: file => { setFileAdd(prevState => [...prevState, file]); return false; }, onRemove: file => { Modal.confirm({ title: "确认删除", onOk() { feedbackEvent.handleFileDel(file.item, sId, () => { setFileList(fileList.filter(item => item.uid !== file.uid)); message.success("附件删除成功"); }); } }); } }; return ( <> {detailModalVisible ? ( {sTitle} {`菜单名称: ${sModuleName} ${Array(10) .fill("\xa0") .join("")} 创建时间: ${tCreateDate}`} } open={detailModalVisible && !bMin} className={detailModalId} wrapClassName={styles.detailModal} width="100%" height="calc(100% - 50px)" style={{ top: 0 }} footer={null} onCancel={() => { dispatch(["set", { detailModalVisible: false }]); }} >
历史记录
{replyData.map((item, index) => { const { sContentMemo: sContentMemoTemp } = item; const itemName = `reply${index}`; const itemName1 = `replyContentMemo${index}`; const value = form.getFieldValue()[itemName]; if (!value) { return ""; } const valueArr = value.split("^"); if (valueArr.length < 3) { return ""; } return ( <>
{valueArr[0]} {{valueArr[1]}} {valueArr[2]}
{sContentMemoTemp ? ( ) : ( "" )} ); })}
{enabled && 添加附件} {enabled && ( <> )}
) : ( "" )} {newContentOpen && ( { setNewContentOpen(false); }} >
)} ); }; // 回复内容 const ReplyModal = () => { const { props, dispatch } = useContext(myContext); const { replyModalOpen, detailData, detailModalId, bMin } = props; const { sId, sTitle } = detailData; const closeModal = () => { dispatch(["set", { replyModalOpen: false }]); }; const onFinish = values => { const { app } = props; const { userinfo } = app; const { sUserName: sMakePerson, sId: sLoginId } = userinfo; feedbackEvent.handleAddReply( { sContentMemo: values.sContentMemo.toHTML(), sMakePerson, sLoginId, sId }, () => { feedbackEvent.handleGetDetailData(detailData, detailDataNew => { dispatch([ "set", { replyModalOpen: false, detailData: detailDataNew } ]); document .querySelector(`.${detailModalId}`) .querySelector(".ant-modal-body").scrollTop = 9999; }); } ); }; return ( replyModalOpen && ( {sTitle} -回复内容 ) ); }; // 图片弹窗 const ImageModal = () => { const { props, dispatch } = useContext(myContext); const { imageSrc, imageVisible } = props; return ( { dispatch(["set", { imageVisible: false }]); } }} /> ); }; // 调用的后台方法 const feedbackEvent = { handleGetHistoryData: async ({ props, dispatch, sSearch = "" }, isWait) => { const { app, historyListPagination } = props; const { page: pageNum = 1, pageSize = commonConfig.pageSize } = historyListPagination; const { userinfo } = app; const { companyName: sBrandName, sUserName: sMakePerson, sId: sLoginId, sType: sUserType } = userinfo; const dataUrl = `${commonConfig.feedback_host}sysworkorder/getData`; const condition = { pageNum, pageSize, sBrandName, sMakePerson, sLoginId, sUserType, sSearch }; const dataReturn = (await commonServices.postValueService( "", condition, dataUrl )).data; if (!dataReturn) { return; } const { total, rows: historyListData } = dataReturn; if (isWait) { return { historyListData, historyListPagination: { ...historyListPagination, total } }; } else { dispatch([ "set", { historyListData, historyListPagination: { ...historyListPagination, total } } ]); } }, handleUpload: (files, cb) => { if (files.length === 0) { cb("[]"); return; } let formData = new FormData(); for (let item of files) { formData.append("file", item); } const url = `${commonConfig.feedback_host}file/uploadMulti`; fetch(url, { method: "POST", body: formData }) .then(res => res.json()) .then(dataReturn => { if (dataReturn.code > 0) { const { dataset } = dataReturn; const { rows } = dataset; const savePathStr = rows[0].savePathStr; const fileData = savePathStr.split(",").map(item => ({ sPicturePath: item, sPictureName: item.substring(item.indexOf("_") + 1) })); if (cb) { cb(JSON.stringify(fileData)); } } else { message.error(dataReturn.msg); } }); }, handleUploadAdd: (file, { sId, sMakePerson }, cb) => { feedbackEvent.handleUpload(file, async fileData => { const url = `${commonConfig.feedback_host}sysworkorder/addFile/${sId}`; const dataReturn = (await commonServices.postValueService( "", { fileData, sMakePerson }, url )).data; if (!dataReturn) { cb([]); } else { cb(dataReturn.data); } }); }, handleFileDel: async (file, sId, cb) => { const url = `${commonConfig.feedback_host}sysworkorder/delFile/${sId}`; const dataReturn = (await commonServices.postValueService( "", { fileData: JSON.stringify(file) }, url )).data; if (!dataReturn) { return; } else { cb(); } }, handleGetDetailData: async (record, cb) => { const { sId } = record; const dataUrl = `${ commonConfig.feedback_host }sysworkorder/getDetail/${sId}/custom`; const dataReturn = (await commonServices.postValueService("", {}, dataUrl)) .data; if (!dataReturn) { return; } cb(dataReturn.data); }, handleAddReply: async (condition, cb) => { const dataUrl = `${commonConfig.feedback_host}sysworkorder/addReply/${ condition.sId }`; const dataReturn = (await commonServices.postValueService( "", condition, dataUrl )).data; if (!dataReturn) { return; } cb(dataReturn); }, handleEvaluate: async (condition, sId, cb) => { const url = `${commonConfig.feedback_host}sysworkorder/evaluate/${sId}`; const dataReturn = (await commonServices.postValueService( "", condition, url )).data; if (!dataReturn) { return; } else { if (dataReturn.code >= 0) { message.success(dataReturn.msg); cb(); } else { message.error(dataReturn.msg); } } } }; export default ProblemFeedback;