Parcourir la source

Merge branch 'develop' of http://120.55.44.4:10080/xujunjie/gt_client_pad into develop

xujunjie il y a 1 an
Parent
commit
8fa635caa6
32 fichiers modifiés avec 1382 ajouts et 394 suppressions
  1. 6 0
      .umirc.ts
  2. 11 5
      src/components/ManagementPage/chartModule.js
  3. 4 0
      src/components/TabsContent/index.js
  4. 0 2
      src/components/TabsContent/index.less
  5. 0 1
      src/global.less
  6. 122 0
      src/pages/DeviceManager/deviceMaintainDetail.js
  7. 3 7
      src/pages/DeviceManager/index.js
  8. 6 1
      src/pages/EqSelfInspection/components/Detail.js
  9. 58 19
      src/pages/Home/ChemCostComparison.js
  10. 82 60
      src/pages/Home/EnergyCostComparison.js
  11. 50 44
      src/pages/Home/QualityMng.js
  12. 75 69
      src/pages/Home/WaterAmtMng.js
  13. 1 1
      src/pages/Home/index.less
  14. 1 1
      src/pages/Home/manage.less
  15. 2 1
      src/pages/SmartOps/Analysis.js
  16. 1 1
      src/pages/SmartOps/HistoryRecord.js
  17. 1 1
      src/pages/SmartOps/OperationRecord.js
  18. 1 0
      src/pages/SmartOps/WorkAnalysisDetail.js
  19. 74 75
      src/pages/SmartOps/components/DeviceAnalysis.js
  20. 27 0
      src/pages/SmartOps/components/SubTitle.js
  21. 2 1
      src/pages/SmartOps/components/VideoAnalysis.js
  22. 9 1
      src/pages/SmartOps/index.js
  23. 203 0
      src/pages/SmartOps/operationManage/CostAnalysis/CostAnalysis.js
  24. 12 0
      src/pages/SmartOps/operationManage/CostAnalysis/CostAnalysis.less
  25. 69 0
      src/pages/SmartOps/operationManage/index.js
  26. 510 55
      src/pages/TaskManage/Detail/TaskDetail/TaskDetail.tsx
  27. 28 29
      src/pages/TaskManage/Detail/TaskDetail/taskDetail.less
  28. 4 0
      src/pages/TaskManage/components/MandateDetail.js
  29. 1 1
      src/pages/TaskManage/components/TopFilter.tsx
  30. 16 16
      src/pages/TaskManage/constent.ts
  31. 1 1
      src/pages/TaskManage/index.less
  32. 2 2
      src/services/OperationManagement.js

+ 6 - 0
.umirc.ts

@@ -305,6 +305,12 @@ export default defineConfig({
       name: '',
       component: './DeviceManager/EquipmentConstructionList',
     },
+    //设备维修保养详情
+    {
+      path: '/device/maintain-detail/:projectId',
+      name: '',
+      component: './DeviceManager/deviceMaintainDetail',
+    },
   ],
   npmClient: 'yarn',
 });

+ 11 - 5
src/components/ManagementPage/chartModule.js

