박정하 박정하 2023-09-01
230901 박정하 통계 페이지 작업
@675e018e739384b663e0b5335e1e042918381261
 
client/views/component/chart/Chart11.jsx (added)
+++ client/views/component/chart/Chart11.jsx
@@ -0,0 +1,156 @@
+import React, { Component } from "react";
+import * as am5 from "@amcharts/amcharts5";
+import * as am5xy from "@amcharts/amcharts5/xy";
+import am5themes_Animated from "@amcharts/amcharts5/themes/Animated";
+import CommonUtil from "../../../resources/js/CommonUtil";
+
+export default function Chart11({ data }) {
+  const createChart = () => {
+    console.log('createChart11 data : ', data);
+
+    let root = am5.Root.new("Chart11");
+    root._logo.dispose();
+    // Set themes
+    // https://www.amcharts.com/docs/v5/concepts/themes/
+    root.setThemes([
+      am5themes_Animated.new(root)
+    ]);
+
+    // Create chart
+    // https://www.amcharts.com/docs/v5/charts/xy-chart/
+    let chart = root.container.children.push(
+      am5xy.XYChart.new(root, {
+        panX: true,
+        panY: true,
+        wheelX: "panX",
+        wheelY: "zoomX",
+        layout: root.verticalLayout,
+        pinchZoomX: true
+      })
+    );
+
+    // Add cursor
+    // https://www.amcharts.com/docs/v5/charts/xy-chart/cursor/
+    let cursor = chart.set("cursor", am5xy.XYCursor.new(root, {
+      behavior: "none"
+    }));
+    cursor.lineY.set("visible", false);
+
+    // Create axes
+    // https://www.amcharts.com/docs/v5/charts/xy-chart/axes/
+    let xRenderer = am5xy.AxisRendererX.new(root, {});
+    xRenderer.grid.template.set("location", 0.5);
+    xRenderer.labels.template.setAll({
+      location: 0.5,
+      multiLocation: 0.5
+    });
+
+    let xAxis = chart.xAxes.push(
+      am5xy.CategoryAxis.new(root, {
+        categoryField: "date",
+        renderer: xRenderer,
+        tooltip: am5.Tooltip.new(root, {})
+      })
+    );
+
+    xAxis.data.setAll(data);
+
+    let yAxis = chart.yAxes.push(
+      am5xy.ValueAxis.new(root, {
+        maxPrecision: 0,
+        renderer: am5xy.AxisRendererY.new(root, {
+          inversed: false
+        })
+      })
+    );
+
+    // Add series
+    // https://www.amcharts.com/docs/v5/charts/xy-chart/series/
+
+    function createSeries(name, field) {
+      let series = chart.series.push(
+        am5xy.LineSeries.new(root, {
+          name: name,
+          xAxis: xAxis,
+          yAxis: yAxis,
+          valueYField: field,
+          categoryXField: "date",
+          tooltip: am5.Tooltip.new(root, {
+            pointerOrientation: "horizontal",
+            labelText: "[bold]{name}[/]\n{categoryX}: {valueY}"
+          })
+        })
+      );
+
+
+      series.bullets.push(function () {
+        return am5.Bullet.new(root, {
+          sprite: am5.Circle.new(root, {
+            radius: 5,
+            fill: series.get("fill")
+          })
+        });
+      });
+
+      // create hover state for series and for mainContainer, so that when series is hovered,
+      // the state would be passed down to the strokes which are in mainContainer.
+      series.set("setStateOnChildren", true);
+      series.states.create("hover", {});
+
+      series.mainContainer.set("setStateOnChildren", true);
+      series.mainContainer.states.create("hover", {});
+
+      series.strokes.template.states.create("hover", {
+        strokeWidth: 4
+      });
+
+      series.data.setAll(data);
+      series.appear(1000);
+    }
+
+    createSeries("기기등록율", "register_percent");
+    createSeries("데이터수집율", "data_percent");
+    createSeries("복약율", "medication_percent");
+
+    // Add scrollbar
+    // https://www.amcharts.com/docs/v5/charts/xy-chart/scrollbars/
+    chart.set("scrollbarX", am5.Scrollbar.new(root, {
+      orientation: "horizontal",
+      marginBottom: 20
+    }));
+
+    let legend = chart.children.push(
+      am5.Legend.new(root, {
+        centerX: am5.p50,
+        x: am5.p50
+      })
+    );
+
+    // Make series change state when legend item is hovered
+    legend.itemContainers.template.states.create("hover", {});
+
+    legend.itemContainers.template.events.on("pointerover", function (e) {
+      e.target.dataItem.dataContext.hover();
+    });
+    legend.itemContainers.template.events.on("pointerout", function (e) {
+      e.target.dataItem.dataContext.unhover();
+    });
+
+    legend.data.setAll(chart.series.values);
+
+    // Make stuff animate on load
+    // https://www.amcharts.com/docs/v5/concepts/animations/
+    chart.appear(1000, 100);
+  }
+
+  React.useEffect(() => {
+    console.log('React.useEffect Chart11 data : ', data);
+    if (CommonUtil.isEmpty(data) == false) {
+      createChart();
+    }
+  }, [data])
+
+  return (
+    <div id="Chart11" style={{ width: "100%", height: "100%" }}></div>
+  )
+}(파일 끝에 줄바꿈 문자 없음)
client/views/pages/AppRoute.jsx
--- client/views/pages/AppRoute.jsx
+++ client/views/pages/AppRoute.jsx
@@ -36,6 +36,7 @@
 import VisitSelectOne from "./visit/visit/VisitSelectOne.jsx";
 import EquipmentRentalInsert from "./equipment/EquipmentRentalInsert.jsx";
 import EquipmentSelect from "./equipment/EquipmentSelect.jsx";