@@ -245,10 +245,10 @@ const defaultOption = {
       nameTextStyle: {
         fontSize: '0.24rem',
         // align: 'left',
-        padding: [0, 0, 20, 0],
+        padding: [0, 0, 15, 0],
       },
       axisLabel: {
-        fontSize: '0.24rem',
+        fontSize: '0.2rem',
       },
       axisLine: {
         show: false,
@@ -269,8 +269,11 @@ const defaultOption = {
       position: 'right',
       nameTextStyle: {
         fontSize: '0.24rem',
-        // align: 'left',
-        padding: [0, 0, 20, 0],
+        align: 'right',
+        padding: [0, 0, 15, 0],
+      },
+      axisLabel: {
+        fontSize: '0.2rem',
       },
       axisLine: {
         show: true,
@@ -293,7 +296,10 @@ const defaultOption = {
       nameTextStyle: {
         fontSize: '0.24rem',
         // align: 'left',
-        padding: [0, 0, 20, 0],
+        padding: [0, 0, 15, 0],
+      },
+      axisLabel: {
+        fontSize: '0.2rem',
       },
       axisLine: {
         show: true,

+ 4 - 0
src/components/TabsContent/index.js

@@ -8,6 +8,7 @@ const TabsContent = (props) => {
     active: parentActive,
     center = true,
     small = false,
+    spacing = 4,
     items = {},
     onChange,
   } = props;
@@ -31,6 +32,9 @@ const TabsContent = (props) => {
           <>
             <div
               key={item.key}
+              style={{
+                padding: `0 ${small ? (spacing - 1) / 10 : spacing / 10}rem`,
+              }}
               className={`${styles.tabsItem} ${
                 active == item.key ? styles.active : ''
               }`}

+ 0 - 2
src/components/TabsContent/index.less

@@ -20,7 +20,6 @@
   // }
 }
 .tabsItem {
-  padding: 0 0.4rem;
   color: #3f3f40;
   font-size: 0.36rem;
   white-space: nowrap;
@@ -36,7 +35,6 @@
   border-bottom: none;
   .tabsItem {
     font-size: 0.28rem;
-    padding: 0 0.3rem;
     font-weight: 600;
     &:last-child {
       padding-right: 0;

+ 0 - 1
src/global.less

@@ -116,7 +116,6 @@ body {
   }
 
   .ant-modal-root .ant-modal-mask {
-    border-radius: 0.5rem;
     background-color: rgba(0, 0, 0, 0.2);
   }
 

+ 122 - 0
src/pages/DeviceManager/deviceMaintainDetail.js

@@ -0,0 +1,122 @@
+import PageContent from '@/components/PageContent';
+import { useParams } from '@umijs/max';
+import { Table } from 'antd';
+export default function DeviceMaintainDetail() {
+  const { projectId, type } = useParams();
+
+  const columns = [
+    {
+      title: type == 1 ? '维修资料' : '保养资料',
+      dataIndex: 'Name',
+      render: (text, item) => {
+        return <PreviewFile name={item.Name} src={item.Url} />;
+      },
+    },
+    {
+      title: '创建时间',
+      dataIndex: 'CreatedTime',
+      render: (text) => {
+        return text ? moment(text).format('YYYY年MM月DD日  HH:mm:ss') : null;
+      },
+    },
+    {
+      title: '操作',
+      width: '20%',
+      render: (record) => (
+        <Fragment>
+          {this.showJurisdiction('func-01-ops-1-2-3-01') && (
+            <>
+              &nbsp;&nbsp;
+              <a
+                style={{ color: '#7BFFFB' }}
+                onClick={() => this.checkFile(record)}
+                download
+              >
+                下载
+              </a>
+            </>
+          )}
+        </Fragment>
+      ),
+    },
+  ];
+
+  function getUser(params) {
+    let arr = [];
+    if (!params) {
+      return;
+    } else {
+      return (arr = params
+        .map((item) => {
+          return item.Operator?.CName;
+        })
+        .join(','));
+    }
+  }
+  const getStatus = (time, acStatus) => {
+    if (time && acStatus == 0) return '不通过';
+    if (time && acStatus == 2) return '通过';
+    return '';
+  };
+  const {
+    DeviceCode,
+    DeviceName,
+    Reason,
+    PlanTime,
+    RepairType,
+    IsPlan,
+    DifficultyLevel,
+    AcceptanceStatus,
+    EvaluationScore,
+    Operators,
+    RepairTime,
+    Desc,
+    MaterialConsumption,
+    AcceptorUser,
+    AcceptanceTime,
+    Level,
+    AcceptanceOpinion,
+    Note,
+    Files = [],
+  } = {};
+  return (
+    <PageContent closeable={false}>
+      <div>
+        <span>设备位号:{DeviceCode}</span>
+        <span>维修人:{getUser(Operators)}</span>
+        <span>设备名称:{DeviceName}</span>
+        <span>维修日期:{RepairTime}</span>
+        <span>维修原因:{Reason}</span>
+        <span>维修内容简述:{Desc}</span>
+
+        <span>计划日期:{PlanTime}</span>
+        <span>物料消耗:{MaterialConsumption}</span>
+
+        <span>维修方式:{RepairType}</span>
+        <span>验收人:{AcceptorUser?.CName}</span>
+
+        <span>是否计划内:{String(IsPlan) == 0 ? '否' : '是'}</span>
+        <div>验收状态:{getStatus(AcceptanceTime, AcceptanceStatus)}</div>
+
+        <span>难度级别:{DifficultyLevel}</span>
+        <span>级别:{Level}</span>
+        <span>维修状态:{AcceptanceStatus}</span>
+        <span>验收意见:{AcceptanceOpinion}</span>
+
+        <span>评估分数:{EvaluationScore * 100}</span>
+        <span>
+          保养备注:
+          {Note?.split('|').map((item) => (
+            <div>{item}</div>
+          ))}
+        </span>
+      </div>
+      <Table
+        style={{ marginTop: '0.1rem' }}
+        dataSource={Files}
+        columns={columns}
+        pagination={false}
+      />
+    </PageContent>
+  );
+}

+ 3 - 7
src/pages/DeviceManager/index.js

@@ -242,13 +242,6 @@ const Device = ({ projectId }) => {
   return (
     <div className={`content-tab ${styles.sparePart}`}>
       <Spin spinning={loadingDevice}>
-        {/* <div className={styles.titleContent}>
-          <img className={styles.img} src={deviceIcon} />
-          <div>
-            <div className="value-number">{allData?.total}</div>
-            <div className={styles.text}>设备总数(个)</div>
-          </div>
-        </div> */}
         <div className={styles.top}>
           <div className={styles.left}>
             <img
@@ -298,6 +291,9 @@ const Device = ({ projectId }) => {
           dataSource={repairData?.filter((item) => item.DeviceCode)}
           columns={columnsRepair}
           pagination={false}
+          onRow={(record, index) => ({
+            onClick: () => console.log(record),
+          })}
         />
       )}
       {type == 2 && (

+ 6 - 1
src/pages/EqSelfInspection/components/Detail.js

@@ -61,7 +61,12 @@ function Detail(props) {
           else if (index === 1) label = '工艺自检';
           else {
             label = '安全自检';
-            value = data?.secureStatus === 0 ? '正常' : '异常';
+            value =
+              data?.secureStatus === 0
+                ? itemSplit[1] !== '异常'
+                  ? '正常'
+                  : '异常'
+                : '异常';
           }
           resultArr.push({
             ...getTextColor(value),

+ 58 - 19
src/pages/Home/ChemCostComparison.js

@@ -15,6 +15,15 @@ import styles from './manage.less';
 
 const { TabPane } = Tabs;
 
+const referencePriceTable = {
+  阻垢剂: '30.088元',
+  盐酸: '0.531元',
+  非氧化杀菌剂: '26.549元',
+  次氯酸钠: '1.001元',
+  氢氧化纳: '1.527元',
+  还原剂: '4.956元',
+};
+
 const typeParams = [
   {
     // 计划吨水药耗
@@ -38,10 +47,32 @@ const typeParams = [
   },
 ];
 
-const CostComparison = (props) => {
+const CostComparison = () => {
+  const [open, setOpen] = useState(false);
+
+  return (
+    <PageContent closeable={false}>
+      <PageTitle onReturn={() => UnityAction.sendMsg('menuItem', '首页')}>
+        药耗监测
+        <div
+          onClick={(e) => {
+            e.stopPropagation();
+            setOpen(!open);
+          }}
+          style={{ marginLeft: 10 }}
+          className={`password-eye ${open ? 'open' : ''}`}
+        ></div>
+      </PageTitle>
+      <ChemCost open={open} />
+    </PageContent>
+  );
+};
+
+export default CostComparison;
+
+export const ChemCost = ({ open }) => {
   const { projectId } = useParams();
 
-  const [open, setOpen] = useState(false);
   const [chartData, setChartData] = useState([]);
   const [chemList, setChemList] = useState([]);
   const [currentChem, setCurrentChem] = useState();
@@ -84,6 +115,9 @@ const CostComparison = (props) => {
     // 大于100,保留一位
     // 大于1000,不保留
     let fixed = 0;
+    if (maxValue === 0) {
+      return 2;
+    }
     if (maxValue === 0) return fixed;
     if (maxValue < 1) {
       //maxValue + 1 防止maxValue过小自动转科学计数法
@@ -130,7 +164,6 @@ const CostComparison = (props) => {
         .map((item) => item.value)
         .reduce((a, b) => Math.max(a, b));
       const chemPerCostFixed = getFixed(chemPerCostMaxValue);
-      console.log(chemPerCostFixed);
       chemPerCost.dataList = [
         {
           type: 0,
@@ -245,24 +278,29 @@ const CostComparison = (props) => {
   }, []);
 
   return (
-    <PageContent closeable={false}>
-      <PageTitle onReturn={() => UnityAction.sendMsg('menuItem', '首页')}>
-        药耗监测
-        <div
-          onClick={(e) => {
-            e.stopPropagation();
-            setOpen(!open);
-          }}
-          style={{ marginLeft: 10 }}
-          className={`password-eye ${open ? 'open' : ''}`}
-        ></div>
-      </PageTitle>
-
+    <>
+      <div className={styles.curEnergyCost}>
+        <div className={styles.item}>
+          <div className={styles.value}>
+            {open ? topValues.chemPer : '***'}
+            <span className={styles.unit}>kg/t</span>
+          </div>
+          <div className={styles.name}>近一天吨水药耗</div>
+        </div>
+        <div className={styles.item}>
+          <div className={styles.value}>
+            {open ? topValues.chemUser : '***'}
+            <span className={styles.unit}>kg</span>
+          </div>
+          <div className={styles.name}>近一天药量</div>
+        </div>
+      </div>
       <div className="card-box" style={{ padding: '0.2rem' }}>
         {/* 使用Tabs来展示所有药的标签 */}
         <div className="tabs">
           {chemList?.map((item) => (
             <div
+              key={item}
               onClick={() => {
                 setCurrentChem(item);
                 handleChemChange(item);
@@ -275,6 +313,9 @@ const CostComparison = (props) => {
         </div>
         <div className={styles.curEnergyCost}>
           <div className={styles.item}>
+            <div style={{ fontSize: '0.18rem', color: 'gray' }}>
+              药剂参考价格:{referencePriceTable[`${currentChem}`]}
+            </div>
             <div className={styles.value}>
               {open ? topValues.chemPer : '***'}
               <span className={styles.unit}>kg/m³</span>
@@ -309,8 +350,6 @@ const CostComparison = (props) => {
           </div>
         )}
       </div>
-    </PageContent>
+    </>
   );
 };
-
-export default CostComparison;

+ 82 - 60
src/pages/Home/EnergyCostComparison.js

@@ -35,9 +35,31 @@ const typeParams = [
 ];
 
 const CostComparison = () => {
+  const [open, setOpen] = useState(false);
+
+  return (
+    <PageContent closeable={false}>
+      <PageTitle onReturn={() => UnityAction.sendMsg('menuItem', '首页')}>
+        能耗数据
+        <div
+          onClick={(e) => {
+            e.stopPropagation();
+            setOpen(!open);
+          }}
+          style={{ marginLeft: '0.1rem' }}
+          className={`password-eye ${open ? 'open' : ''}`}
+        ></div>
+      </PageTitle>
+      <EnergyCost open={open} />
+    </PageContent>
+  );
+};
+
+export default CostComparison;
+
+export const EnergyCost = ({ open, detailClick }) => {
   const { projectId } = useParams();
 
-  const [open, setOpen] = useState(false);
   const [chartData, setChartData] = useState([]);
   const [curElecPerCost, setElecPerCost] = useState(0); // 当前月实际吨水电耗
   const [curElecUsed, setCurElecUsed] = useState(0); // 当前月实际用电量
@@ -226,32 +248,33 @@ const CostComparison = () => {
   }, []);
 
   const goEnergyDetail = () => {
-    history.push(`/home/energy/detail/${projectId}`);
+    if (detailClick) {
+      detailClick();
+    } else {
+      history.push(`/home/energy/detail/${projectId}`);
+    }
   };
 
   return (
-    <PageContent closeable={false}>
-      <PageTitle onReturn={() => UnityAction.sendMsg('menuItem', '首页')}>
-        能耗数据
+    <div
+      className="card-box"
+      style={{ padding: '0.2rem', position: 'relative' }}
+    >
+      <LineChartOutlined
+        style={{
+          fontSize: '0.4rem',
+          position: 'absolute',
+          right: '0.3rem',
+          color: '#0139f1',
+        }}
+        onClick={goEnergyDetail}
+      />
+      <div className={styles.curEnergyCost}>
         <div
-          onClick={(e) => {
-            e.stopPropagation();
-            setOpen(!open);
-          }}
-          style={{ marginLeft: '0.1rem' }}
-          className={`password-eye ${open ? 'open' : ''}`}
-        ></div>
-      </PageTitle>
-      <div
-        className="card-box"
-        style={{ padding: '0.2rem', position: 'relative' }}
-      >
-        <LineChartOutlined
+          className={styles.item}
           style={{
-            fontSize: '0.4rem',
-            position: 'absolute',
-            right: '0.3rem',
-            color: '#0139f1',
+            borderRight: '1px solid #eaeaea',
+            borderBottom: '1px solid #eaeaea',
           }}
           onClick={goEnergyDetail}
         />
@@ -269,17 +292,17 @@ const CostComparison = () => {
             </div>
             <div className={styles.name}>近一天吨水电耗</div>
           </div>
-          <div
-            className={styles.item}
-            style={{
-              borderBottom: '1px solid #eaeaea',
-            }}
-          >
-            <div className={styles.value}>
-              {open ? curElecUsed : '***'}
-              <span className={styles.unit}>kWh</span>
-            </div>
-            <div className={styles.name}>近一天实际用电量</div>
+          <div className={styles.name}>近一天吨水电耗</div>
+        </div>
+        <div
+          className={styles.item}
+          style={{
+            borderBottom: '1px solid #eaeaea',
+          }}
+        >
+          <div className={styles.value}>
+            {open ? curElecUsed : '***'}
+            <span className={styles.unit}>kWh</span>
           </div>
           <div
             className={styles.item}
@@ -293,36 +316,35 @@ const CostComparison = () => {
             </div>
             <div className={styles.name}>当月吨水电耗</div>
           </div>
-          <div className={styles.item}>
-            <div className={styles.value}>
-              {open ? curElecUsed : '***'}
-              <span className={styles.unit}>kWh</span>
-            </div>
-            <div className={styles.name}>当月实际用电量</div>
+          <div className={styles.name}>当月吨水电耗</div>
+        </div>
+        <div className={styles.item}>
+          <div className={styles.value}>
+            {open ? curElecUsed : '***'}
+            <span className={styles.unit}>kWh</span>
           </div>
+          <div className={styles.name}>当月实际用电量</div>
         </div>
-        {chartData.length !== 0 && (
-          <div
-            style={{
-              height: '9.2rem',
-              display: 'flex',
-              flexDirection: 'column',
-              justifyContent: 'space-between',
-              padding: '0.6rem 0 0.4rem',
-            }}
-          >
-            <div style={{ height: '3.5rem' }}>
-              <ChartModule {...chartData[0]} />
-            </div>
+      </div>
+      {chartData.length !== 0 && (
+        <div
+          style={{
+            height: '9.2rem',
+            display: 'flex',
+            flexDirection: 'column',
+            justifyContent: 'space-between',
+            padding: '0.6rem 0 0.4rem',
+          }}
+        >
+          <div style={{ height: '3.5rem' }}>
+            <ChartModule {...chartData[0]} />
+          </div>
 
-            <div style={{ height: '3.5rem' }}>
-              <ChartModule {...chartData[1]} />
-            </div>
+          <div style={{ height: '3.5rem' }}>
+            <ChartModule {...chartData[1]} />
           </div>
-        )}
-      </div>
-    </PageContent>
+        </div>
+      )}
+    </div>
   );
 };
-
-export default CostComparison;

+ 50 - 44
src/pages/Home/QualityMng.js

@@ -15,6 +15,20 @@ import dayjs from 'dayjs';
 import { useEffect, useMemo, useRef, useState } from 'react';
 
 function Quality() {
+  return (
+    <PageContent closeable={false}>
+      <PageTitle onReturn={() => UnityAction.sendMsg('menuItem', '首页')}>
+        水质监测
+      </PageTitle>
+
+      <WaterQuality />
+    </PageContent>
+  );
+}
+
+export default Quality;
+
+export const WaterQuality = () => {
   const { projectId } = useParams();
   const [currentCode, setCode] = useState(null);
   // const [processId, setProcessId] = useState(null);
@@ -100,54 +114,46 @@ function Quality() {
     const code = codeList.find((item) => item.metric == name);
     if (code) setCode(code);
   };
-
   return (
-    <PageContent closeable={false}>
-      <PageTitle onReturn={() => UnityAction.sendMsg('menuItem', '首页')}>
-        水质监测
-      </PageTitle>
-
-      <div className="card-box" style={{ padding: '0.2rem' }}>
-        <div className="tabs" style={{ marginBottom: '0.2rem' }}>
-          {codeList?.map((item) => (
-            <div
-              onClick={() => {
-                setCode(item);
-              }}
-              className={`tabs-item ${item == currentCode ? 'active' : ''}`}
-            >
-              {item.metric}
-            </div>
-          ))}
+    <div className="card-box" style={{ padding: '0.2rem' }}>
+      <div className="tabs" style={{ marginBottom: '0.2rem' }}>
+        {codeList?.map((item) => (
+          <div
+            key={item}
+            onClick={() => {
+              setCode(item);
+            }}
+            className={`tabs-item ${item == currentCode ? 'active' : ''}`}
+          >
+            {item.metric}
+          </div>
+        ))}
+      </div>
+      <div className="section-title">
+        <div className="section-line"></div>
+        数据曲线
+      </div>
+      <Spin spinning={mainRes.loading}>
+        <div style={{ height: '5rem', marginTop: '0.2rem' }}>
+          {mainRes?.data ? (
+            <ChartModule {...chartProps} onChange={onChange} />
+          ) : (
+            <Empty />
+          )}
         </div>
+      </Spin>
+      <div style={{ marginTop: '0.3rem' }}>
         <div className="section-title">
           <div className="section-line"></div>
-          数据曲线
-        </div>
-        <Spin spinning={mainRes.loading}>
-          <div style={{ height: '5rem', marginTop: '0.2rem' }}>
-            {mainRes?.data ? (
-              <ChartModule {...chartProps} onChange={onChange} />
-            ) : (
-              <Empty />
-            )}
-          </div>
-        </Spin>
-        <div style={{ marginTop: '0.3rem' }}>
-          <div className="section-title">
-            <div className="section-line"></div>
-            数据列表
-          </div>
-          <Table
-            columns={column}
-            style={{ marginTop: '0.2rem' }}
-            dataSource={mainRes?.data}
-            pagination={false}
-          />
+          数据列表
         </div>
+        <Table
+          columns={column}
+          style={{ marginTop: '0.2rem' }}
+          dataSource={mainRes?.data}
+          pagination={false}
+        />
       </div>
-    </PageContent>
+    </div>
   );
-}
-
-export default Quality;
+};

+ 75 - 69
src/pages/Home/WaterAmtMng.js

@@ -14,6 +14,19 @@ import styles from './index.less';
 const { RangePicker } = DatePicker;
 
 const WaterAmtMng = () => {
+  return (
+    <PageContent closeable={false}>
+      <PageTitle onReturn={() => UnityAction.sendMsg('menuItem', '首页')}>
+        水量监测
+      </PageTitle>
+      <WaterAmt />
+    </PageContent>
+  );
+};
+
+export default WaterAmtMng;
+
+export const WaterAmt = () => {
   const { projectId } = useParams();
 
   const [filter, setFilter] = useState([dayjs().subtract(1, 'day'), dayjs()]);
@@ -103,79 +116,72 @@ const WaterAmtMng = () => {
   };
 
   return (
-    <PageContent closeable={false}>
-      <PageTitle onReturn={() => UnityAction.sendMsg('menuItem', '首页')}>
-        水量监测
-      </PageTitle>
-      <Spin spinning={loading}>
-        <div className={styles.timeSelectBox}>
-          <div className={styles.timeBtn}>
-            <Button
-              type="primary"
-              shape="round"
-              onClick={() => {
-                onSearch('day');
-              }}
-            >
-              近一天
-            </Button>
-            <Button
-              type="primary"
-              shape="round"
-              onClick={() => {
-                onSearch('week');
-              }}
-            >
-              近一周
-            </Button>
-            <Button
-              type="primary"
-              shape="round"
-              onClick={() => {
-                onSearch('month');
-              }}
-            >
-              近一个月
-            </Button>
-          </div>
-          <div style={{ fontSize: '0.3rem' }}>
-            <RangePicker
-              allowClear
-              value={filter}
-              onChange={(time) => {
-                onSearch(time);
-              }}
-            />
-          </div>
+    <Spin spinning={loading}>
+      <div className={styles.timeSelectBox}>
+        <div style={{ fontSize: '0.3rem' }}>
+          <RangePicker
+            allowClear
+            value={filter}
+            onChange={(time) => {
+              onSearch(time);
+            }}
+          />
+        </div>
+        <div className={styles.timeBtn}>
+          <Button
+            type="primary"
+            shape="round"
+            onClick={() => {
+              onSearch('day');
+            }}
+          >
+            近一天
+          </Button>
+          <Button
+            type="primary"
+            shape="round"
+            onClick={() => {
+              onSearch('week');
+            }}
+          >
+            近一周
+          </Button>
+          <Button
+            type="primary"
+            shape="round"
+            onClick={() => {
+              onSearch('month');
+            }}
+          >
+            近一个月
+          </Button>
         </div>
-        <div className="card-box" style={{ padding: '0.2rem' }}>
+      </div>
+      <div className="card-box" style={{ padding: '0.2rem' }}>
+        <div className="section-title">
+          <div className="section-line"></div>
+          数据曲线
+        </div>
+        <Spin spinning={loading}>
+          <div style={{ height: '5rem', marginTop: 20 }}>
+            <ChartModule yName="水量(t)" xData={xData} dataList={dataList} />
+          </div>
+        </Spin>
+        <div style={{ marginTop: 30 }}>
           <div className="section-title">
             <div className="section-line"></div>
-            数据曲线
-          </div>
-          <Spin spinning={loading}>
-            <div style={{ height: '5rem', marginTop: 20 }}>
-              <ChartModule yName="水量(t)" xData={xData} dataList={dataList} />
-            </div>
-          </Spin>
-          <div style={{ marginTop: 30 }}>
-            <div className="section-title">
-              <div className="section-line"></div>
-              数据列表
-            </div>
-            <Table
-              loading={loading}
-              columns={columns}
-              rowKey="time"
-              style={{ marginTop: 20 }}
-              pagination={false}
-              dataSource={data?.sort((a, b) => b?.time?.localeCompare(a?.time))}
-            />
+            数据列表
           </div>
+          <Table
+            loading={loading}
+            columns={columns}
+            rowKey="time"
+            style={{ marginTop: 20 }}
+            pagination={false}
+            dataSource={data?.sort((a, b) => b?.time?.localeCompare(a?.time))}
+          />
         </div>
-      </Spin>
-    </PageContent>
+      </div>
+    </Spin>
   );
 };
-
-export default WaterAmtMng;

+ 1 - 1
src/pages/Home/index.less

@@ -373,7 +373,7 @@
     margin-right: 0.1rem;
   }
   .timeBtn {
-    margin-right: 0.1rem;
+    margin-left: 0.1rem;
   }
 
   :global {

+ 1 - 1
src/pages/Home/manage.less

@@ -61,7 +61,7 @@
   flex-wrap: wrap;
   justify-content: center;
   align-items: center;
-  margin: 0.4rem 0.4rem 0 0.4rem;
+  margin: 0.1rem 0.4rem 0 0.4rem;
   .item {
     width: 50%;
     padding: 0.1rem;

+ 2 - 1
src/pages/SmartOps/Analysis.js

@@ -167,7 +167,8 @@ const Analysis = (props) => {
     <Spin spinning={loading}>
       <div style={{ height: 'calc(100vh - 5.6rem)', overflow: 'auto' }}>
         <TabsContent
-          small={true}
+          small
+          spacing={2.5}
           center={false}
           defaultActiveKey="1"
           items={data?.map((item) => {

+ 1 - 1
src/pages/SmartOps/HistoryRecord.js

@@ -19,7 +19,7 @@ const HistoryRecord = (props) => {
   const convertObject2FormData = (params) => {
     const formData = new FormData();
     Object.entries(params).forEach(([key, value]) => {
-      if (value !== null && value !== undefined && value !== NaN) {
+      if (value !== null && value !== undefined && !isNaN(value)) {
         formData.append(key, value);
       }
     });

+ 1 - 1
src/pages/SmartOps/OperationRecord.js

@@ -18,7 +18,7 @@ const OperationRecord = (props) => {
   const convertObject2FormData = (params) => {
     const formData = new FormData();
     Object.entries(params).forEach(([key, value]) => {
-      if (value !== null && value !== undefined && value !== NaN) {
+      if (value !== null && value !== undefined && !isNaN(value)) {
         formData.append(key, value);
       }
     });

+ 1 - 0
src/pages/SmartOps/WorkAnalysisDetail.js

@@ -200,6 +200,7 @@ function WorkAnalysisDetail(props) {
         <TabsContent
           center={false}
           small
+          spacing={2.5}
           defaultActiveKey={active}
           items={technologys.map((item) => ({
             label: TYPE[item]?.name,

+ 74 - 75
src/pages/SmartOps/components/DeviceAnalysis.js

@@ -3,7 +3,7 @@ import TabsContent from '@/components/TabsContent';
 import ThresholdDetail from '@/components/ThresholdDetail';
 import { UnityAction } from '@/utils/utils';
 import { connect } from '@umijs/max';
-import { Spin, Table, Tabs } from 'antd';
+import { Table, Tabs } from 'antd';
 import dayjs from 'dayjs';
 import { useEffect, useMemo, useState } from 'react';
 import styles from './DeviceAnalysis.less';
@@ -122,10 +122,10 @@ const DeviceAnalysis = (props) => {
         WaterInCheckList: autoReport?.WaterInCheckList?.filter(
           (item) => item.status,
         ),
-        PressureCompareList: autoReport?.PressureCompareList.filter(
+        PressureCompareList: autoReport?.PressureCompareList?.filter(
           (item) => !item.history,
         ),
-        WaterQualityCompareList: autoReport?.WaterQualityCompareList.filter(
+        WaterQualityCompareList: autoReport?.WaterQualityCompareList?.filter(
           (item) => !item.history,
         ),
       },
@@ -183,79 +183,78 @@ const DeviceAnalysis = (props) => {
   };
 
   return (
-    <Spin spinning={loading}>
-      <div style={{ height: 'calc(100vh - 5.6rem)', overflow: 'auto' }}>
-        <TabsContent
-          small={true}
-          center={false}
-          defaultActiveKey="1"
-          items={data?.map((item) => {
-            return {
-              label: `${item.name}(${calculateLength(item) || 0})`,
-              key: item.type,
-              children: (
-                <>
-                  {(item.type === '1' ? item?.data?.length > 0 : true) && (
-                    <>
-                      <ModuleTitle title="设备检测" />
-                      <Table
-                        dataSource={item.data}
-                        columns={columns}
-                        rowKey="Id"
-                        rowClassName={setRowClassName}
-                        onRow={(record, index) => ({
-                          onClick: () => onSelectRow(record, index),
-                        })}
-                        pagination={false}
-                        // scroll={{ y: document.body.clientHeight - 730 }}
-                      />
-                    </>
-                  )}
-                  {(item.type === '1'
-                    ? item?.FluidLevelList.length > 0
-                    : true) && (
-                    <LiquidLevel
-                      allData={item?.FluidLevelList}
-                      type={item.type}
+    <div style={{ height: 'calc(100vh - 5.6rem)', overflow: 'auto' }}>
+      <TabsContent
+        small
+        spacing={2.5}
+        center={false}
+        defaultActiveKey="1"
+        items={data?.map((item) => {
+          return {
+            label: `${item.name}(${calculateLength(item) || 0})`,
+            key: item.type,
+            children: (
+              <>
+                {(item.type === '1' ? item?.data?.length > 0 : true) && (
+                  <>
+                    <ModuleTitle title="设备检测" />
+                    <Table
+                      dataSource={item.data}
+                      columns={columns}
+                      rowKey="Id"
+                      rowClassName={setRowClassName}
+                      onRow={(record, index) => ({
+                        onClick: () => onSelectRow(record, index),
+                      })}
+                      pagination={false}
+                      // scroll={{ y: document.body.clientHeight - 730 }}
                     />
-                  )}
-                  {(item.type === '1'
-                    ? item?.DrugFlowList.length > 0
-                    : true) && (
-                    <DosingFlow allData={item?.DrugFlowList} type={item.type} />
-                  )}
-                  {(item.type === '1'
-                    ? item?.WaterInCheckList.length > 0
-                    : true) && (
-                    <WaterFlow
-                      allData={item?.WaterInCheckList}
-                      type={item.type}
-                    />
-                  )}
-                  {(item.type === '1'
-                    ? item?.PressureCompareList.length > 0
-                    : true) && (
-                    <PressureGauge
-                      allData={item?.PressureCompareList}
-                      type={item.type}
-                    />
-                  )}
-                  {(item.type === '1'
-                    ? item?.WaterQualityCompareList.length > 0
-                    : true) && (
-                    <WaterQuality
-                      allData={item?.WaterQualityCompareList}
-                      type={item.type}
-                    />
-                  )}
-                </>
-              ),
-            };
-          })}
-          onChange={onTabChange}
-        />
-      </div>
-    </Spin>
+                  </>
+                )}
+                {(item.type === '1'
+                  ? item?.FluidLevelList?.length > 0
+                  : true) && (
+                  <LiquidLevel
+                    allData={item?.FluidLevelList}
+                    type={item.type}
+                  />
+                )}
+                {(item.type === '1'
+                  ? item?.DrugFlowList?.length > 0
+                  : true) && (
+                  <DosingFlow allData={item?.DrugFlowList} type={item.type} />
+                )}
+                {(item.type === '1'
+                  ? item?.WaterInCheckList?.length > 0
+                  : true) && (
+                  <WaterFlow
+                    allData={item?.WaterInCheckList}
+                    type={item.type}
+                  />
+                )}
+                {(item.type === '1'
+                  ? item?.PressureCompareList?.length > 0
+                  : true) && (
+                  <PressureGauge
+                    allData={item?.PressureCompareList}
+                    type={item.type}
+                  />
+                )}
+                {(item.type === '1'
+                  ? item?.WaterQualityCompareList?.length > 0
+                  : true) && (
+                  <WaterQuality
+                    allData={item?.WaterQualityCompareList}
+                    type={item.type}
+                  />
+                )}
+              </>
+            ),
+          };
+        })}
+        onChange={onTabChange}
+      />
+    </div>
   );
 };
 export default connect(({ eqSelfInspection, loading }) => ({

+ 27 - 0
src/pages/SmartOps/components/SubTitle.js

@@ -0,0 +1,27 @@
+const SubTitle = ({ title, fontSize = '0.28rem' }) => {
+  return (
+    <div
+      style={{
+        display: 'flex',
+        justifyContent: 'flex-start',
+        alignItems: 'center',
+        marginBottom: '0.1rem',
+        fontSize,
+        fontWeight: '600',
+      }}
+    >
+      <div
+        style={{
+          width: '0.15rem',
+          height: '0.15rem',
+          background: '#1755ff',
+          marginRight: '0.1rem',
+          borderRadius: '0.16rem',
+        }}
+      />
+      {title}
+    </div>
+  );
+};
+
+export default SubTitle;

+ 2 - 1
src/pages/SmartOps/components/VideoAnalysis.js

@@ -74,7 +74,8 @@ function VideoAnalysis(props) {
     <Spin spinning={loading}>
       <div style={{ height: 'calc(100vh - 5.6rem)', overflow: 'auto' }}>
         <TabsContent
-          small={true}
+          small
+          spacing={2.5}
           center={false}
           defaultActiveKey="1"
           items={[

+ 9 - 1
src/pages/SmartOps/index.js

@@ -23,6 +23,7 @@ import DeviceAnalysis from './components/DeviceAnalysis';
 import VideoAnalysis from './components/VideoAnalysis';
 import WorkAnalysis from './components/WorkAnalysis';
 import styles from './index.less';
+import OperationManage from './operationManage';
 
 const { TabPane } = Tabs;
 const icon06 = require('@/assets/smartOps/icon06.png');
@@ -324,6 +325,8 @@ function SmartOps(props) {
       <div className={styles.tabContent}>
         <TabsContent
           defaultActiveKey="1"
+          center={false}
+          spacing={2.5}
           small
           items={[
             {
@@ -359,6 +362,11 @@ function SmartOps(props) {
                 />
               ),
             },
+            {
+              label: `经营分析(-)`,
+              key: '5',
+              children: <OperationManage />,
+            },
           ]}
           onChange={onChangeTab}
         />
@@ -370,7 +378,7 @@ function SmartOps(props) {
 const convertObject2FormData = (params) => {
   const formData = new FormData();
   Object.entries(params).forEach(([key, value]) => {
-    if (value !== null && value !== undefined && value !== NaN) {
+    if (value !== null && value !== undefined && !isNaN(value)) {
       formData.append(key, value);
     }
   });

+ 203 - 0
src/pages/SmartOps/operationManage/CostAnalysis/CostAnalysis.js

@@ -0,0 +1,203 @@
+import ChartModule from '@/components/ManagementPage/chartModule';
+import {
+  queryEnergyChartList,
+  queryProcessSection,
+} from '@/services/OperationManagement';
+import { useParams, useRequest } from '@umijs/max';
+import { Button, DatePicker, Table } from 'antd';
+import dayjs from 'dayjs';
+import SubTitle from '../../components/SubTitle';
+import styles from './CostAnalysis.less';
+
+const { RangePicker } = DatePicker;
+
+const CostAnalysis = () => {
+  const { projectId } = useParams();
+
+  const defaultTime = {
+    s_time: dayjs().subtract(7, 'day').format('YYYY-MM-DD 00:00:00'),
+    e_time: dayjs().format('YYYY-MM-DD 23:59:59'),
+  };
+
+  const columns = [
+    {
+      title: '时间',
+      dataIndex: 'data_time',
+    },
+    {
+      title: '能耗(kWh)',
+      dataIndex: 'electric',
+    },
+    {
+      title: '药耗(kg)',
+      dataIndex: 'medicine',
+    },
+    {
+      title: '进水水量(t)',
+      dataIndex: 'waterIn',
+    },
+    {
+      title: '出水水量(t)',
+      dataIndex: 'waterOut',
+    },
+  ];
+
+  const { data, loading, run } = useRequest(
+    (date) =>
+      queryEnergyChartList({
+        project_id: Number(projectId),
+        start_time: date.s_time,
+        end_time: date.e_time,
+        order: 1,
+      }),
+    {
+      defaultParams: [{ ...defaultTime }],
+      formatResult: (data) => {
+        const tempData = data.data;
+        const arr = Object.values(tempData).find((arr) => arr);
+
+        return {
+          chartData: {
+            yName: ['药耗(kg)', '能耗(kWh)', '水量(t)'],
+            xData: arr?.map((item) => item.data_time),
+            dataList: [
+              {
+                type: 0,
+                name: '能耗',
+                yIndex: 1,
+                data: tempData.electric?.map((item) => item.value),
+              },
+              {
+                type: 0,
+                name: '药耗',
+                data: tempData.medicine?.map((item) => item.value),
+              },
+              {
+                type: 0,
+                name: '进水水量',
+                yIndex: 2,
+                data: tempData.waterIn?.map((item) => item.value),
+              },
+              {
+                type: 2,
+                name: '出水水量',
+                yIndex: 2,
+                data: tempData.waterOut?.map((item) => item.value),
+              },
+            ],
+          },
+          tableData: tempData?.electric?.map((item, idx) => {
+            return {
+              key: `analy_table_${idx}`,
+              // value: item.value?.toFixed(2) | '-',
+              data_time: item.data_time,
+              electric: formatElcData(tempData?.electric, item?.data_time),
+              medicine:
+                tempData.medicine
+                  ?.find((cur) => cur.data_time === item.data_time)
+                  ?.value?.toFixed(2) || '-',
+              waterIn:
+                tempData.waterIn
+                  ?.find((cur) => cur.data_time === item.data_time)
+                  ?.value?.toFixed(2) || '-',
+              waterOut:
+                tempData.waterOut
+                  ?.find((cur) => cur.data_time === item.data_time)
+                  ?.value?.toFixed(2) || '-',
+            };
+          }),
+        };
+      },
+    },
+  );
+
+  const ProcessSectionRequest = useRequest(queryProcessSection, {
+    manual: true,
+    onSuccess: (data) => {
+      if (data.length > 0) {
+        let section;
+        section = data[0];
+        setTitle(section.name);
+      }
+    },
+  });
+
+  const formatElcData = (data, data_time) => {
+    const value = data?.find((cur) => cur.data_time == data_time)?.value;
+    return value === 0 ? '-' : value.toFixed(2);
+  };
+
+  const handleTimeRangeChange = (range) => {
+    if (range?.length === 2) {
+      run({
+        s_time: dayjs(range[0]).format('YYYY-MM-DD 00:00:00'),
+        e_time: dayjs(range[1]).format('YYYY-MM-DD HH:mm:ss'),
+      });
+    } else {
+      run(defaultTime);
+    }
+  };
+
+  const handleTimeBTNClick = (unit) => {
+    run({
+      s_time: dayjs().subtract(1, unit).format('YYYY-MM-DD 00:00:00'),
+      e_time: dayjs().format('YYYY-MM-DD HH:mm:ss'),
+    });
+  };
+
+  return (
+    <div className="card-box" style={{ padding: '0.2rem' }}>
+      <div>
+        <SubTitle title="全厂概览" />
+        <div className={styles.timeBtn}>
+          <RangePicker onChange={handleTimeRangeChange} />
+          <Button
+            type="primary"
+            shape="round"
+            onClick={() => {
+              handleTimeBTNClick('day');
+            }}
+          >
+            近一天
+          </Button>
+          <Button
+            type="primary"
+            shape="round"
+            onClick={() => {
+              handleTimeBTNClick('week');
+            }}
+          >
+            近一周
+          </Button>
+          <Button
+            type="primary"
+            shape="round"
+            onClick={() => {
+              handleTimeBTNClick('month');
+            }}
+          >
+            近一个月
+          </Button>
+        </div>
+      </div>
+      <div>
+        <SubTitle title={'数据曲线'} fontSize="0.26rem" />
+        <div style={{ height: '3.5rem', marginBottom: '0.1rem' }}>
+          {data?.chartData && <ChartModule {...data.chartData} />}
+        </div>
+      </div>
+      <div>
+        <SubTitle title={'数据列表'} fontSize="0.26rem" />
+        <Table
+          loading={loading}
+          columns={columns}
+          dataSource={data?.tableData?.sort((a, b) =>
+            b?.data_time?.localeCompare(a?.data_time),
+          )}
+        />
+      </div>
+    </div>
+  );
+};
+
+export default CostAnalysis;

+ 12 - 0
src/pages/SmartOps/operationManage/CostAnalysis/CostAnalysis.less

@@ -0,0 +1,12 @@
+.timeBtn > * {
+  margin-right: 0.1rem;
+}
+.timeBtn {
+  margin-bottom: 0.1rem;
+  margin-right: 0.1rem;
+  :global {
+    .ant-btn {
+      height: 0.5rem;
+    }
+  }
+}

+ 69 - 0
src/pages/SmartOps/operationManage/index.js

@@ -0,0 +1,69 @@
+import TabsContent from '@/components/TabsContent';
+import { ChemCost } from '@/pages/Home/ChemCostComparison';
+import { EnergyCost } from '@/pages/Home/EnergyCostComparison';
+import { WaterQuality } from '@/pages/Home/QualityMng';
+import { WaterAmt } from '@/pages/Home/WaterAmtMng';
+import { useState } from 'react';
+import CostAnalysis from './CostAnalysis/CostAnalysis';
+
+const OperationManage = () => {
+  const [showEnergyDetail, setShowEnergyDetail] = useState(false);
+
+  const items = [
+    {
+      label: '成本分析',
+      key: '1',
+      children: <CostAnalysis />,
+    },
+    {
+      label: '水质分析',
+      key: '2',
+      children: <WaterQuality />,
+    },
+    {
+      label: '水量分析',
+      key: '3',
+      children: <WaterAmt />,
+    },
+    {
+      label: '药耗分析',
+      key: '4',
+      children: <ChemCost open />,
+    },
+    {
+      label: '能耗分析',
+      key: '5',
+      children: showEnergyDetail ? (
+        <div
+          onClick={() => {
+            setShowEnergyDetail(!showEnergyDetail);
+          }}
+        >
+          能耗详情
+        </div>
+      ) : (
+        <EnergyCost
+          open
+          detailClick={() => {
+            setShowEnergyDetail(!showEnergyDetail);
+          }}
+        />
+      ),
+    },
+  ];
+
+  const onTabsChange = () => {};
+
+  return (
+    <TabsContent
+      small
+      spacing={2.5}
+      center={false}
+      defaultActiveKey="1"
+      items={items}
+      onChange={onTabsChange}
+    />
+  );
+};
+
+export default OperationManage;

+ 510 - 55
src/pages/TaskManage/Detail/TaskDetail/TaskDetail.tsx

@@ -15,24 +15,33 @@ import {
   MandateType,
   OrderStatus,
   OrderType,
+  ignoreReason,
 } from '@/pages/TaskManage/constent';
 import {
+  dispatchOrder,
   getDiagnosticDetail,
   getMandateDetail,
+  ignoreTaskRequest,
+  setTaskAutomation,
   withdrawOrderRequest,
 } from '@/services/TaskManage';
 import { useLocation } from '@@/exports';
 import { CaretDownFilled } from '@ant-design/icons';
 import { connect, useRequest } from '@umijs/max';
 import {
+  Button,
+  Checkbox,
   Col,
   Collapse,
   CollapseProps,
+  ConfigProvider,
+  DatePicker,
   Divider,
   Form,
   Input,
   Modal,
   Row,
+  Select,
   Table,
   message,
 } from 'antd';
@@ -42,8 +51,271 @@ import { useEffect, useState } from 'react';
 // @ts-ignore
 import ReactZmage from 'react-zmage';
 import { useNavigate } from 'umi';
+
+import zhCN from 'antd/es/locale/zh_CN';
+
+import { UnityAction } from '@/utils/utils';
 import styles from './taskDetail.less';
 
+const IgnoreTaskModal = (params: any) => {
+  const { open, onCancel, onOk } = params;
+
+  const [ignoreReasonText, setIgnoreReasonText] = useState('');
+  const [selectedReason, setSelectedReason] = useState<any>({});
+  const [showInput, setShowInput] = useState(false);
+
+  const onReasonChange = (reason: any, option: any) => {
+    if (reason !== 4) {
+      setSelectedReason(option);
+      setShowInput(false);
+    } else {
+      setShowInput(true);
+    }
+  };
+
+  const onReasonTextChange = (e: any) => {
+    setIgnoreReasonText(e.target.value);
+  };
+
+  const handleCancle = () => {
+    setSelectedReason({});
+    setIgnoreReasonText('');
+    setShowInput(false);
+    onCancel();
+  };
+
+  const confirmIgnore = () => {
+    if (showInput) {
+      if (!ignoreReasonText.length) {
+        message.warning('请输入忽略理由');
+      } else {
+        onOk(ignoreReasonText);
+      }
+    } else {
+      if (selectedReason?.label) {
+        onOk(selectedReason.label);
+      } else {
+        message.warning('请选择忽略理由');
+      }
+    }
+  };
+
+  return (
+    <Modal
+      className={styles.handleModal}
+      title="忽略"
+      maskStyle={{ borderRadius: 0 }}
+      open={open}
+      onCancel={handleCancle}
+      onOk={confirmIgnore}
+      width="60%"
+      destroyOnClose
+    >
+      <div style={{ padding: '0.15rem' }}>
+        <Form>
+          <Form.Item label="忽略理由:">
+            <Select
+              className={styles.fontS28}
+              options={ignoreReason}
+              onChange={onReasonChange}
+              allowClear
+            />
+          </Form.Item>
+          {showInput && (
+            <Form.Item label="输入理由:">
+              <Input placeholder="请输入理由" onChange={onReasonTextChange} />
+            </Form.Item>
+          )}
+        </Form>
+      </div>
+    </Modal>
+  );
+};
+
+const AutoHandleModal = (props: any) => {
+  const { open, onCancel, onOk } = props;
+
+  const [automation, setAutomation] = useState<string>();
+
+  const confirmAutoHandle = () => {
+    if (automation && automation.length) {
+      onOk(automation);
+    } else {
+      message.warning('请输入口令');
+    }
+  };
+
+  return (
+    <Modal
+      className={styles.handleModal}
+      title="自动处理"
+      maskStyle={{ borderRadius: 0 }}
+      open={open}
+      onCancel={onCancel}
+      onOk={confirmAutoHandle}
+      width="60%"
+      destroyOnClose
+    >
+      <div style={{ padding: '0.15rem' }}>
+        <Form>
+          <Form.Item label="用户口令:">
+            {
+              <Input
+                autoFocus
+                style={{ width: '100%' }}
+                placeholder="请输入口令"
+                onChange={(e) => {
+                  setAutomation(e.target.value);
+                }}
+              />
+            }
+          </Form.Item>
+        </Form>
+      </div>
+    </Modal>
+  );
+};
+
+const MandateSelectModal = (props: any) => {
+  const { open, onCancel, list, onOk, selectedTask, setSelectedTask } = props;
+
+  const [checkOptions, setCheckOptions] = useState([]);
+
+  useEffect(() => {
+    setCheckOptions(
+      list.map((mandate: any, index: number) => {
+        return {
+          label: (
+            <Row className={styles.taskCheckItem}>
+              <span
+                style={{
+                  textDecoration: `${
+                    mandate.Status === 0 ? '' : 'line-through'
+                  }`,
+                }}
+              >
+                {`${index + 1}. ${mandate.Title}为${mandate.Content}`}
+              </span>
+            </Row>
+          ),
+          value: mandate.Id,
+          disabled: mandate.Status !== 0,
+        };
+      }),
+    );
+  }, [list]);
+
+  const onDispatchClick = () => {
+    onOk(selectedTask);
+  };
+
+  const handleCheckChange = (checkedValue: any) => {
+    setSelectedTask(checkedValue);
+  };
+
+  return (
+    <Modal
+      className={styles.handleModal}
+      title="选择任务"
+      open={open}
+      onCancel={onCancel}
+      width={'80%'}
+      destroyOnClose
+      footer={[
+        <Button key="back" onClick={onCancel}>
+          取消
+        </Button>,
+        <Button key="dispatch" type="primary" onClick={onDispatchClick}>
+          派单
+        </Button>,
+      ]}
+    >
+      <Checkbox.Group
+        className={styles.taskCheckBox}
+        options={checkOptions}
+        onChange={handleCheckChange}
+      />
+    </Modal>
+  );
+};
+
+const DispatchTaskModal = (props: any) => {
+  const { open, onCancel, onOK, userList } = props;
+
+  const [form] = Form.useForm();
+
+  useEffect(() => {
+    if (!open) {
+      form.resetFields();
+    }
+  }, [open]);
+
+  const handleDispatchConfirm = async () => {
+    const value = await form.validateFields().catch((err) =>
+      err.errorFields.forEach((item) => {
+        message.error(item.errors);
+      }),
+    );
+    if (!value) {
+      return;
+    }
+    onOK(value);
+  };
+
+  return (
+    <Modal
+      className={styles.handleModal}
+      title="派遣任务"
+      onCancel={onCancel}
+      open={open}
+      destroyOnClose
+      width="65%"
+      onOk={handleDispatchConfirm}
+    >
+      <Form
+        form={form}
+        layout="horizontal"
+        labelCol={{ span: 6 }}
+        wrapperCol={{ span: 16 }}
+      >
+        <Form.Item
+          label="工单类型"
+          name="type"
+          rules={[{ required: true, message: '请选择工单类型' }]}
+        >
+          <Select options={OrderType} placeholder="请选择工单类型" />
+        </Form.Item>
+        <Form.Item
+          label="操作人"
+          name="operator_id"
+          rules={[{ required: true, message: '请选择操作人' }]}
+        >
+          <Select
+            options={userList.map((item) => {
+              return {
+                label: item.CName,
+                value: item.ID,
+              };
+            })}
+            placeholder="请选择操作人"
+          />
+        </Form.Item>
+        <Form.Item
+          label="计划完成时间"
+          name="plan_end_time"
+          rules={[{ required: true, message: '请选择完成时间' }]}
+        >
+          <DatePicker
+            inputReadOnly
+            style={{ width: '100%' }}
+            placeholder="请选择完成时间"
+          />
+        </Form.Item>
+      </Form>
+    </Modal>
+  );
+};
+
 interface IPropsType {
   userList: IUserType[];
   dispatch: (args: { type: string; payload: object }) => void;
@@ -67,7 +339,13 @@ function TaskDetail(props: IPropsType) {
   const [mandateTable, setMandateTable] = useState<IColumn[]>([]);
   const [withdrawReason, setWithdrawReason] = useState('');
   const [withdrawOrderOpen, setWithdrawOrderOpen] = useState(false);
-  const [clickedOrder, setClickedOrder] = useState({});
+  const [clickedOrder, setClickedOrder] = useState<any>({});
+
+  const [ignoreModalOpen, setIgnoreModalOpen] = useState(false);
+  const [autoHandleModalOpen, setAutoHandleModalOpen] = useState(false);
+  const [mandateSelectModalOpen, setMandateSelectModalOpen] = useState(false);
+  const [selectedTask, setSelectedTask] = useState([]);
+  const [dispatchModalOpen, setDispatchModalOpen] = useState(false);
 
   const columnDef: ColumnsType<IColumn> = [
     {
@@ -86,19 +364,6 @@ function TaskDetail(props: IPropsType) {
     },
   ];
 
-  const base64ToImageUrl = (base64String: string) => {
-    const byteCharacters = atob(base64String);
-    const byteArrays = [];
-
-    for (let i = 0; i < byteCharacters.length; i++) {
-      byteArrays.push(byteCharacters.charCodeAt(i));
-    }
-
-    const byteArray = new Uint8Array(byteArrays);
-    const blob = new Blob([byteArray], { type: 'image/png' });
-    return URL.createObjectURL(blob);
-  };
-
   const { refresh: refreshDetail } = useRequest(getMandateDetail, {
     defaultParams: [
       {
@@ -230,6 +495,121 @@ function TaskDetail(props: IPropsType) {
     },
   });
 
+  const { run: runIgnore } = useRequest(ignoreTaskRequest, {
+    manual: true,
+  });
+
+  const { run: runDispatch } = useRequest(dispatchOrder, {
+    manual: true,
+  });
+
+  const { run: runAutomate } = useRequest(setTaskAutomation, {
+    manual: true,
+  });
+
+  // 忽略
+  const onIgnoreTaskConfirm = async (id: any, reason: string) => {
+    const params = {
+      Id: id,
+      Status: 4,
+      note: reason,
+    };
+    const result = await runIgnore(params);
+    if (result) {
+      return true;
+    }
+  };
+  // 派单
+  const onDispatchTaskConfirm = async (params: any) => {
+    const result = await runDispatch(params);
+    if (result) {
+      return true;
+    }
+  };
+  // 自动处理
+  const onAutoHandleTaskConfirm = async (pw: any, mandate: any) => {
+    const params = {
+      mandate_id: mandate.Id,
+      pw,
+    };
+    const result = runAutomate(params, mandate);
+    if (result) {
+      return true;
+    }
+  };
+
+  // 打开指定弹窗
+  const openSpecifiedModal = (type: any) => {
+    switch (type) {
+      case 'ignore':
+        setIgnoreModalOpen(true);
+        break;
+      case 'manual':
+        UnityAction.sendMsg('menuItem', '工艺监控');
+        break;
+      case 'auto':
+        setAutoHandleModalOpen(true);
+        break;
+      case 'dispatch':
+        setMandateSelectModalOpen(true);
+        break;
+    }
+  };
+
+  // 忽略
+  const onIgnoreModalOk = async (reason: any) => {
+    const result = await onIgnoreTaskConfirm(mandate_id, reason);
+    if (result) {
+      setIgnoreModalOpen(false);
+      refreshDetail();
+    }
+  };
+
+  const onAutoHandleModalOk = async (pw: any) => {
+    const result = await onAutoHandleTaskConfirm(pw, mandateDetail);
+    if (result) {
+      setAutoHandleModalOpen(false);
+      refreshDetail();
+    }
+  };
+
+  const onMandateSelected = (records: any) => {
+    // 打开派单Form弹窗将选中的任务进行派遣
+    if (records?.length === 0) {
+      message.warning('请先选择要派遣的任务');
+      return;
+    }
+    setSelectedTask(records);
+    setDispatchModalOpen(true);
+  };
+
+  const onDispatchModalOk = async (value: any) => {
+    const params = {
+      ...value,
+      m_id: Number(mandate_id),
+      mc_id: selectedTask.join(),
+      plan_end_time: dayjs(value.plan_end_time).format('YYYY-MM-DD HH:mm:ss'),
+    };
+    if (params.type === 5) {
+      if (params.mc_id.split(',').length > 1) {
+        message.warning('加药工单不可批量派遣');
+        return;
+      }
+      params.note = `${
+        mandateChild
+          .find((mandate) => mandate.Id === Number(params.mc_id))
+          ?.Title?.split(':')[1] + ',请及时加药'
+      }`;
+    } else {
+      params.note = mandateDetail?.Summary;
+    }
+    const result = await onDispatchTaskConfirm(params);
+    if (result) {
+      setDispatchModalOpen(false);
+      refreshDetail();
+    }
+  };
+
   useEffect(() => {
     if (userList.length === 0) {
       dispatch({
@@ -292,9 +672,9 @@ function TaskDetail(props: IPropsType) {
       return;
     }
     const res = await withdrawOrderRequest({
-      record_id: clickedOrder.Id,
+      record_id: clickedOrder?.Id,
       note: withdrawReason,
-      type: clickedOrder.RecordType.value,
+      type: clickedOrder?.RecordType?.value,
     });
     if (res.code === 200) {
       message.success('关闭工单成功');
@@ -380,46 +760,47 @@ function TaskDetail(props: IPropsType) {
               </Row>
             )}
 
-            {mandateDetail?.Files.length > 0 && (
-              <Row className={styles.infoRow}>
-                <Col
-                  className={styles.fontS30}
-                  span={4}
-                  style={{ fontWeight: 600 }}
-                >
-                  截图
-                </Col>
-                <Col className={styles.fontS30}>
-                  <ReactZmage
-                    controller={{
-                      // 关闭按钮
-                      close: true,
-                      // 缩放按钮
-                      zoom: false,
-                      // 下载按钮
-                      download: false,
-                      // 翻页按钮
-                      flip: true,
-                      // 多页指示
-                      pagination: true,
-                    }}
-                    backdrop="rgba(255,255,255,0.5)"
-                    style={{ width: '3.5rem' }}
-                    src={mandateDetail?.Files[0].url}
-                    set={mandateDetail?.Files.map((item) => {
-                      if (item) {
-                        return {
-                          src: item.url,
-                        };
-                      }
-                      return {};
-                    })}
-                  />
-                </Col>
-              </Row>
-            )}
+            {mandateDetail?.Files !== undefined &&
+              mandateDetail?.Files?.length > 0 && (
+                <Row className={styles.infoRow}>
+                  <Col
+                    className={styles.fontS30}
+                    span={4}
+                    style={{ fontWeight: 600 }}
+                  >
+                    截图
+                  </Col>
+                  <Col className={styles.fontS30}>
+                    <ReactZmage
+                      controller={{
+                        // 关闭按钮
+                        close: true,
+                        // 缩放按钮
+                        zoom: false,
+                        // 下载按钮
+                        download: false,
+                        // 翻页按钮
+                        flip: true,
+                        // 多页指示
+                        pagination: true,
+                      }}
+                      backdrop="rgba(255,255,255,0.5)"
+                      style={{ width: '3.5rem' }}
+                      src={mandateDetail?.Files[0].url}
+                      set={mandateDetail?.Files.map((item) => {
+                        if (item) {
+                          return {
+                            src: item.url,
+                          };
+                        }
+                        return {};
+                      })}
+                    />
+                  </Col>
+                </Row>
+              )}
 
-            <Row>
+            <Row className={styles.infoRow}>
               <Col
                 className={styles.fontS30}
                 span={4}
@@ -438,6 +819,52 @@ function TaskDetail(props: IPropsType) {
                 />
               </Col>
             </Row>
+            <Row justify="end" gutter={10}>
+              <Col>
+                <Button
+                  className={styles.footerBtn}
+                  shape="round"
+                  onClick={() => {
+                    openSpecifiedModal('ignore');
+                  }}
+                >
+                  忽略
+                </Button>
+              </Col>
+              <Col>
+                <Button
+                  className={styles.footerBtn}
+                  shape="round"
+                  onClick={() => {
+                    openSpecifiedModal('manual');
+                  }}
+                >
+                  手动处理
+                </Button>
+              </Col>
+              <Col>
+                <Button
+                  className={styles.footerBtn}
+                  shape="round"
+                  onClick={() => {
+                    openSpecifiedModal('auto');
+                  }}
+                >
+                  自动处理
+                </Button>
+              </Col>
+              <Col>
+                <Button
+                  className={styles.footerBtn}
+                  shape="round"
+                  onClick={() => {
+                    openSpecifiedModal('dispatch');
+                  }}
+                >
+                  派单
+                </Button>
+              </Col>
+            </Row>
           </div>
           <div className={styles.relatedOrder}>
             <Collapse
@@ -473,6 +900,34 @@ function TaskDetail(props: IPropsType) {
           </Form.Item>
         </Form>
       </Modal>
+      <ConfigProvider locale={zhCN}>
+        <IgnoreTaskModal
+          open={ignoreModalOpen}
+          onCancel={() => setIgnoreModalOpen(false)}
+          onOk={onIgnoreModalOk}
+        />
+        <AutoHandleModal
+          open={autoHandleModalOpen}
+          onCancel={() => setAutoHandleModalOpen(false)}
+          onOk={onAutoHandleModalOk}
+        />
+        <MandateSelectModal
+          open={mandateSelectModalOpen}
+          onCancel={() => setMandateSelectModalOpen(false)}
+          selectedTask={selectedTask}
+          setSelectedTask={setSelectedTask}
+          onOk={onMandateSelected}
+          list={mandateChild}
+        />
+        <DispatchTaskModal
+          open={dispatchModalOpen}
+          userList={userList}
+          onCancel={() => {
+            setDispatchModalOpen(false);
+          }}
+          onOK={onDispatchModalOk}
+        />
+      </ConfigProvider>
     </PageContent>
   );
 }

+ 28 - 29
src/pages/TaskManage/Detail/TaskDetail/taskDetail.less

@@ -112,35 +112,34 @@
     }
   }
 
-  // .workOrderCard {
-  //   margin-bottom: 0.25rem;
-  //   padding: 0.2rem 0.1rem;
-  //   background-color: #e5effa;
-  //   display: flex;
-  //   align-items: center;
-
-  //   .leftInfo {
-  //     width: 80%;
-  //   }
-
-  //   .rightButtonContainer {
-  //     width: 20%;
-  //     display: flex;
-  //     flex-direction: column;
-  //     justify-content: space-between;
-  //     align-items: center;
-  //   }
-
-  //   .rightButton {
-  //     flex: auto;
-  //     color: #5697e4;
-  //     font-size: 0.24rem;
-  //     text-align: center;
-  //     display: flex;
-  //     justify-content: center;
-  //     align-items: center;
-  //   }
-  // }
+  .footerBtn {
+    height: 0.5rem;
+    width: 1.4rem;
+    font-size: 0.26rem;
+    border: 0;
+    color: #5697e4;
+    background-color: #e5effa;
+  }
+}
+
+.taskCheckBox {
+  padding: 0.1rem;
+  :global {
+    .ant-checkbox-wrapper {
+      width: 100%;
+      margin-top: 0.05rem;
+      margin-bottom: 0.05rem;
+      border-bottom: 0.01rem dashed gray;
+    }
+  }
+}
+
+.taskCheckItem {
+  font-size: 0.28rem;
+  padding: 0.1rem;
+  min-width: 0.8rem;
+  display: flex;
+  align-items: center;
 }
 
 .handleModal {

+ 4 - 0
src/pages/TaskManage/components/MandateDetail.js

@@ -419,6 +419,7 @@ const IgnoreTaskModal = (params) => {
     <Modal
       className={styles.handleModal}
       title="忽略"
+      maskStyle={{ borderRadius: '0.5rem' }}
       open={open}
       onCancel={handleCancle}
       onOk={confirmIgnore}
@@ -463,6 +464,7 @@ const AutoHandleModal = (props) => {
     <Modal
       className={styles.handleModal}
       title="自动处理"
+      maskStyle={{ borderRadius: '0.5rem' }}
       open={open}
       onCancel={onCancel}
       onOk={confirmAutoHandle}
@@ -530,6 +532,7 @@ const MandateSelectModal = (props) => {
     <Modal
       className={styles.handleModal}
       title="选择任务"
+      maskStyle={{ borderRadius: '0.5rem' }}
       open={open}
       onCancel={onCancel}
       width={'80%'}
@@ -579,6 +582,7 @@ const DispatchTaskModal = (props) => {
     <Modal
       className={styles.handleModal}
       title="派遣任务"
+      maskStyle={{ borderRadius: '0.5rem' }}
       onCancel={onCancel}
       open={open}
       destroyOnClose

+ 1 - 1
src/pages/TaskManage/components/TopFilter.tsx

@@ -40,7 +40,7 @@ const TopFilter: React.FC<IProps> = ({ filters, onChange }) => {
                 options={item.options}
                 popupMatchSelectWidth={250}
                 allowClear
-                suffixIcon={<CaretDownFilled />}
+                suffixIcon={<CaretDownFilled style={{ fontSize: '0.36rem' }} />}
                 onChange={(value) => {
                   const temp = filters.map((f, i) => {
                     if (index === i) {

+ 16 - 16
src/pages/TaskManage/constent.ts

@@ -23,12 +23,12 @@ export const MandateClass = [
     MandateType: 2,
     OrderType: 1,
   },
-  {
-    value: 5,
-    label: '电气检测',
-    MandateType: 2,
-    OrderType: 2,
-  },
+  // {
+  //   value: 5,
+  //   label: '电气检测',
+  //   MandateType: 3,
+  //   OrderType: 2,
+  // },
   {
     value: 6,
     label: '环境检测',
@@ -62,56 +62,56 @@ export const MandateClass = [
   {
     value: 11,
     label: '故障上报',
-    MandateType: 3,
+    MandateType: 2,
     OrderType: 2,
   },
   {
     value: 12,
     label: '工艺数据',
-    MandateType: 3,
+    MandateType: 2,
     OrderType: 2,
   },
   {
     value: 13,
     label: '设备巡检',
-    MandateType: 3,
+    MandateType: 2,
     OrderType: 2,
   },
   {
     value: 14,
     label: '数据超限',
-    MandateType: 3,
+    MandateType: 2,
     OrderType: 2,
   },
   {
+    value: 15,
     label: '现场巡检',
     MandateType: 3,
-    value: 15,
   },
   {
+    value: 16,
     label: '备品/盘点',
     MandateType: 3,
-    value: 16,
   },
   {
+    value: 17,
     label: '集团任务',
     MandateType: 4,
-    value: 17,
   },
 ];
 
 export const MandateType = [
   {
     value: 1,
-    label: '工况任务',
+    label: '生产任务',
   },
   {
     value: 2,
-    label: '系统自检任务',
+    label: '应急任务',
   },
   {
     value: 3,
-    label: '生产任务',
+    label: '定时任务',
   },
   {
     value: 4,

+ 1 - 1
src/pages/TaskManage/index.less

@@ -53,7 +53,7 @@
 .ant-select-clear {
   opacity: 1 !important;
   margin-top: -0.12rem !important;
-  width: 0.24rem !important;
+  width: 0.36rem !important;
   height: 0.24rem !important;
   font-size: 0.24rem !important;
   color: black !important;

+ 2 - 2
src/services/OperationManagement.js

@@ -113,13 +113,13 @@ export async function queryChartListByCode(data, codes, isCheck = false) {
         chartRes = await queryChartList({ ...data, metric_code });
       }
 
-      if (i == 0) {
+      if (i === 0) {
         time = chartRes.data?.map((item) => item.data_time);
       }
       res[metric_code] = chartRes.data?.map((item) => item.value.toFixed(2));
     }
     console.log(res, codes);
-    dataSource = time.map((time, index) => {
+    dataSource = time?.map((time, index) => {
       const data = { time };
       codes.forEach((code) => {
         data[code] = res[code]?.[index];