+import EquipmentData from "./equipment/EquipmentData.jsx";
 import GovernmentEquipmentSelect from "./equipment/GovernmentEquipmentSelect.jsx";
 import AgencyEquipmentSelect from "./equipment/AgencyEquipmentSelect.jsx";
 import EquipmentManagementSelectOne from "./equipment/EquipmentManagementSelectOne.jsx";
@@ -144,6 +145,7 @@
       <Route path="/EquipmentRentalInsert" element={<EquipmentRentalInsert />}></Route>
       <Route path="/EquipmentManagementInsert" element={<EquipmentManagementInsert />}></Route>
       <Route path="/EquipmentSelect" element={<EquipmentSelect />}></Route>
+      <Route path="/EquipmentData" element={<EquipmentData />}></Route>
       <Route path="/EquipmentManagementSelectOne" element={<EquipmentManagementSelectOne />}></Route>
       <Route path="/MedicineCareSelectOne" element={<MedicineCareSelectOne />}></Route>
       <Route path="/TemperatureManagementSelectOne" element={<TemperatureManagementSelectOne />}></Route>
 
client/views/pages/equipment/EquipmentData.jsx (added)
+++ client/views/pages/equipment/EquipmentData.jsx
@@ -0,0 +1,269 @@
+import React from "react";
+import { useNavigate, useLocation } from "react-router";
+import { useSelector } from "react-redux";
+import SubTitle from "../../component/SubTitle.jsx";
+import Modal from "../../component/Modal.jsx";
+
+import House from "../../../resources/files/icon/house.png";
+import Arrow from "../../../resources/files/icon/arrow.png";
+import Pagination from "../../component/Pagination.jsx";
+import Chart11 from "../../component/chart/Chart11.jsx";
+
+import CommonUtil from "../../../resources/js/CommonUtil.js";
+
+import DatePicker from 'react-datepicker';
+import 'react-datepicker/dist/react-datepicker.css';
+
+export default function EquipmentData() {
+  const navigate = useNavigate();
+  const location = useLocation();
+
+  //전역 변수 저장 객체
+  const state = useSelector((state) => { return state });
+  const defaultGovernmentId = CommonUtil.isEmpty(location.state) || CommonUtil.isEmpty(location.state['government_id']) ? null : location.state['government_id'];
+
+  //기관 계층 구조 목록
+  const [orgListOfHierarchy, setOrgListOfHierarchy] = React.useState([]);
+  //기관(관리, 시행) 계층 구조 목록 조회
+  const orgSelectListOfHierarchy = () => {
+    fetch("/org/orgSelectListOfHierarchy.json", {
+      method: "POST",
+      headers: {
+        'Content-Type': 'application/json; charset=UTF-8'
+      },
+      body: JSON.stringify({ 'government_id': defaultGovernmentId }),
+    }).then((response) => response.json()).then((data) => {
+      console.log("기관(관리, 시행) 계층 구조 목록 조회 : ", data);
+      setOrgListOfHierarchy(data);
+    }).catch((error) => {
+      console.log('orgSelectListOfHierarchy() /org/orgSelectListOfHierarchy.json error : ', error);
+    });
+  };
+  //검색 변수 (초기화값)
+  const [userSearch, setUserSearch] = React.useState({
+    'government_id': null,
+    'agency_id': null,
+
+    'currentPage': 1,
+    'perPage': 10,
+  });
+
+  //올잇메디 선택
+  const adminChange = () => {
+    const newUserSearch = JSON.parse(JSON.stringify(userSearch));
+    newUserSearch['government_id'] = null;
+    newUserSearch['agency_id'] = null;
+    setUserSearch(newUserSearch);
+    equipmentDataList();
+  }
+
+  //관리 기관 선택
+  const governmentChange = (government_id) => {
+    const newUserSearch = JSON.parse(JSON.stringify(userSearch));
+    newUserSearch['government_id'] = government_id;
+    newUserSearch['agency_id'] = null;
+    setUserSearch(newUserSearch);
+    equipmentDataList();
+  }
+
+  //시행 기관 선택
+  const agencyChange = (government_id, agency_id) => {
+    const newUserSearch = JSON.parse(JSON.stringify(userSearch));
+    newUserSearch['government_id'] = government_id;
+    newUserSearch['agency_id'] = agency_id;
+    setUserSearch(newUserSearch);
+    equipmentDataList();
+  }
+
+  const [isSelectDate, setIsSelectDate] = React.useState(false);
+  const [startDate, setStartDate] = React.useState(new Date('2023-08-01'));
+  const [endDate, setEndDate] = React.useState(new Date());
+
+  //통계 자료 데이터
+  const [equipmentData, setEquipmentData] = React.useState([]);
+  //복약 현황 조회
+  const equipmentDataList = () => {
+    console.log("시작날짜 : ", startDate);
+    console.log("끝난날짜 : ", endDate);
+
+    var dateList = [];
+    var curDate = new Date(startDate);
+    while (curDate <= new Date(endDate)) {
+      dateList.push({ government_id: userSearch['government_id'], agency_id: userSearch['agency_id'], receive_datetime: curDate.toISOString().split("T")[0] });
+      curDate.setDate(curDate.getDate() + 1);
+    }
+
+    const reverse = dateList.reverse();
+
+    var dataListArry = [];
+    for (let i = 0; i < reverse.length; i++) {
+      fetch("/stats/equipmentDataList.json", {
+        method: "POST",
+        headers: {
+          'Content-Type': 'application/json; charset=UTF-8'
+        },
+        body: JSON.stringify(reverse[i]),
+      }).then((response) => response.json()).then((data) => {
+        dataListArry[i] = {
+          date: new Date(data['stockList'][0]['datetime']),
+          datetime: data['stockList'][0]['datetime'],
+          delivery_equipment: data['stockList'][0]['delivery_equipment'],
+          register_equipment: data['stockList'][0]['register_equipment'],
+          data_equipment: data['dataList'][0]['data_equipment'],
+          medication_equipment: data['dataList'][0]['medication_equipment'],
+          register_percent: Math.floor((data['stockList'][0]['register_equipment'] / data['stockList'][0]['delivery_equipment']) * 100),
+          data_percent: Math.floor((data['dataList'][0]['data_equipment'] / data['stockList'][0]['delivery_equipment']) * 100),
+          medication_percent: Math.floor((data['dataList'][0]['medication_equipment'] / data['stockList'][0]['delivery_equipment']) * 100)
+        };
+      }).catch((error) => {
+        console.log('equipmentDataList() /stats/equipmentDataList.json error : ', error);
+      });
+    }
+    console.log('dataListArry : ', dataListArry)
+    setEquipmentData(dataListArry);
+    console.log("equipmentData : ", equipmentData)
+  };
+
+  const searchData = () => {
+    equipmentDataList();
+  }
+
+  React.useEffect(() => {
+    orgSelectListOfHierarchy();
+    equipmentDataList();
+  }, []);
+
+  return (
+    <main>
+      <div className="content-wrap">
+        <div className="search-management flex margin-bottom2 margin-top gap">
+          <select style={{ maxWidth: '150px' }}
+            onChange={(e) => { setIsSelectDate(e.target.value == 'selectDate' ? false : true) }}
+          >
+            <option value="selectDate">날짜선택</option>
+            <option value="selectDatetime">기간선택</option>
+          </select>
+          {!isSelectDate ?
+            <div className="selectDate" style={{ width: 'calc(100% - 300px)' }}>
+              <input type="date"
+                value={endDate}
+                onChange={(e) => { setEndDate(e.target.value) }}
+                onKeyDown={function (e) { e.preventDefault() }} />
+            </div>
+            :
+            <div className="selectDatetime flex" style={{ width: 'calc(100% - 300px)' }}>
+              <input type="date"
+                value={startDate}
+                onChange={(e) => { setStartDate(e.target.value) }}
+                onKeyDown={function (e) { e.preventDefault() }} />
+              <input type="date"
+                value={endDate}
+                onChange={(e) => { setEndDate(e.target.value) }}
+                onKeyDown={function (e) { e.preventDefault() }} />
+            </div>
+          }
+          <button className={"btn-small gray-btn"} style={{ maxWidth: '150px' }}
+            onClick={() => { searchData(); }}
+          >검색</button>
+        </div>
+
+        <div>
+          <div className="flex-align-start userauthoriylist gap5">
+            <div className="left">
+              <SubTitle
+                explanation={"기관 리스트"}
+                className="margin-bottom"
+              />
+              {/* 카테고리 디자인 필요 (a.active 클래스 필요) */}
+              <div style={{ fontSize: '16px' }} className="category">
+                {state.loginUser['authority'] == 'ROLE_ADMIN' ?
+                  <a onClick={adminChange}
+                    className={userSearch['government_id'] == null && userSearch['agency_id'] == null ? "active" : ""}>
+                    올잇메디
+                  </a>
+                  : null}
+                <ul style={{ marginLeft: '15px' }}>
+                  {orgListOfHierarchy.map((item, idx) => {
+                    return (
+                      <li style={{ margin: '10px 0px' }} key={idx}>
+                        <span style={{ marginRight: '5px' }}><img src={House} alt="" /></span>
+                        <a onClick={() => { governmentChange(item['government_id']) }}
+                          className={item['government_id'] == userSearch['government_id'] ? "active" : ""}>
+                          {item['government_name']}
+                        </a>
+                        {item['agencyList'] != undefined && item['agencyList'] != null ?
+                          <ul style={{ marginLeft: '15px' }}>
+                            {item['agencyList'].map((item2, idx2) => {
+                              return (
+                                <li style={{ margin: '10px 0px' }} key={idx2}>
+                                  <span style={{ marginRight: '5px' }}><img src={Arrow} alt="" /></span>
+                                  <a onClick={() => { agencyChange(item['government_id'], item2['agency_id']) }}
+                                    className={item2['agency_id'] == userSearch['agency_id'] ? "active" : ""}>
+                                    {item2['agency_name']}
+                                  </a>
+                                </li>
+                              )
+                            })}
+                          </ul>
+                          : null
+                        }
+                      </li>
+                    )
+                  })}
+                </ul>
+              </div>
+            </div>
+            <div className="right">
+              <div className="flex equip-tab">
+                <SubTitle explanation={"선택한 기관의 통계 자료입니다."} />
+              </div>
+
+              <div style={{ height: '30vh' }}>
+                {equipmentData.length > 0 ?
+                  <Chart11 data={equipmentData} />
+                  : "데이터가 없습니다."}
+              </div>
+
+              <table class="caregiver-user protector-user">
+                <thead>
+                  <tr>
+                    <th>날짜</th>
+                    <th>납품기기</th>
+                    <th>등록기기</th>
+                    <th>데이터수집기기</th>
+                    <th>복약기기</th>
+                    <th>기기등록율(%)</th>
+                    <th>데이터수집율(%)</th>
+                    <th>복약율(%)</th>
+                  </tr>
+                </thead>
+                <tbody>
+                  {equipmentData.map((item, idx) => {
+                    return (
+                      <tr key={idx}>
+                        <td data-label="날짜">{item['datetime']}</td>
+                        <td data-label="납품기기">{item['delivery_equipment']}</td>
+                        <td data-label="등록기기">{item['register_equipment']}</td>
+                        <td data-label="데이터수집기기">{item['data_equipment']}</td>
+                        <td data-label="복약기기">{item['medication_equipment']}</td>
+                        <td data-label="기기등록율">{item['register_percent']}%</td>
+                        <td data-label="데이터수집율">{item['data_percent']}%</td>
+                        <td data-label="복약율">{item['medication_percent']}%</td>
+                      </tr>
+                    )
+                  })
+                  }
+                  {equipmentData == null || equipmentData.length == 0 ?
+                    <tr>
+                      <td colSpan={8}>조회된 데이터가 없습니다</td>
+                    </tr>
+                    : null}
+                </tbody>
+              </table>
+            </div>
+          </div>
+        </div>
+      </div>
+    </main >
+  );
+}(파일 끝에 줄바꿈 문자 없음)
Add a comment
List