kirin-ri / memo

0 stars 0 forks source link

azure #27

Open kirin-ri opened 4 months ago

kirin-ri commented 4 months ago

let cpu_rate_per_core_hour = 0.048; // 1 vCPUあたりの料金(USD/時間) let mem_rate_per_gb_hour = 0.012; // 1 GiB RAMあたりの料金(USD/時間)

// 各名前空間のCPU使用量の料金計算 let cpu_cost = KubePodInventory | summarize AvgCPU=avg(TotalCpuUsageNanoCores) by Namespace | extend CPUHours = AvgCPU (datetime_diff('hour', max(TimeGenerated), min(TimeGenerated))) / 1e9 | extend CPUCost = CPUHours cpu_rate_per_core_hour;

// 各名前空間のメモリ使用量の料金計算 let mem_cost = KubePodInventory | summarize AvgMemory=avg(TotalMemoryUsageBytes) by Namespace | extend MemoryHours = AvgMemory (datetime_diff('hour', max(TimeGenerated), min(TimeGenerated))) / (1024 1024 1024) | extend MemoryCost = MemoryHours mem_rate_per_gb_hour;

// CPUとメモリの料金を統合 cpu_cost | join kind=inner (mem_cost) on Namespace | project Namespace, CPUCost, MemoryCost, TotalCost = CPUCost + MemoryCost

kirin-ri commented 3 months ago
import React, { useState, useEffect, useRef } from "react";
import { Chart } from 'react-chartjs-2';
import { Chart as ChartJS, CategoryScale, LinearScale, BarElement, LineElement, PointElement, Title, Tooltip, Legend, ScriptableContext } from 'chart.js';

ChartJS.register(CategoryScale, LinearScale, BarElement, LineElement, PointElement, Title, Tooltip, Legend);

const CollapsiblePanel = ({ title, content }: { title: string; content: string }) => {
  const [isOpen, setIsOpen] = useState(false);

  const togglePanel = () => {
    setIsOpen(!isOpen);
  };

  return (
    <div className="collapsible-panel">
      <div className="panel-header" onClick={togglePanel}>
        <div className="panel-title">{title}</div>
      </div>
      {isOpen && <div className="panel-content">{content}</div>}
    </div>
  );
};

const EmptyPage = () => {
  const test = '資金繰り表';
  const [showAlert, setShowAlert] = useState(true); // 控制警告弹窗的显示
  const chartRef = useRef<ChartJS | null>(null);

  useEffect(() => {
    const chart = chartRef.current;
    if (chart) {
      const ctx = chart.ctx;
      if (ctx && chart.data.datasets[2].data) {
        (chart.data.datasets[2].data as number[]).forEach((value, index) => {
          if (value < 0) {
            const meta = chart.getDatasetMeta(2).data[index];
            if (meta) {
              const x = meta.x;
              const y = meta.y;
              ctx.fillStyle = 'red';
              ctx.font = 'bold 12px Arial';
              ctx.fillText('!', x - 5, y - 10);
            }
          }
        });
      }
    }
  }, []);

  const AlertBox = ({ message, onClose }: { message: string; onClose: () => void }) => {
    return (
      <div className="alert-box">
        <div className="alert-content">
          <i className="fa fa-exclamation-circle alert-icon" aria-hidden="true"></i>
          <span className="alert-message">{message}</span>
        </div>
        <button className="close-btn" onClick={onClose}>非表示</button>
      </div>
    );
  };

  const data = {
    labels: ['4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月', '1月', '2月', '3月'],
    datasets: [
      {
        type: 'bar' as const,
        label: '収入',
        data: [20, 30, 25, 15, 30, 25, 20, 30, 25, 30, 20, 25],
        backgroundColor: 'rgba(153, 102, 255, 0.5)',
      },
      {
        type: 'bar' as const,
        label: '支出',
        data: [-25, -35, -30, -20, -25, -30, -35, -25, -30, -20, -25, -30],
        backgroundColor: 'rgba(54, 162, 235, 0.5)',
      },
      {
        type: 'line' as const,
        label: '資金繰り',
        data: [10, -5, -10, 5, 10, -15, 20, -10, 15, 5, -5, 10],
        borderColor: 'black',
        backgroundColor: 'black',
        fill: false,
        tension: 0.1,
        borderWidth: 2,
        pointStyle: 'rectRot',
        pointRadius: 6,
        pointHoverRadius: 8,
        pointBackgroundColor: (context: ScriptableContext<'line'>) => {
          const index = context.dataIndex;
          const value = context.dataset.data[index] as number;
          return value < 0 ? 'red' : 'black';
        }
      }
    ],
  };

  const options = {
    responsive: true,
    plugins: {
      legend: {
        position: 'top' as const,
        labels: {
          font: {
            size: 16 // 修改图例文字大小
          }
        }
      },
      title: {
        display: true,
        text: '期中資金繰り表',
        font: {
          size: 24 // 修改标题文字大小
        }
      },
      tooltip: {
        callbacks: {
          label: function(context: any) {
            const label = context.dataset.label || '';
            const value = context.raw;
            return `${label}: ${value}`;
          }
        }
      },
    },
    scales: {
      y: {
        beginAtZero: true,
      }
    }
  };

  return (
    <div className="content-wrapper metrics-details">
      <section className="page-cover">
        <div className="page-cover-title-frame">
          <h1>{test}</h1>
        </div>
      </section>
      {/* 警告弹窗 */}
      {showAlert && (
        <div className="alert-container">
          <AlertBox
            message="期中に資金がマイナスとなる期間があります"
            onClose={() => setShowAlert(false)}
          />
        </div>
      )}
      <div className="main-content">
        {/* 資金繰り表のグラフ */}
        <div className="graph-container">
          <Chart ref={chartRef} type="bar" data={data} options={options} />
        </div>
        {/* 可折叠面板 */}
        <div className="collapsible-panels">
          <CollapsiblePanel title="test1" content="abcdef" />
          <CollapsiblePanel title="test2" content="abcdef" />
          <CollapsiblePanel title="test3" content="abcdef" />
        </div>
      </div>
    </div>
  );
};

export default EmptyPage;
kirin-ri commented 3 months ago
.content-wrapper {
  display: flex;
  flex-direction: column;
  padding: 0;
}

.main-content {
  display: flex;
  flex-direction: row; /* 使内容在一行内显示 */
  justify-content: flex-start; /* 确保内容在左侧 */
  align-items: flex-start; /* 确保内容在顶部对齐 */
}

.page-cover {
  margin: 0;
}

.page-cover-title-frame {
  margin: 0;
  padding: 0;
}

.alert-container {
  padding: 0;
  margin: 0;
}

.alert-box {
  background-color: white;
  color: black;
  border: 1px solid #ccc;
  border-radius: 5px;
  padding: 10px;
  margin: 0;
  display: flex;
  justify-content: space-between;
  align-items: center;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  position: relative;
}

.alert-content {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-grow: 1;
}

.alert-icon {
  color: red;
  font-size: 2rem;
  margin-right: 10px;
}

.alert-message {
  text-align: center;
  font-size: 1.2rem;
}

.close-btn {
  background-color: transparent;
  border: 1px solid red;
  border-radius: 15px;
  color: red;
  cursor: pointer;
  font-size: 1rem;
  padding: 5px 10px;
  margin-left: 10px;
}

.graph-container {
  margin-top: 20px;
  width: 60%; /* 图表宽度 */
  height: auto; /* 图表高度 */
  padding: 20px;
  background-color: white;
  border-radius: 5px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}

.collapsible-panels {
  margin-top: 20px;
  margin-left: 20px; /* 确保与图表有间距 */
  width: 30%; /* 可根据需要调整宽度 */
}

.collapsible-panel {
  margin-bottom: 10px; /* 面板之间的间距 */
}

.panel-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 10px;
  background-color: #f1f1f1;
  cursor: pointer;
  border-radius: 5px;
}

.panel-title {
  font-weight: bold;
}

.panel-icon {
  font-size: 1.2rem;
}

.panel-content {
  padding: 10px;
  background-color: #ffffff;
  border: 1px solid #f1f1f1;
  border-radius: 0 0 5px 5px;
}
kirin-ri commented 3 months ago
import React, { useState, useEffect, useRef } from "react";
import { Chart } from 'react-chartjs-2';
import { Chart as ChartJS, CategoryScale, LinearScale, BarElement, LineElement, PointElement, Title, Tooltip, Legend, ScriptableContext } from 'chart.js';

ChartJS.register(CategoryScale, LinearScale, BarElement, LineElement, PointElement, Title, Tooltip, Legend);

const CollapsiblePanel = ({ title, content }: { title: string; content: string }) => {
  const [isOpen, setIsOpen] = useState(false);

  const togglePanel = () => {
    setIsOpen(!isOpen);
  };

  return (
    <div className="collapsible-panel">
      <div className="panel-header" onClick={togglePanel}>
        <div className="panel-title">{title}</div>
      </div>
      {isOpen && <div className="panel-content">{content}</div>}
    </div>
  );
};

const EmptyPage = () => {
  const test = '資金繰り表';
  const [showAlert, setShowAlert] = useState(true); // 控制警告弹窗的显示
  const chartRef = useRef<ChartJS | null>(null);

  useEffect(() => {
    const chart = chartRef.current;
    if (chart) {
      const ctx = chart.ctx;
      if (ctx && chart.data.datasets[2].data) {
        (chart.data.datasets[2].data as number[]).forEach((value, index) => {
          if (value < 0) {
            const meta = chart.getDatasetMeta(2).data[index];
            if (meta) {
              const x = meta.x;
              const y = meta.y;
              ctx.fillStyle = 'red';
              ctx.font = 'bold 12px Arial';
              ctx.fillText('!', x - 5, y - 10);
            }
          }
        });
      }
    }
  }, []);

  const AlertBox = ({ message, onClose }: { message: string; onClose: () => void }) => {
    return (
      <div className="alert-box">
        <div className="alert-content">
          <i className="fa fa-exclamation-circle alert-icon" aria-hidden="true"></i>
          <span className="alert-message">{message}</span>
        </div>
        <button className="close-btn" onClick={onClose}>非表示</button>
      </div>
    );
  };

  const data = {
    labels: ['4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月', '1月', '2月', '3月'],
    datasets: [
      {
        type: 'bar' as const,
        label: '収入',
        data: [20, 30, 25, 15, 30, 25, 20, 30, 25, 30, 20, 25],
        backgroundColor: 'rgba(153, 102, 255, 0.5)',
      },
      {
        type: 'bar' as const,
        label: '支出',
        data: [-25, -35, -30, -20, -25, -30, -35, -25, -30, -20, -25, -30],
        backgroundColor: 'rgba(54, 162, 235, 0.5)',
      },
      {
        type: 'line' as const,
        label: '資金繰り',
        data: [10, -5, -10, 5, 10, -15, 20, -10, 15, 5, -5, 10],
        borderColor: 'black',
        backgroundColor: 'black',
        fill: false,
        tension: 0.1,
        borderWidth: 2,
        pointStyle: 'rectRot',
        pointRadius: 6,
        pointHoverRadius: 8,
        pointBackgroundColor: (context: ScriptableContext<'line'>) => {
          const index = context.dataIndex;
          const value = context.dataset.data[index] as number;
          return value < 0 ? 'red' : 'black';
        }
      }
    ],
  };

  const options = {
    responsive: true,
    plugins: {
      legend: {
        position: 'top' as const,
        labels: {
          font: {
            size: 16 // 修改图例文字大小
          }
        }
      },
      title: {
        display: true,
        text: '期中資金繰り表',
        font: {
          size: 24 // 修改标题文字大小
        }
      },
      tooltip: {
        callbacks: {
          label: function(context: any) {
            const label = context.dataset.label || '';
            const value = context.raw;
            return `${label}: ${value}`;
          }
        }
      },
    },
    scales: {
      y: {
        beginAtZero: true,
      }
    }
  };

  return (
    <div className="content-wrapper metrics-details">
      <section className="page-cover">
        <div className="page-cover-title-frame">
          <h1>{test}</h1>
        </div>
      </section>
      {/* 警告弹窗 */}
      {showAlert && (
        <div className="alert-container">
          <AlertBox
            message="期中に資金がマイナスとなる期間があります"
            onClose={() => setShowAlert(false)}
          />
        </div>
      )}
      <div className="main-content">
        {/* 資金繰り表のグラフ */}
        <div className="graph-container">
          <Chart ref={chartRef} type="bar" data={data} options={options} />
        </div>
        {/* 可折叠面板 */}
        <div className="collapsible-panels">
          <CollapsiblePanel title="test1" content="abcdef" />
          <CollapsiblePanel title="test2" content="abcdef" />
          <CollapsiblePanel title="test3" content="abcdef" />
        </div>
      </div>
    </div>
  );
};

export default EmptyPage;
kirin-ri commented 3 months ago
.content-wrapper {
  display: flex;
  flex-direction: column;
  padding: 0;
}

.main-content {
  display: flex;
  flex-direction: row;
  justify-content: flex-start;
  align-items: flex-start;
  width: 100%; /* 确保内容宽度为100% */
}

.graph-container {
  flex: 1;
  margin-top: 20px;
  width: 50%; /* 图表宽度占50% */
  height: auto;
  padding: 20px;
  background-color: white;
  border-radius: 5px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}

.collapsible-panels {
  flex: 1;
  margin-top: 20px;
  margin-left: 20px;
  width: 50%; /* 面板宽度占50% */
}

.collapsible-panel {
  margin-bottom: 10px;
}

.panel-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 10px;
  background-color: #f1f1f1;
  cursor: pointer;
  border-radius: 5px;
}

.panel-title {
  font-weight: bold;
}

.panel-icon {
  font-size: 1.2rem;
}

.panel-content {
  padding: 10px;
  background-color: #ffffff;
  border: 1px solid #f1f1f1;
  border-radius: 0 0 5px 5px;
}
kirin-ri commented 3 months ago
.content-wrapper {
  display: flex;
  flex-direction: column;
  padding: 0;
}

.main-content {
  display: flex;
  flex-direction: row; /* 使内容在一行内显示 */
  justify-content: flex-start; /* 确保内容在左侧 */
  align-items: flex-start; /* 确保内容在顶部对齐 */
  width: 100%; /* 确保内容宽度为100% */
}

.page-cover {
  margin: 0;
}

.page-cover-title-frame {
  margin: 0;
  padding: 0;
}

.alert-container {
  padding: 0;
  margin: 0;
}

.alert-box {
  background-color: white;
  color: black;
  border: 1px solid #ccc;
  border-radius: 5px;
  padding: 10px;
  margin: 0;
  display: flex;
  justify-content: space-between;
  align-items: center;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  position: relative;
}

.alert-content {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-grow: 1;
}

.alert-icon {
  color: red;
  font-size: 2rem;
  margin-right: 10px;
}

.alert-message {
  text-align: center;
  font-size: 1.2rem;
}

.close-btn {
  background-color: transparent;
  border: 1px solid red;
  border-radius: 15px;
  color: red;
  cursor: pointer;
  font-size: 1rem;
  padding: 5px 10px;
  margin-left: 10px;
}

.graph-container {
  flex: 1; /* 确保容器平分 */
  margin-top: 20px;
  padding: 20px;
  background-color: white;
  border-radius: 5px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}

.collapsible-panels {
  flex: 1; /* 确保容器平分 */
  margin-top: 20px;
  margin-left: 20px;
}

.collapsible-panel {
  margin-bottom: 10px;
}

.panel-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 10px;
  background-color: #f1f1f1;
  cursor: pointer;
  border-radius: 5px;
}

.panel-title {
  font-weight: bold;
}

.panel-icon {
  font-size: 1.2rem;
}

.panel-content {
  padding: 10px;
  background-color: #ffffff;
  border: 1px solid #f1f1f1;
  border-radius: 0 0 5px 5px;
}
kirin-ri commented 3 months ago
.content-wrapper {
  display: flex;
  flex-direction: column;
  padding: 0;
  width: 100%;
}

.main-content {
  display: flex;
  flex-direction: row;
  justify-content: space-between;
  align-items: flex-start;
  width: 100%;
}

.page-cover {
  margin: 0;
}

.page-cover-title-frame {
  margin: 0;
  padding: 0;
}

.alert-container {
  padding: 0;
  margin: 0;
}

.alert-box {
  background-color: white;
  color: black;
  border: 1px solid #ccc;
  border-radius: 5px;
  padding: 10px;
  margin: 0;
  display: flex;
  justify-content: space-between;
  align-items: center;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  position: relative;
}

.alert-content {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-grow: 1;
}

.alert-icon {
  color: red;
  font-size: 2rem;
  margin-right: 10px;
}

.alert-message {
  text-align: center;
  font-size: 1.2rem;
}

.close-btn {
  background-color: transparent;
  border: 1px solid red;
  border-radius: 15px;
  color: red;
  cursor: pointer;
  font-size: 1rem;
  padding: 5px 10px;
  margin-left: 10px;
}

.graph-container {
  flex: 1;
  margin-top: 20px;
  max-width: 50%; /* 图表宽度 */
  height: auto;
  padding: 20px;
  background-color: white;
  border-radius: 5px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}

.collapsible-panels {
  flex: 1;
  margin-top: 20px;
  margin-left: 20px;
  max-width: 50%; /* 面板宽度 */
}

.collapsible-panel {
  margin-bottom: 10px;
}

.panel-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 10px;
  background-color: #f1f1f1;
  cursor: pointer;
  border-radius: 5px;
}

.panel-title {
  font-weight: bold;
}

.panel-icon {
  font-size: 1.2rem;
}

.panel-content {
  padding: 10px;
  background-color: #ffffff;
  border: 1px solid #f1f1f1;
  border-radius: 0 0 5px 5px;
}
kirin-ri commented 3 months ago
.content-wrapper {
  display: flex;
  flex-direction: column;
  padding: 0;
  width: 100%;
  overflow: hidden;
}

.main-content {
  display: flex;
  flex-direction: row;
  justify-content: space-between;
  align-items: flex-start;
  width: 100%;
  overflow: hidden;
}

.page-cover {
  margin: 0;
}

.page-cover-title-frame {
  margin: 0;
  padding: 0;
}

.alert-container {
  padding: 0;
  margin: 0;
}

.alert-box {
  background-color: white;
  color: black;
  border: 1px solid #ccc;
  border-radius: 5px;
  padding: 10px;
  margin: 0;
  display: flex;
  justify-content: space-between;
  align-items: center;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  position: relative;
}

.alert-content {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-grow: 1;
}

.alert-icon {
  color: red;
  font-size: 2rem;
  margin-right: 10px;
}

.alert-message {
  text-align: center;
  font-size: 1.2rem;
}

.close-btn {
  background-color: transparent;
  border: 1px solid red;
  border-radius: 15px;
  color: red;
  cursor: pointer;
  font-size: 1rem;
  padding: 5px 10px;
  margin-left: 10px;
}

.graph-container {
  flex: 1;
  margin-top: 20px;
  max-width: 48%; /* 减少宽度以避免滚动条 */
  height: auto;
  padding: 20px;
  background-color: white;
  border-radius: 5px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  box-sizing: border-box; /* 确保内边距包含在宽度内 */
}

.collapsible-panels {
  flex: 1;
  margin-top: 20px;
  margin-left: 20px;
  max-width: 48%; /* 减少宽度以避免滚动条 */
  box-sizing: border-box; /* 确保内边距包含在宽度内 */
}

.collapsible-panel {
  margin-bottom: 10px;
}

.panel-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 10px;
  background-color: #f1f1f1;
  cursor: pointer;
  border-radius: 5px;
}

.panel-title {
  font-weight: bold;
}

.panel-icon {
  font-size: 1.2rem;
}

.panel-content {
  padding: 10px;
  background-color: #ffffff;
  border: 1px solid #f1f1f1;
  border-radius: 0 0 5px 5px;
}
kirin-ri commented 3 months ago
.content-wrapper {
  display: flex;
  flex-direction: column;
  padding: 0;
  width: 100%;
  box-sizing: border-box; /* 确保内外边距包含在宽度内 */
}

.main-content {
  display: flex;
  flex-direction: row;
  justify-content: space-between;
  align-items: flex-start;
  width: 100%;
  overflow: hidden; /* 隐藏超出部分 */
}

.page-cover {
  margin: 0;
}

.page-cover-title-frame {
  margin: 0;
  padding: 0;
}

.alert-container {
  padding: 0;
  margin: 0;
}

.alert-box {
  background-color: white;
  color: black;
  border: 1px solid #ccc;
  border-radius: 5px;
  padding: 10px;
  margin: 0;
  display: flex;
  justify-content: space-between;
  align-items: center;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  position: relative;
}

.alert-content {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-grow: 1;
}

.alert-icon {
  color: red;
  font-size: 2rem;
  margin-right: 10px;
}

.alert-message {
  text-align: center;
  font-size: 1.2rem;
}

.close-btn {
  background-color: transparent;
  border: 1px solid red;
  border-radius: 15px;
  color: red;
  cursor: pointer;
  font-size: 1rem;
  padding: 5px 10px;
  margin-left: 10px;
}

.graph-container {
  flex: 1;
  margin-top: 20px;
  max-width: 50%; /* 图表宽度 */
  height: auto;
  padding: 20px;
  background-color: white;
  border-radius: 5px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  box-sizing: border-box; /* 确保内外边距包含在宽度内 */
}

.collapsible-panels {
  flex: 1;
  margin-top: 20px;
  margin-left: 20px;
  max-width: 50%; /* 面板宽度 */
  box-sizing: border-box; /* 确保内外边距包含在宽度内 */
}

.collapsible-panel {
  margin-bottom: 10px;
  max-width: 100%; /* 确保折叠面板宽度不超过容器 */
}

.panel-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 10px;
  background-color: #f1f1f1;
  cursor: pointer;
  border-radius: 5px;
}

.panel-title {
  font-weight: bold;
}

.panel-icon {
  font-size: 1.2rem;
}

.panel-content {
  padding: 10px;
  background-color: #ffffff;
  border: 1px solid #f1f1f1;
  border-radius: 0 0 5px 5px;
}
kirin-ri commented 3 months ago
.content-wrapper {
  display: flex;
  flex-direction: column;
  padding: 0;
  width: 100%;
  box-sizing: border-box; /* 确保内外边距包含在宽度内 */
}

.main-content {
  display: flex;
  flex-direction: row;
  justify-content: space-between;
  align-items: flex-start;
  width: 100%;
  overflow: hidden; /* 隐藏超出部分 */
}

.page-cover {
  margin: 0;
}

.page-cover-title-frame {
  margin: 0;
  padding: 0;
}

.alert-container {
  padding: 0;
  margin: 0;
}

.alert-box {
  background-color: white;
  color: black;
  border: 1px solid #ccc;
  border-radius: 5px;
  padding: 10px;
  margin: 0;
  display: flex;
  justify-content: space-between;
  align-items: center;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  position: relative;
}

.alert-content {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-grow: 1;
}

.alert-icon {
  color: red;
  font-size: 2rem;
  margin-right: 10px;
}

.alert-message {
  text-align: center;
  font-size: 1.2rem;
}

.close-btn {
  background-color: transparent;
  border: 1px solid red;
  border-radius: 15px;
  color: red;
  cursor: pointer;
  font-size: 1rem;
  padding: 5px 10px;
  margin-left: 10px;
}

.graph-container {
  flex: 1;
  margin-top: 20px;
  width: 50%; /* 限制图表容器宽度 */
  height: auto;
  padding: 20px;
  background-color: white;
  border-radius: 5px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  box-sizing: border-box; /* 确保内外边距包含在宽度内 */
}

.collapsible-panels {
  flex: 1;
  margin-top: 20px;
  margin-left: 20px;
  width: 50%; /* 限制面板容器宽度 */
  padding: 20px;
  background-color: #f9f9f9;
  border-radius: 5px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  box-sizing: border-box; /* 确保内外边距包含在宽度内 */
}

.collapsible-panel {
  margin-bottom: 10px;
  width: 100%; /* 确保折叠面板宽度填满容器 */
}

.panel-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 10px;
  background-color: #f1f1f1;
  cursor: pointer;
  border-radius: 5px;
}

.panel-title {
  font-weight: bold;
}

.panel-icon {
  font-size: 1.2rem;
}

.panel-content {
  padding: 10px;
  background-color: #ffffff;
  border: 1px solid #f1f1f1;
  border-radius: 0 0 5px 5px;
}
kirin-ri commented 3 months ago
import React, { useState, useEffect, useRef } from "react";
import { Chart } from 'react-chartjs-2';
import { Chart as ChartJS, CategoryScale, LinearScale, BarElement, LineElement, PointElement, Title, Tooltip, Legend, ScriptableContext } from 'chart.js';

ChartJS.register(CategoryScale, LinearScale, BarElement, LineElement, PointElement, Title, Tooltip, Legend);

const CollapsiblePanel = ({ title, content }: { title: string; content: string }) => {
  const [isOpen, setIsOpen] = useState(false);

  const togglePanel = () => {
    setIsOpen(!isOpen);
  };

  return (
    <div className="collapsible-panel">
      <div className="panel-header" onClick={togglePanel}>
        <div className="panel-title">{title}</div>
        <div className="panel-icon">{isOpen ? '-' : '+'}</div>
      </div>
      {isOpen && <div className="panel-content">{content}</div>}
    </div>
  );
};

const EmptyPage = () => {
  const test = '資金繰り表';
  const [showAlert, setShowAlert] = useState(true); // 控制警告弹窗的显示
  const chartRef = useRef<ChartJS | null>(null);

  useEffect(() => {
    const chart = chartRef.current;
    if (chart) {
      const ctx = chart.ctx;
      if (ctx && chart.data.datasets[2].data) {
        (chart.data.datasets[2].data as number[]).forEach((value, index) => {
          if (value < 0) {
            const meta = chart.getDatasetMeta(2).data[index];
            if (meta) {
              const x = meta.x;
              const y = meta.y;
              ctx.fillStyle = 'red';
              ctx.font = 'bold 12px Arial';
              ctx.fillText('!', x - 5, y - 10);
            }
          }
        });
      }
    }
  }, []);

  const AlertBox = ({ message, onClose }: { message: string; onClose: () => void }) => {
    return (
      <div className="alert-box">
        <div className="alert-content">
          <i className="fa fa-exclamation-circle alert-icon" aria-hidden="true"></i>
          <span className="alert-message">{message}</span>
        </div>
        <button className="close-btn" onClick={onClose}>非表示</button>
      </div>
    );
  };

  const data = {
    labels: ['4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月', '1月', '2月', '3月'],
    datasets: [
      {
        type: 'bar' as const,
        label: '収入',
        data: [20, 30, 25, 15, 30, 25, 20, 30, 25, 30, 20, 25],
        backgroundColor: 'rgba(153, 102, 255, 0.5)',
      },
      {
        type: 'bar' as const,
        label: '支出',
        data: [-25, -35, -30, -20, -25, -30, -35, -25, -30, -20, -25, -30],
        backgroundColor: 'rgba(54, 162, 235, 0.5)',
      },
      {
        type: 'line' as const,
        label: '資金繰り',
        data: [10, -5, -10, 5, 10, -15, 20, -10, 15, 5, -5, 10],
        borderColor: 'black',
        backgroundColor: 'black',
        fill: false,
        tension: 0.1,
        borderWidth: 2,
        pointStyle: 'rectRot',
        pointRadius: 6,
        pointHoverRadius: 8,
        pointBackgroundColor: (context: ScriptableContext<'line'>) => {
          const index = context.dataIndex;
          const value = context.dataset.data[index] as number;
          return value < 0 ? 'red' : 'black';
        }
      }
    ],
  };

  const options = {
    responsive: true,
    plugins: {
      legend: {
        position: 'top' as const,
        labels: {
          font: {
            size: 16 // 修改图例文字大小
          }
        }
      },
      title: {
        display: true,
        text: '期中資金繰り表',
        font: {
          size: 24 // 修改标题文字大小
        }
      },
      tooltip: {
        callbacks: {
          label: function(context: any) {
            const label = context.dataset.label || '';
            const value = context.raw;
            return `${label}: ${value}`;
          }
        }
      },
    },
    scales: {
      y: {
        beginAtZero: true,
      }
    }
  };

  return (
    <div className="content-wrapper metrics-details">
      <section className="page-cover">
        <div className="page-cover-title-frame">
          <h1>{test}</h1>
        </div>
      </section>
      {/* 警告弹窗 */}
      {showAlert && (
        <div className="alert-container">
          <AlertBox
            message="期中に資金がマイナスとなる期間があります"
            onClose={() => setShowAlert(false)}
          />
        </div>
      )}
      <div className="main-content">
        {/* 資金繰り表のグラフ */}
        <div className="graph-container">
          <Chart ref={chartRef} type="bar" data={data} options={options} />
        </div>
        {/* 可折叠面板 */}
        <div className="collapsible-panels">
          <CollapsiblePanel title="test1" content="abcdef" />
          <CollapsiblePanel title="test2" content="abcdef" />
          <CollapsiblePanel title="test3" content="abcdef" />
        </div>
      </div>
    </div>
  );
};

export default EmptyPage;
kirin-ri commented 3 months ago
.content-wrapper {
  display: flex;
  flex-direction: column;
  padding: 0;
  width: 100%;
  box-sizing: border-box; /* 确保内外边距包含在宽度内 */
}

.main-content {
  display: flex;
  flex-direction: row;
  justify-content: space-between;
  align-items: flex-start;
  width: 100%;
  max-width: 100%; /* 确保宽度不超过视口 */
  overflow: hidden; /* 隐藏超出部分 */
}

.page-cover {
  margin: 0;
}

.page-cover-title-frame {
  margin: 0;
  padding: 0;
}

.alert-container {
  padding: 0;
  margin: 0;
}

.alert-box {
  background-color: white;
  color: black;
  border: 1px solid #ccc;
  border-radius: 5px;
  padding: 10px;
  margin: 0;
  display: flex;
  justify-content: space-between;
  align-items: center;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  position: relative;
}

.alert-content {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-grow: 1;
}

.alert-icon {
  color: red;
  font-size: 2rem;
  margin-right: 10px;
}

.alert-message {
  text-align: center;
  font-size: 1.2rem;
}

.close-btn {
  background-color: transparent;
  border: 1px solid red;
  border-radius: 15px;
  color: red;
  cursor: pointer;
  font-size: 1rem;
  padding: 5px 10px;
  margin-left: 10px;
}

.graph-container {
  flex: 1;
  margin-top: 20px;
  width: 48%; /* 限制图表容器宽度 */
  height: auto;
  padding: 20px;
  background-color: white;
  border-radius: 5px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  box-sizing: border-box; /* 确保内外边距包含在宽度内 */
}

.collapsible-panels {
  flex: 1;
  margin-top: 20px;
  width: 48%; /* 限制面板容器宽度 */
  padding: 20px;
  background-color: #f9f9f9;
  border-radius: 5px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  box-sizing: border-box; /* 确保内外边距包含在宽度内 */
}

.collapsible-panel {
  margin-bottom: 10px;
  width: 100%; /* 确保折叠面板宽度填满容器 */
}

.panel-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 10px;
  background-color: #f1f1f1;
  cursor: pointer;
  border-radius: 5px;
}

.panel-title {
  font-weight: bold;
}

.panel-icon {
  font-size: 1.2rem;
}

.panel-content {
  padding: 10px;
  background-color: #ffffff;
  border: 1px solid #f1f1f1;
  border-radius: 0 0 5px 5px;
}
kirin-ri commented 3 months ago
.content-wrapper {
  display: flex;
  flex-direction: column;
  padding: 0;
  width: 100%;
  box-sizing: border-box; /* 确保内外边距包含在宽度内 */
}

.main-content {
  display: flex;
  flex-direction: row;
  justify-content: space-between;
  align-items: flex-start;
  width: 100%;
  max-width: 100%; /* 确保宽度不超过视口 */
  overflow: hidden; /* 隐藏超出部分 */
  box-sizing: border-box; /* 确保内外边距包含在宽度内 */
}

.page-cover {
  margin: 0;
}

.page-cover-title-frame {
  margin: 0;
  padding: 0;
}

.alert-container {
  padding: 0;
  margin: 0;
}

.alert-box {
  background-color: white;
  color: black;
  border: 1px solid #ccc;
  border-radius: 5px;
  padding: 10px;
  margin: 0;
  display: flex;
  justify-content: space-between;
  align-items: center;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  position: relative;
}

.alert-content {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-grow: 1;
}

.alert-icon {
  color: red;
  font-size: 2rem;
  margin-right: 10px;
}

.alert-message {
  text-align: center;
  font-size: 1.2rem;
}

.close-btn {
  background-color: transparent;
  border: 1px solid red;
  border-radius: 15px;
  color: red;
  cursor: pointer;
  font-size: 1rem;
  padding: 5px 10px;
  margin-left: 10px;
}

.graph-container {
  flex: 1;
  margin-top: 20px;
  max-width: 50%; /* 限制图表容器宽度 */
  height: auto;
  padding: 20px;
  background-color: white;
  border-radius: 5px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  box-sizing: border-box; /* 确保内外边距包含在宽度内 */
}

.collapsible-panels {
  flex: 1;
  margin-top: 20px;
  margin-left: 20px;
  max-width: 50%; /* 限制面板容器宽度 */
  padding: 20px;
  background-color: #f9f9f9;
  border-radius: 5px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  box-sizing: border-box; /* 确保内外边距包含在宽度内 */
}

.collapsible-panel {
  margin-bottom: 10px;
  width: 100%; /* 确保折叠面板宽度填满容器 */
}

.panel-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 10px;
  background-color: #f1f1f1;
  cursor: pointer;
  border-radius: 5px;
}

.panel-title {
  font-weight: bold;
}

.panel-icon {
  font-size: 1.2rem;
}

.panel-content {
  padding: 10px;
  background-color: #ffffff;
  border: 1px solid #f1f1f1;
  border-radius: 0 0 5px 5px;
}
kirin-ri commented 3 months ago
.content-wrapper {
  display: flex;
  flex-direction: column;
  padding: 0;
  width: 100%;
  box-sizing: border-box; /* 确保内外边距包含在宽度内 */
}

.main-content {
  display: flex;
  flex-direction: row;
  justify-content: space-between;
  align-items: flex-start;
  width: 100%;
  max-width: 100%; /* 确保宽度不超过视口 */
  overflow: hidden; /* 隐藏超出部分 */
  padding: 0 20px; /* 添加左右内边距,防止内容紧贴边缘 */
  box-sizing: border-box; /* 确保内外边距包含在宽度内 */
}

.page-cover {
  margin: 0;
  padding: 20px; /* 添加适当的内边距 */
}

.page-cover-title-frame {
  margin: 0;
  padding: 0;
}

.alert-container {
  padding: 0;
  margin: 0;
}

.alert-box {
  background-color: white;
  color: black;
  border: 1px solid #ccc;
  border-radius: 5px;
  padding: 10px;
  margin: 0;
  display: flex;
  justify-content: space-between;
  align-items: center;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  position: relative;
}

.alert-content {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-grow: 1;
}

.alert-icon {
  color: red;
  font-size: 2rem;
  margin-right: 10px;
}

.alert-message {
  text-align: center;
  font-size: 1.2rem;
}

.close-btn {
  background-color: transparent;
  border: 1px solid red;
  border-radius: 15px;
  color: red;
  cursor: pointer;
  font-size: 1rem;
  padding: 5px 10px;
  margin-left: 10px;
}

.graph-container {
  flex: 1;
  margin-top: 20px;
  max-width: 50%; /* 限制图表容器宽度 */
  height: auto;
  padding: 20px;
  background-color: white;
  border-radius: 5px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  box-sizing: border-box; /* 确保内外边距包含在宽度内 */
}

.collapsible-panels {
  flex: 1;
  margin-top: 20px;
  margin-left: 20px;
  max-width: 50%; /* 限制面板容器宽度 */
  padding: 20px;
  background-color: #f9f9f9;
  border-radius: 5px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  box-sizing: border-box; /* 确保内外边距包含在宽度内 */
}

.collapsible-panel {
  margin-bottom: 10px;
  width: 100%; /* 确保折叠面板宽度填满容器 */
}

.panel-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 10px;
  background-color: #f1f1f1;
  cursor: pointer;
  border-radius: 5px;
}

.panel-title {
  font-weight: bold;
}

.panel-icon {
  font-size: 1.2rem;
}

.panel-content {
  padding: 10px;
  background-color: #ffffff;
  border: 1px solid #f1f1f1;
  border-radius: 0 0 5px 5px;
}
kirin-ri commented 3 months ago
.content-wrapper {
  padding: 0; /* 确保没有内边距 */
}

.page-cover {
  margin: 0; /* 确保没有外边距 */
}

.page-cover-title-frame {
  margin: 0; /* 确保没有外边距 */
  padding: 0; /* 确保没有内边距 */
}

.alert-container {
  padding: 0;
  margin: 0;
}

.alert-box {
  background-color: white;
  color: black;
  border: 1px solid #ccc;
  border-radius: 5px;
  padding: 10px;
  margin: 0; /* 确保没有外边距 */
  display: flex;
  justify-content: space-between; /* 确保按钮在右侧 */
  align-items: center;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  position: relative;
}

.alert-content {
  display: flex;
  align-items: center;
  justify-content: center; /* 确保内容居中 */
  flex-grow: 1;
}

.alert-icon {
  color: red;
  font-size: 2rem; /* 调整感叹号的大小 */
  margin-right: 10px; /* 感叹号与文字之间的间距 */
}

.alert-message {
  text-align: center; /* 文本居中对齐 */
  font-size: 1.2rem
}

.close-btn {
  background-color: transparent;
  border: 1px solid red;
  border-radius: 15px; /* 椭圆形 */
  color: red;
  cursor: pointer;
  font-size: 1rem;
  padding: 5px 10px;
  margin-left: 10px;
}

.graph-container {
  left:50px;
  position: relative;
  margin-top: 20px;
  max-width: 50%; /* 图表宽度 */
  height: auto; /* 图表高度 */
  padding: 20px;
  background-color: white;
  border-radius: 5px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
kirin-ri commented 3 months ago

.collapsible-panels { position: absolute; / 使用绝对定位确保其在右侧 / top: 20px; / 与图表顶部对齐 / right: 20px; / 距离右侧20px / width: 40%; / 折叠面板宽度 / padding: 20px; background-color: #f9f9f9; border-radius: 5px; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); box-sizing: border-box; / 确保内外边距包含在宽度内 / }

.collapsible-panel { margin-bottom: 10px; width: 100%; / 确保折叠面板宽度填满容器 / }

.panel-header { display: flex; justify-content: space-between; align-items: center; padding: 10px; background-color: #f1f1f1; cursor: pointer; border-radius: 5px; }

.panel-title { font-weight: bold; }

.panel-icon { font-size: 1.2rem; }

.panel-content { padding: 10px; background-color: #ffffff; border: 1px solid #f1f1f1; border-radius: 0 0 5px 5px; }

kirin-ri commented 3 months ago
.collapsible-panels {
  position: absolute; /* 使用绝对定位确保其在右侧 */
  top: 20px; /* 与图表顶部对齐 */
  right: 20px; /* 距离右侧20px */
  width: 40%; /* 折叠面板宽度 */
  padding: 20px;
  background-color: #f9f9f9;
  border-radius: 5px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  box-sizing: border-box; /* 确保内外边距包含在宽度内 */
}

.collapsible-panel {
  margin-bottom: 10px;
  width: 100%; /* 确保折叠面板宽度填满容器 */
}

.panel-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 10px;
  background-color: #f1f1f1;
  cursor: pointer;
  border-radius: 5px;
}

.panel-title {
  font-weight: bold;
}

.panel-icon {
  font-size: 1.2rem;
}

.panel-content {
  padding: 10px;
  background-color: #ffffff;
  border: 1px solid #f1f1f1;
  border-radius: 0 0 5px 5px;
}
kirin-ri commented 3 months ago
.content-wrapper {
  padding: 0; /* 确保没有内边距 */
}

.page-cover {
  margin: 0; /* 确保没有外边距 */
}

.page-cover-title-frame {
  margin: 0; /* 确保没有外边距 */
  padding: 0; /* 确保没有内边距 */
}

.alert-container {
  padding: 0;
  margin: 0;
}

.alert-box {
  background-color: white;
  color: black;
  border: 1px solid #ccc;
  border-radius: 5px;
  padding: 10px;
  margin: 0; /* 确保没有外边距 */
  display: flex;
  justify-content: space-between; /* 确保按钮在右侧 */
  align-items: center;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  position: relative;
}

.alert-content {
  display: flex;
  align-items: center;
  justify-content: center; /* 确保内容居中 */
  flex-grow: 1;
}

.alert-icon {
  color: red;
  font-size: 2rem; /* 调整感叹号的大小 */
  margin-right: 10px; /* 感叹号与文字之间的间距 */
}

.alert-message {
  text-align: center; /* 文本居中对齐 */
  font-size: 1.2rem
}

.close-btn {
  background-color: transparent;
  border: 1px solid red;
  border-radius: 15px; /* 椭圆形 */
  color: red;
  cursor: pointer;
  font-size: 1rem;
  padding: 5px 10px;
  margin-left: 10px;
}

.graph-container {
  left:50px;
  position: relative;
  margin-top: 20px;
  max-width: 50%; /* 图表宽度 */
  height: auto; /* 图表高度 */
  padding: 20px;
  background-color: white;
  border-radius: 5px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}

.collapsible-panels {
  position: absolute; /* 使用绝对定位确保其在右侧 */
  top: 380px; /* 与图表顶部对齐 */
  right: 20px; /* 距离右侧20px */
  width: 40%; /* 折叠面板宽度 */
  padding: 20px;
  background-color: #f9f9f9;
  border-radius: 5px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  box-sizing: border-box; /* 确保内外边距包含在宽度内 */
}

.collapsible-panel {
  margin-bottom: 10px;
  width: 100%; /* 确保折叠面板宽度填满容器 */
}

.panel-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 10px;
  background-color: #f1f1f1;
  cursor: pointer;
  border-radius: 5px;
}

.panel-title {
  font-weight: bold;
}

.panel-icon {
  font-size: 1.2rem;
}

.panel-content {
  padding: 10px;
  background-color: #ffffff;
  border: 1px solid #f1f1f1;
  border-radius: 0 0 5px 5px;
}
kirin-ri commented 3 months ago
.content-wrapper {
  padding: 0; /* 确保没有内边距 */
}

.page-cover {
  margin: 0; /* 确保没有外边距 */
}

.page-cover-title-frame {
  margin: 0; /* 确保没有外边距 */
  padding: 0; /* 确保没有内边距 */
}

.alert-container {
  padding: 0;
  margin: 0;
}

.alert-box {
  background-color: white;
  color: black;
  border: 1px solid #ccc;
  border-radius: 5px;
  padding: 10px;
  margin: 0; /* 确保没有外边距 */
  display: flex;
  justify-content: space-between; /* 确保按钮在右侧 */
  align-items: center;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  position: relative;
}

.alert-content {
  display: flex;
  align-items: center;
  justify-content: center; /* 确保内容居中 */
  flex-grow: 1;
}

.alert-icon {
  color: red;
  font-size: 2rem; /* 调整感叹号的大小 */
  margin-right: 10px; /* 感叹号与文字之间的间距 */
}

.alert-message {
  text-align: center; /* 文本居中对齐 */
  font-size: 1.2rem
}

.close-btn {
  background-color: transparent;
  border: 1px solid red;
  border-radius: 15px; /* 椭圆形 */
  color: red;
  cursor: pointer;
  font-size: 1rem;
  padding: 5px 10px;
  margin-left: 10px;
}

.main-content {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  width: 100%;
  margin-top: 20px;
  box-sizing: border-box; /* 确保内外边距包含在宽度内 */
}

.graph-container {
  flex: 1;
  max-width: 50%; /* 图表宽度 */
  height: auto; /* 图表高度 */
  padding: 20px;
  background-color: white;
  border-radius: 5px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  box-sizing: border-box; /* 确保内外边距包含在宽度内 */
}

.collapsible-panels {
  flex: 1;
  margin-left: 20px;
  max-width: 40%; /* 折叠面板宽度 */
  padding: 20px;
  background-color: #f9f9f9;
  border-radius: 5px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  box-sizing: border-box; /* 确保内外边距包含在宽度内 */
}

.collapsible-panel {
  margin-bottom: 10px;
  width: 100%; /* 确保折叠面板宽度填满容器 */
}

.panel-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 10px;
  background-color: #f1f1f1;
  cursor: pointer;
  border-radius: 5px;
}

.panel-title {
  font-weight: bold;
}

.panel-icon {
  font-size: 1.2rem;
}

.panel-content {
  padding: 10px;
  background-color: #ffffff;
  border: 1px solid #f1f1f1;
  border-radius: 0 0 5px 5px;
}
kirin-ri commented 3 months ago
.content-wrapper {
  padding: 0; /* 确保没有内边距 */
}

.page-cover {
  margin: 0; /* 确保没有外边距 */
}

.page-cover-title-frame {
  margin: 0; /* 确保没有外边距 */
  padding: 0; /* 确保没有内边距 */
}

.alert-container {
  padding: 0;
  margin: 0;
}

.alert-box {
  background-color: white;
  color: black;
  border: 1px solid #ccc;
  border-radius: 5px;
  padding: 10px;
  margin: 0; /* 确保没有外边距 */
  display: flex;
  justify-content: space-between; /* 确保按钮在右侧 */
  align-items: center;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  position: relative;
}

.alert-content {
  display: flex;
  align-items: center;
  justify-content: center; /* 确保内容居中 */
  flex-grow: 1;
}

.alert-icon {
  color: red;
  font-size: 2rem; /* 调整感叹号的大小 */
  margin-right: 10px; /* 感叹号与文字之间的间距 */
}

.alert-message {
  text-align: center; /* 文本居中对齐 */
  font-size: 1.2rem;
}

.close-btn {
  background-color: transparent;
  border: 1px solid red;
  border-radius: 15px; /* 椭圆形 */
  color: red;
  cursor: pointer;
  font-size: 1rem;
  padding: 5px 10px;
  margin-left: 10px;
}

.main-content {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  width: 100%;
  margin-top: 20px;
  box-sizing: border-box; /* 确保内外边距包含在宽度内 */
}

.graph-container {
  flex: 1;
  max-width: 48%; /* 图表宽度 */
  height: auto; /* 图表高度 */
  padding: 20px;
  background-color: white;
  border-radius: 5px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  box-sizing: border-box; /* 确保内外边距包含在宽度内 */
}

.collapsible-panels {
  flex: 1;
  margin-left: 2%; /* 与图表之间的距离 */
  max-width: 48%; /* 折叠面板宽度 */
  padding: 20px;
  background-color: #f9f9f9;
  border-radius: 5px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  box-sizing: border-box; /* 确保内外边距包含在宽度内 */
}

.collapsible-panel {
  margin-bottom: 10px;
  width: 100%; /* 确保折叠面板宽度填满容器 */
}

.panel-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 10px;
  background-color: #f1f1f1;
  cursor: pointer;
  border-radius: 5px;
}

.panel-title {
  font-weight: bold;
}

.panel-icon {
  font-size: 1.2rem;
}

.panel-content {
  padding: 10px;
  background-color: #ffffff;
  border: 1px solid #f1f1f1;
  border-radius: 0 0 5px 5px;
}
kirin-ri commented 3 months ago
.content-wrapper {
  padding: 0; /* 确保没有内边距 */
}

.page-cover {
  margin: 0; /* 确保没有外边距 */
}

.page-cover-title-frame {
  margin: 0; /* 确保没有外边距 */
  padding: 0; /* 确保没有内边距 */
}

.alert-container {
  padding: 0;
  margin: 0;
}

.alert-box {
  background-color: white;
  color: black;
  border: 1px solid #ccc;
  border-radius: 5px;
  padding: 10px;
  margin: 0; /* 确保没有外边距 */
  display: flex;
  justify-content: space-between; /* 确保按钮在右侧 */
  align-items: center;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  position: relative;
}

.alert-content {
  display: flex;
  align-items: center;
  justify-content: center; /* 确保内容居中 */
  flex-grow: 1;
}

.alert-icon {
  color: red;
  font-size: 2rem; /* 调整感叹号的大小 */
  margin-right: 10px; /* 感叹号与文字之间的间距 */
}

.alert-message {
  text-align: center; /* 文本居中对齐 */
  font-size: 1.2rem;
}

.close-btn {
  background-color: transparent;
  border: 1px solid red;
  border-radius: 15px; /* 椭圆形 */
  color: red;
  cursor: pointer;
  font-size: 1rem;
  padding: 5px 10px;
  margin-left: 10px;
}

.main-content {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  width: 100%;
  margin-top: 20px;
  box-sizing: border-box; /* 确保内外边距包含在宽度内 */
}

.graph-container {
  flex: 1;
  max-width: 48%; /* 图表宽度 */
  height: auto; /* 图表高度 */
  padding: 20px;
  background-color: white;
  border-radius: 5px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  box-sizing: border-box; /* 确保内外边距包含在宽度内 */
}

.collapsible-panels {
  flex: 1;
  margin-left: 2%; /* 与图表之间的距离 */
  max-width: 48%; /* 折叠面板宽度 */
  padding: 20px;
  background-color: #f9f9f9;
  border-radius: 5px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  box-sizing: border-box; /* 确保内外边距包含在宽度内 */
}

.collapsible-panel {
  margin-bottom: 10px;
  width: 100%; /* 确保折叠面板宽度填满容器 */
}

.panel-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 10px;
  background-color: #f1f1f1;
  cursor: pointer;
  border-radius: 5px;
}

.panel-title {
  font-weight: bold;
}

.panel-icon {
  font-size: 1.2rem;
}

.panel-content {
  padding: 10px;
  background-color: #ffffff;
  border: 1px solid #f1f1f1;
  border-radius: 0 0 5px 5px;
}
kirin-ri commented 3 months ago
.content-wrapper {
  padding: 0; /* 确保没有内边距 */
}

.page-cover {
  margin: 0; /* 确保没有外边距 */
}

.page-cover-title-frame {
  margin: 0; /* 确保没有外边距 */
  padding: 0; /* 确保没有内边距 */
}

.alert-container {
  padding: 0;
  margin: 0;
}

.alert-box {
  background-color: white;
  color: black;
  border: 1px solid #ccc;
  border-radius: 5px;
  padding: 10px;
  margin: 0; /* 确保没有外边距 */
  display: flex;
  justify-content: space-between; /* 确保按钮在右侧 */
  align-items: center;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  position: relative;
}

.alert-content {
  display: flex;
  align-items: center;
  justify-content: center; /* 确保内容居中 */
  flex-grow: 1;
}

.alert-icon {
  color: red;
  font-size: 2rem; /* 调整感叹号的大小 */
  margin-right: 10px; /* 感叹号与文字之间的间距 */
}

.alert-message {
  text-align: center; /* 文本居中对齐 */
  font-size: 1.2rem;
}

.close-btn {
  background-color: transparent;
  border: 1px solid red;
  border-radius: 15px; /* 椭圆形 */
  color: red;
  cursor: pointer;
  font-size: 1rem;
  padding: 5px 10px;
  margin-left: 10px;
}

.main-content {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  width: 100%;
  margin-top: 20px;
  padding-left: 20px; /* 增加左侧边距,避免图表贴边 */
  box-sizing: border-box; /* 确保内外边距包含在宽度内 */
}

.graph-container {
  flex: 1;
  max-width: 48%; /* 图表宽度 */
  height: auto; /* 图表高度 */
  padding: 20px;
  background-color: white;
  border-radius: 5px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  box-sizing: border-box; /* 确保内外边距包含在宽度内 */
}

.collapsible-panels {
  flex: 1;
  margin-left: 2%; /* 与图表之间的距离 */
  max-width: 33.6%; /* 折叠面板宽度,减少30% */
  padding: 20px;
  background-color: #f9f9f9;
  border-radius: 5px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  box-sizing: border-box; /* 确保内外边距包含在宽度内 */
}

.collapsible-panel {
  margin-bottom: 10px;
  width: 100%; /* 确保折叠面板宽度填满容器 */
}

.panel-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 10px;
  background-color: #f1f1f1;
  cursor: pointer;
  border-radius: 5px;
}

.panel-title {
  font-weight: bold;
}

.panel-icon {
  font-size: 1.2rem;
}

.panel-content {
  padding: 10px;
  background-color: #ffffff;
  border: 1px solid #f1f1f1;
  border-radius: 0 0 5px 5px;
}
kirin-ri commented 3 months ago
.content-wrapper {
  padding: 0; /* 确保没有内边距 */
}

.page-cover {
  margin: 0; /* 确保没有外边距 */
}

.page-cover-title-frame {
  margin: 0; /* 确保没有外边距 */
  padding: 0; /* 确保没有内边距 */
}

.alert-container {
  padding: 0;
  margin: 0;
}

.alert-box {
  background-color: white;
  color: black;
  border: 1px solid #ccc;
  border-radius: 5px;
  padding: 10px;
  margin: 0; /* 确保没有外边距 */
  display: flex;
  justify-content: space-between; /* 确保按钮在右侧 */
  align-items: center;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  position: relative;
}

.alert-content {
  display: flex;
  align-items: center;
  justify-content: center; /* 确保内容居中 */
  flex-grow: 1;
}

.alert-icon {
  color: red;
  font-size: 2rem; /* 调整感叹号的大小 */
  margin-right: 10px; /* 感叹号与文字之间的间距 */
}

.alert-message {
  text-align: center; /* 文本居中对齐 */
  font-size: 1.2rem;
}

.close-btn {
  background-color: transparent;
  border: 1px solid red;
  border-radius: 15px; /* 椭圆形 */
  color: red;
  cursor: pointer;
  font-size: 1rem;
  padding: 5px 10px;
  margin-left: 10px;
}

.main-content {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  width: 100%;
  margin-top: 20px;
  padding-left: 20px; /* 增加左侧边距,避免图表贴边 */
  box-sizing: border-box; /* 确保内外边距包含在宽度内 */
}

.graph-container {
  flex: 1;
  max-width: 48%; /* 图表宽度 */
  height: auto; /* 图表高度 */
  padding: 20px;
  background-color: white;
  border-radius: 5px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  box-sizing: border-box; /* 确保内外边距包含在宽度内 */
}

.collapsible-panels {
  flex: 1;
  margin-left: 1%; /* 减小与图表之间的距离 */
  max-width: 31.6%; /* 折叠面板宽度,减少30% */
  padding: 20px;
  background-color: #f9f9f9;
  border-radius: 5px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  box-sizing: border-box; /* 确保内外边距包含在宽度内 */
}

.collapsible-panel {
  margin-bottom: 10px;
  width: 100%; /* 确保折叠面板宽度填满容器 */
}

.panel-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 10px;
  background-color: #f1f1f1;
  cursor: pointer;
  border-radius: 5px;
}

.panel-title {
  font-weight: bold;
}

.panel-icon {
  font-size: 1.2rem;
}

.panel-content {
  padding: 10px;
  background-color: #ffffff;
  border: 1px solid #f1f1f1;
  border-radius: 0 0 5px 5px;
}
kirin-ri commented 3 months ago
import React, { useState, useEffect, useRef } from "react";
import { Chart } from 'react-chartjs-2';
import { Chart as ChartJS, CategoryScale, LinearScale, BarElement, LineElement, PointElement, Title, Tooltip, Legend, ScriptableContext } from 'chart.js';

ChartJS.register(CategoryScale, LinearScale, BarElement, LineElement, PointElement, Title, Tooltip, Legend);

const CollapsiblePanel = ({ title, content }: { title: string; content: string }) => {
  const [isOpen, setIsOpen] = useState(false);

  const togglePanel = () => {
    setIsOpen(!isOpen);
  };

  return (
    <div className="collapsible-panel">
      <div className="panel-header" onClick={togglePanel}>
        <div className="panel-title">{title}</div>
        <div className="panel-icon">{isOpen ? '-' : '+'}</div>
      </div>
      {isOpen && <div className="panel-content">{content}</div>}
    </div>
  );
};

const EmptyPage = () => {
  const test = '資金繰り表';
  const [showAlert, setShowAlert] = useState(true); // 控制警告弹窗的显示
  const chartRef = useRef<ChartJS | null>(null);

  useEffect(() => {
    const chart = chartRef.current;
    if (chart) {
      const ctx = chart.ctx;
      if (ctx && chart.data.datasets[2].data) {
        (chart.data.datasets[2].data as number[]).forEach((value, index) => {
          if (value < 0) {
            const meta = chart.getDatasetMeta(2).data[index];
            if (meta) {
              const x = meta.x;
              const y = meta.y;
              ctx.fillStyle = 'red';
              ctx.font = 'bold 12px Arial';
              ctx.fillText('!', x - 5, y - 10);
            }
          }
        });
      }
    }
  }, []);

  const AlertBox = ({ message, onClose }: { message: string; onClose: () -> void }) => {
    return (
      <div className="alert-box">
        <div className="alert-content">
          <i className="fa fa-exclamation-circle alert-icon" aria-hidden="true"></i>
          <span className="alert-message">{message}</span>
        </div>
        <button className="close-btn" onClick={onClose}>非表示</button>
      </div>
    );
  };

  const data = {
    labels: ['4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月', '1月', '2月', '3月'],
    datasets: [
      {
        type: 'bar' as const,
        label: '収入',
        data: [20, 30, 25, 15, 30, 25, 20, 30, 25, 30, 20, 25],
        backgroundColor: 'rgba(153, 102, 255, 0.5)',
      },
      {
        type: 'bar' as const,
        label: '支出',
        data: [-25, -35, -30, -20, -25, -30, -35, -25, -30, -20, -25, -30],
        backgroundColor: 'rgba(54, 162, 235, 0.5)',
      },
      {
        type: 'line' as const,
        label: '資金繰り',
        data: [10, -5, -10, 5, 10, -15, 20, -10, 15, 5, -5, 10],
        borderColor: 'black',
        backgroundColor: 'black',
        fill: false,
        tension: 0.1,
        borderWidth: 2,
        pointStyle: 'rectRot',
        pointRadius: 6,
        pointHoverRadius: 8,
        pointBackgroundColor: (context: ScriptableContext<'line'>) => {
          const index = context.dataIndex;
          const value = context.dataset.data[index] as number;
          return value < 0 ? 'red' : 'black';
        }
      }
    ],
  };

  const options = {
    responsive: true,
    maintainAspectRatio: false, /* 确保图表不会因为宽度问题而变形 */
    plugins: {
      legend: {
        position: 'top' as const,
        labels: {
          font: {
            size: 16 // 修改图例文字大小
          }
        }
      },
      title: {
        display: true,
        text: '期中資金繰り表',
        font: {
          size: 24 // 修改标题文字大小
        }
      },
      tooltip: {
        callbacks: {
          label: function(context: any) {
            const label = context.dataset.label || '';
            const value = context.raw;
            return `${label}: ${value}`;
          }
        }
      },
    },
    scales: {
      y: {
        beginAtZero: true,
      }
    }
  };

  return (
    <div className="content-wrapper metrics-details">
      <section className="page-cover">
        <div className="page-cover-title-frame">
          <h1>{test}</h1>
        </div>
      </section>
      {/* 警告弹窗 */}
      {showAlert && (
        <div className="alert-container">
          <AlertBox
            message="期中に資金がマイナスとなる期間があります"
            onClose={() => setShowAlert(false)}
          />
        </div>
      )}
      <div className="main-content">
        {/* 資金繰り表のグラフ */}
        <div className="graph-container">
          <Chart ref={chartRef} type="bar" data={data} options={options} />
        </div>
        {/* 可折叠面板 */}
        <div className="collapsible-panels">
          <CollapsiblePanel title="test1" content="abcdef" />
          <CollapsiblePanel title="test2" content="abcdef" />
          <CollapsiblePanel title="test3" content="abcdef" />
        </div>
      </div>
    </div>
  );
};

export default EmptyPage;
kirin-ri commented 3 months ago
ERROR in src/app2/App.tsx:51:15
TS2769: No overload matches this call.
  Overload 1 of 2, '(props: (RouteProps<string, { [x: string]: string | undefined; }> & OmitNative<{}, keyof RouteProps<string, { [x: string]: string | undefined; }>>) | Readonly<...>): Route<...>', gave the following error.
    Type '() => void' is not assignable to type 'ComponentType<any> | ComponentType<RouteComponentProps<any, StaticContext, unknown>> | undefined'.
      Type '() => void' is not assignable to type 'FunctionComponent<any>'.
        Type 'void' is not assignable to type 'ReactElement<any, any> | null'.
  Overload 2 of 2, '(props: RouteProps<string, { [x: string]: string | undefined; }> & OmitNative<{}, keyof RouteProps<string, { [x: string]: string | undefined; }>>, context: any): Route<...>', gave the following error.
    Type '() => void' is not assignable to type 'ComponentType<any> | ComponentType<RouteComponentProps<any, StaticContext, unknown>> | undefined'.
    49 |               path={route.path}
    50 |               exact={route.exact}
  > 51 |               component={route.component}
       |               ^^^^^^^^^
    52 |               key={idx}
    53 |             />
    54 |           ))}

ERROR in src/app2/components/pages/emptyPage.tsx:51:23
TS2552: Cannot find name 'message'. Did you mean 'onmessage'?
    49 |   }, []);
    50 |
  > 51 |   const AlertBox = ({ message, onClose }: { message: string; onClose: () -> void }) => {
       |                       ^^^^^^^
    52 |     return (
    53 |       <div className="alert-box">
    54 |         <div className="alert-content">

ERROR in src/app2/components/pages/emptyPage.tsx:51:32
TS2552: Cannot find name 'onClose'. Did you mean 'onclose'?
    49 |   }, []);
    50 |
  > 51 |   const AlertBox = ({ message, onClose }: { message: string; onClose: () -> void }) => {
       |                                ^^^^^^^
    52 |     return (
    53 |       <div className="alert-box">
    54 |         <div className="alert-content">

ERROR in src/app2/components/pages/emptyPage.tsx:51:41
TS1005: ')' expected.
    49 |   }, []);
    50 |
  > 51 |   const AlertBox = ({ message, onClose }: { message: string; onClose: () -> void }) => {
       |                                         ^
    52 |     return (
    53 |       <div className="alert-box">
    54 |         <div className="alert-content">

ERROR in src/app2/components/pages/emptyPage.tsx:51:54
TS7031: Binding element 'string' implicitly has an 'any' type.
    49 |   }, []);
    50 |
  > 51 |   const AlertBox = ({ message, onClose }: { message: string; onClose: () -> void }) => {
       |                                                      ^^^^^^
    52 |     return (
    53 |       <div className="alert-box">
    54 |         <div className="alert-content">

ERROR in src/app2/components/pages/emptyPage.tsx:51:60
TS1005: ',' expected.
    49 |   }, []);
    50 |
  > 51 |   const AlertBox = ({ message, onClose }: { message: string; onClose: () -> void }) => {
       |                                                            ^
    52 |     return (
    53 |       <div className="alert-box">
    54 |         <div className="alert-content">

ERROR in src/app2/components/pages/emptyPage.tsx:51:72
TS1109: Expression expected.
    49 |   }, []);
    50 |
  > 51 |   const AlertBox = ({ message, onClose }: { message: string; onClose: () -> void }) => {
       |                                                                        ^
    52 |     return (
    53 |       <div className="alert-box">
    54 |         <div className="alert-content">

ERROR in src/app2/components/pages/emptyPage.tsx:51:75
TS1109: Expression expected.
    49 |   }, []);
    50 |
  > 51 |   const AlertBox = ({ message, onClose }: { message: string; onClose: () -> void }) => {
       |                                                                           ^
    52 |     return (
    53 |       <div className="alert-box">
    54 |         <div className="alert-content">

ERROR in src/app2/components/pages/emptyPage.tsx:51:77
TS2532: Object is possibly 'undefined'.
    49 |   }, []);
    50 |
  > 51 |   const AlertBox = ({ message, onClose }: { message: string; onClose: () -> void }) => {
       |                                                                             ^^^^
    52 |     return (
    53 |       <div className="alert-box">
    54 |         <div className="alert-content">

ERROR in src/app2/components/pages/emptyPage.tsx:51:82
TS1109: Expression expected.
    49 |   }, []);
    50 |
  > 51 |   const AlertBox = ({ message, onClose }: { message: string; onClose: () -> void }) => {
       |                                                                                  ^
    52 |     return (
    53 |       <div className="alert-box">
    54 |         <div className="alert-content">

ERROR in src/app2/components/pages/emptyPage.tsx:51:83
TS1005: ',' expected.
    49 |   }, []);
    50 |
  > 51 |   const AlertBox = ({ message, onClose }: { message: string; onClose: () -> void }) => {
       |                                                                                   ^
    52 |     return (
    53 |       <div className="alert-box">
    54 |         <div className="alert-content">

ERROR in src/app2/components/pages/emptyPage.tsx:51:85
TS1005: ';' expected.
    49 |   }, []);
    50 |
  > 51 |   const AlertBox = ({ message, onClose }: { message: string; onClose: () -> void }) => {
       |                                                                                     ^^
    52 |     return (
    53 |       <div className="alert-box">
    54 |         <div className="alert-content">

ERROR in src/app2/components/pages/emptyPage.tsx:165:1
TS1128: Declaration or statement expected.
    163 |     </div>
    164 |   );
  > 165 | };
        | ^
    166 |
    167 | export default EmptyPage;
    168 |
kirin-ri commented 3 months ago
import React, { useState, useEffect, useRef } from "react";
import { Chart } from 'react-chartjs-2';
import { Chart as ChartJS, CategoryScale, LinearScale, BarElement, LineElement, PointElement, Title, Tooltip, Legend, ScriptableContext } from 'chart.js';

ChartJS.register(CategoryScale, LinearScale, BarElement, LineElement, PointElement, Title, Tooltip, Legend);

const CollapsiblePanel = ({ title, content }: { title: string; content: string }) => {
  const [isOpen, setIsOpen] = useState(false);

  const togglePanel = () => {
    setIsOpen(!isOpen);
  };

  return (
    <div className="collapsible-panel">
      <div className="panel-header" onClick={togglePanel}>
        <div className="panel-title">{title}</div>
        <div className="panel-icon">{isOpen ? '-' : '+'}</div>
      </div>
      {isOpen && <div className="panel-content">{content}</div>}
    </div>
  );
};

const EmptyPage = () => {
  const test = '資金繰り表';
  const [showAlert, setShowAlert] = useState(true); // 控制警告弹窗的显示
  const chartRef = useRef<ChartJS | null>(null);

  useEffect(() => {
    const chart = chartRef.current;
    if (chart) {
      const ctx = chart.ctx;
      if (ctx && chart.data.datasets[2].data) {
        (chart.data.datasets[2].data as number[]).forEach((value, index) => {
          if (value < 0) {
            const meta = chart.getDatasetMeta(2).data[index];
            if (meta) {
              const x = meta.x;
              const y = meta.y;
              ctx.fillStyle = 'red';
              ctx.font = 'bold 12px Arial';
              ctx.fillText('!', x - 5, y - 10);
            }
          }
        });
      }
    }
  }, []);

  const AlertBox = ({ message, onClose }: { message: string; onClose: () => void }) => {
    return (
      <div className="alert-box">
        <div className="alert-content">
          <i className="fa fa-exclamation-circle alert-icon" aria-hidden="true"></i>
          <span className="alert-message">{message}</span>
        </div>
        <button className="close-btn" onClick={onClose}>非表示</button>
      </div>
    );
  };

  const data = {
    labels: ['4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月', '1月', '2月', '3月'],
    datasets: [
      {
        type: 'bar' as const,
        label: '収入',
        data: [20, 30, 25, 15, 30, 25, 20, 30, 25, 30, 20, 25],
        backgroundColor: 'rgba(153, 102, 255, 0.5)',
      },
      {
        type: 'bar' as const,
        label: '支出',
        data: [-25, -35, -30, -20, -25, -30, -35, -25, -30, -20, -25, -30],
        backgroundColor: 'rgba(54, 162, 235, 0.5)',
      },
      {
        type: 'line' as const,
        label: '資金繰り',
        data: [10, -5, -10, 5, 10, -15, 20, -10, 15, 5, -5, 10],
        borderColor: 'black',
        backgroundColor: 'black',
        fill: false,
        tension: 0.1,
        borderWidth: 2,
        pointStyle: 'rectRot',
        pointRadius: 6,
        pointHoverRadius: 8,
        pointBackgroundColor: (context: ScriptableContext<'line'>) => {
          const index = context.dataIndex;
          const value = context.dataset.data[index] as number;
          return value < 0 ? 'red' : 'black';
        }
      }
    ],
  };

  const options = {
    responsive: true,
    maintainAspectRatio: false, /* 确保图表不会因为宽度问题而变形 */
    plugins: {
      legend: {
        position: 'top' as const,
        labels: {
          font: {
            size: 16 // 修改图例文字大小
          }
        }
      },
      title: {
        display: true,
        text: '期中資金繰り表',
        font: {
          size: 24 // 修改标题文字大小
        }
      },
      tooltip: {
        callbacks: {
          label: function(context: any) {
            const label = context.dataset.label || '';
            const value = context.raw;
            return `${label}: ${value}`;
          }
        }
      },
    },
    scales: {
      y: {
        beginAtZero: true,
      }
    }
  };

  return (
    <div className="content-wrapper metrics-details">
      <section className="page-cover">
        <div className="page-cover-title-frame">
          <h1>{test}</h1>
        </div>
      </section>
      {/* 警告弹窗 */}
      {showAlert && (
        <div className="alert-container">
          <AlertBox
            message="期中に資金がマイナスとなる期間があります"
            onClose={() => setShowAlert(false)}
          />
        </div>
      )}
      <div className="main-content">
        {/* 資金繰り表のグラフ */}
        <div className="graph-container">
          <Chart ref={chartRef} type="bar" data={data} options={options} />
        </div>
        {/* 可折叠面板 */}
        <div className="collapsible-panels">
          <CollapsiblePanel title="test1" content="abcdef" />
          <CollapsiblePanel title="test2" content="abcdef" />
          <CollapsiblePanel title="test3" content="abcdef" />
        </div>
      </div>
    </div>
  );
};

export default EmptyPage;
kirin-ri commented 3 months ago
.content-wrapper {
  padding: 0; /* 确保没有内边距 */
}

.page-cover {
  margin: 0; /* 确保没有外边距 */
}

.page-cover-title-frame {
  margin: 0; /* 确保没有外边距 */
  padding: 0; /* 确保没有内边距 */
}

.alert-container {
  padding: 0;
  margin: 0;
}

.alert-box {
  background-color: white;
  color: black;
  border: 1px solid #ccc;
  border-radius: 5px;
  padding: 10px;
  margin: 0; /* 确保没有外边距 */
  display: flex;
  justify-content: space-between; /* 确保按钮在右侧 */
  align-items: center;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  position: relative;
}

.alert-content {
  display: flex;
  align-items: center;
  justify-content: center; /* 确保内容居中 */
  flex-grow: 1;
}

.alert-icon {
  color: red;
  font-size: 2rem; /* 调整感叹号的大小 */
  margin-right: 10px; /* 感叹号与文字之间的间距 */
}

.alert-message {
  text-align: center; /* 文本居中对齐 */
  font-size: 1.2rem;
}

.close-btn {
  background-color: transparent;
  border: 1px solid red;
  border-radius: 15px; /* 椭圆形 */
  color: red;
  cursor: pointer;
  font-size: 1rem;
  padding: 5px 10px;
  margin-left: 10px;
}

.main-content {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  width: 100%;
  margin-top: 20px;
  padding-left: 20px; /* 增加左侧边距,避免图表贴边 */
  box-sizing: border-box; /* 确保内外边距包含在宽度内 */
}

.graph-container {
  flex: 1;
  max-width: 48%; /* 图表宽度 */
  height: auto; /* 图表高度 */
  padding: 20px;
  background-color: white;
  border-radius: 5px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}

.collapsible-panels {
  flex: 1;
  max-width: 33%; /* 折叠面板宽度,减少30% */
  padding: 20px;
  background-color: #f9f9f9;
  border-radius: 5px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}

.collapsible-panel {
  margin-bottom: 10px;
  width: 100%; /* 确保折叠面板宽度填满容器 */
}

.panel-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 10px;
  background-color: #f1f1f1;
  cursor: pointer;
  border-radius: 5px;
}

.panel-title {
  font-weight: bold;
}

.panel-icon {
  font-size: 1.2rem;
}

.panel-content {
  padding: 10px;
  background-color: #ffffff;
  border: 1px solid #f1f1f1;
  border-radius: 0 0 5px 5px;
}
kirin-ri commented 3 months ago

0801

kirin-ri commented 3 months ago
    <div className="collapsible-panel">
      <div className="panel-header" onClick={togglePanel}>
        <div className="panel-title">{title}</div>
        <div className="panel-money">{money}</div>
        <div className="panel-icon">{isOpen ? '-' : '+'}</div>
      </div>
      {isOpen && (
        <div className="panel-content">
          <div className="details-container">
            {details.map((detail, index) => (
              <div key={index} className="detail-item">
                <span>{detail.month}</span>
                <span>{detail.alert && <i className="fa fa-exclamation-triangle" style={{ color: 'black', marginRight: '5px' }} />}</span>
                <span>{detail.amount}</span>
              </div>
            ))}
          </div>
        </div>
      )}
    </div>
  );
};
kirin-ri commented 3 months ago
  const details = [
    { month: '4月', amount: 'XXXXX円' },
    { month: '5月', amount: 'XXXXX円' },
    { month: '6月', amount: 'XXXXX円' },
    { month: '7月', amount: '▲XXXXX円', alert: true },
    { month: '8月', amount: 'XXXXX円' },
    { month: '9月', amount: 'XXXXX円' },
    { month: '10月', amount: 'XXXXX円' },
    { month: '11月', amount: '▲XXXXX円', alert: true },
    { month: '12月', amount: 'XXXXX円' },
    { month: '1月', amount: 'XXXXX円' },
    { month: '2月', amount: '▲XXXXX円', alert: true },
    { month: '3月', amount: 'XXXXX円' }
  ];
kirin-ri commented 3 months ago
import React, { useState, useEffect, useRef } from "react";
import { Chart } from 'react-chartjs-2';
import { Chart as ChartJS, CategoryScale, LinearScale, BarElement, LineElement, PointElement, Title, Tooltip, Legend, ScriptableContext } from 'chart.js';

ChartJS.register(CategoryScale, LinearScale, BarElement, LineElement, PointElement, Title, Tooltip, Legend);

const CollapsiblePanel = ({ title, money, details }: { title: string; money: string; details: { month: string, amount: string, alert?: boolean }[] }) => {
  const [isOpen, setIsOpen] = useState(false);

  const togglePanel = () => {
    setIsOpen(!isOpen);
  };

  return (
    <div className="collapsible-panel">
      <div className="panel-header" onClick={togglePanel}>
        <div className="panel-title">{title}</div>
        <div className="panel-money">{money}</div>
        <div className="panel-icon">{isOpen ? '-' : '+'}</div>
      </div>
      {isOpen && (
        <div className="panel-content">
          <div className="details-container">
            {details.map((detail, index) => (
              <div key={index} className="detail-item">
                <span>{detail.month}</span>
                <span>{detail.alert && <i className="fa fa-exclamation-triangle" style={{ color: 'black', marginRight: '5px' }} />}</span>
                <span>{detail.amount}</span>
              </div>
            ))}
          </div>
        </div>
      )}
    </div>
  );
};

const EmptyPage = () => {
  const test = '資金繰り表';
  const [showAlert, setShowAlert] = useState(true);
  const chartRef = useRef<ChartJS | null>(null);

  useEffect(() => {
    const chart = chartRef.current;
    if (chart) {
      const ctx = chart.ctx;
      if (ctx && chart.data.datasets[2].data) {
        (chart.data.datasets[2].data as number[]).forEach((value, index) => {
          if (value < 0) {
            const meta = chart.getDatasetMeta(2).data[index];
            if (meta) {
              const x = meta.x;
              const y = meta.y;
              ctx.fillStyle = 'red';
              ctx.font = 'bold 12px Arial';
              ctx.fillText('!', x - 5, y - 10);
            }
          }
        });
      }
    }
  }, []);

  const AlertBox = ({ message, onClose }: { message: string; onClose: () => void }) => {
    return (
      <div className="alert-box">
        <div className="alert-content">
          <i className="fa fa-exclamation-circle alert-icon" aria-hidden="true"></i>
          <span className="alert-message">{message}</span>
        </div>
        <button className="close-btn" onClick={onClose}>非表示</button>
      </div>
    );
  };

  const data = {
    labels: ['4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月', '1月', '2月', '3月'],
    datasets: [
      {
        type: 'bar' as const,
        label: '収入',
        data: [32000, 28000, 35000, 37000, 31000, 30000, 33000, 29000, 36000, 34000, 31000, 37000],
        backgroundColor: 'rgba(153, 102, 255, 0.5)',
      },
      {
        type: 'bar' as const,
        label: '支出',
        data: [-15000, -18000, -16000, -17000, -20000, -19000, -18000, -16000, -15000, -14000, -18000, -17000],
        backgroundColor: 'rgba(54, 162, 235, 0.5)',
      },
      {
        type: 'line' as const,
        label: '営業キャッシュフロー',
        data: [17000, 10000, 19000, 20000, 11000, 11000, 15000, 13000, 21000, 20000, 13000, 20000],
        borderColor: 'black',
        backgroundColor: 'black',
        fill: false,
        tension: 0.1,
        borderWidth: 2,
        pointStyle: 'rectRot',
        pointRadius: 6,
        pointHoverRadius: 8,
        pointBackgroundColor: (context: ScriptableContext<'line'>) => {
          const index = context.dataIndex;
          const value = context.dataset.data[index] as number;
          return value < 0 ? 'red' : 'black';
        }
      }
    ],
  };

  const options = {
    responsive: true,
    maintainAspectRatio: false,
    plugins: {
      legend: {
        position: 'top' as const,
        labels: {
          font: {
            size: 16
          }
        }
      },
      title: {
        display: true,
        text: '期中資金繰り表',
        font: {
          size: 24
        }
      },
      tooltip: {
        callbacks: {
          label: function(context: any) {
            const label = context.dataset.label || '';
            const value = context.raw;
            return `${label}: ${value}`;
          }
        }
      },
    },
    scales: {
      y: {
        beginAtZero: true,
      }
    }
  };

  const details = [
    { month: '4月', amount: 'XXXXX円' },
    { month: '5月', amount: 'XXXXX円' },
    { month: '6月', amount: 'XXXXX円' },
    { month: '7月', amount: '▲XXXXX円', alert: true },
    { month: '8月', amount: 'XXXXX円' },
    { month: '9月', amount: 'XXXXX円' },
    { month: '10月', amount: 'XXXXX円' },
    { month: '11月', amount: '▲XXXXX円', alert: true },
    { month: '12月', amount: 'XXXXX円' },
    { month: '1月', amount: 'XXXXX円' },
    { month: '2月', amount: '▲XXXXX円', alert: true },
    { month: '3月', amount: 'XXXXX円' }
  ];

  return (
    <div className="content-wrapper metrics-details">
      <section className="page-cover">
        <div className="page-cover-title-frame">
          <h1>{test}</h1>
        </div>
      </section>
      {showAlert && (
        <div className="alert-container">
          <AlertBox
            message="期中に資金がマイナスとなる期間があります"
            onClose={() => setShowAlert(false)}
          />
        </div>
      )}
      <div className="main-content">
        <div className="graph-container">
          <Chart ref={chartRef} type="bar" data={data} options={options} />
        </div>
        <div className="collapsible-panels">
          <CollapsiblePanel title="営業キャッシュフロー" money="343,564円" details={details} />
        </div>
      </div>
    </div>
  );
};

export default EmptyPage;
kirin-ri commented 3 months ago
.details-container {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 10px;
}

.detail-item {
  display: flex;
  align-items: center;
}

.detail-item span {
  margin-right: 5px;
}
kirin-ri commented 3 months ago
const details = [
  { month: '4月', amount: '28,000円' },
  { month: '5月', amount: '29,000円' },
  { month: '6月', amount: '27,500円' },
  { month: '7月', amount: '30,500円', alert: true },
  { month: '8月', amount: '29,000円' },
  { month: '9月', amount: '28,000円' },
  { month: '10月', amount: '32,000円' },
  { month: '11月', amount: '28,500円', alert: true },
  { month: '12月', amount: '29,000円' },
  { month: '1月', amount: '31,000円' },
  { month: '2月', amount: '30,000円', alert: true },
  { month: '3月', amount: '21,064円' }
];
kirin-ri commented 3 months ago

style={{ color: detail.alert ? 'red' : 'black' }}>

kirin-ri commented 3 months ago
import { BarElement, CategoryScale, Chart as ChartJS, Legend, LinearScale, LineElement, PointElement, ScriptableContext, Title, Tooltip } from 'chart.js';
import { useEffect, useRef, useState } from "react";
import { Chart } from 'react-chartjs-2';

ChartJS.register(CategoryScale, LinearScale, BarElement, LineElement, PointElement, Title, Tooltip, Legend);

const CollapsiblePanel = ({ title, money, details }: { title: string; money: string; details: { month: string, amount: string, alert?: boolean }[] }) => {
  const [isOpen, setIsOpen] = useState(false);

  const togglePanel = () => {
    setIsOpen(!isOpen);
  };

  return (
    <div className="collapsible-panel">
      <div className="panel-header" onClick={togglePanel}>
        <div className="panel-title">{title}</div>
        <div className="panel-money">{money}</div>
      </div>
      {isOpen && (
        <div className="panel-content">
          <div className="details-container">
            {details.map((detail, index) => (
              <div key={index} className="detail-item" style={{ color: detail.alert ? 'red' : 'black' }}>
                <span>{detail.month}</span>
                <span>{detail.alert && <i style={{ color: 'red', marginRight: '5px' }} />}</span>
                <span>{detail.amount}</span>
              </div>
            ))}
          </div>
        </div>
      )}
    </div>
  );
};

const EmptyPage = () => {
  const test = '資金繰り表';
  const [showAlert, setShowAlert] = useState(true); // 控制警告弹窗的显示
  const chartRef = useRef<ChartJS | null>(null);

  useEffect(() => {
    const chart = chartRef.current;
    if (chart) {
      const ctx = chart.ctx;
      if (ctx && chart.data.datasets[2].data) {
        (chart.data.datasets[2].data as number[]).forEach((value, index) => {
          if (value < 0) {
            const meta = chart.getDatasetMeta(2).data[index];
            if (meta) {
              const x = meta.x;
              const y = meta.y;
              ctx.fillStyle = 'red';
              ctx.font = 'bold 12px Arial';
              ctx.fillText('!', x - 5, y - 10);
            }
          }
        });
      }
    }
  }, []);

  const AlertBox = ({ message, onClose }: { message: string; onClose: () => void }) => {
    return (
      <div className="alert-box">
        <div className="alert-content">
          <i className="fa fa-exclamation-circle alert-icon" aria-hidden="true"></i>
          <span className="alert-message">{message}</span>
        </div>
        <button className="close-btn" onClick={onClose}>非表示</button>
      </div>
    );
  };

  const data = {
    labels: ['4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月', '1月', '2月', '3月'],
    datasets: [
      {
        type: 'bar' as const,
        label: '収入',
        data: [20, 30, 25, 15, 30, 25, 20, 30, 25, 30, 20, 25],
        backgroundColor: 'rgba(153, 102, 255, 0.5)',
      },
      {
        type: 'bar' as const,
        label: '支出',
        data: [-25, -35, -30, -20, -25, -30, -35, -25, -30, -20, -25, -30],
        backgroundColor: 'rgba(54, 162, 235, 0.5)',
      },
      {
        type: 'line' as const,
        label: '資金繰り',
        data: [10, -5, -10, 5, 10, -15, 20, -10, 15, 5, -5, 10],
        borderColor: 'black',
        backgroundColor: 'black',
        fill: false,
        tension: 0.1,
        borderWidth: 2,
        pointStyle: 'rectRot',
        pointRadius: 6,
        pointHoverRadius: 8,
        pointBackgroundColor: (context: ScriptableContext<'line'>) => {
          const index = context.dataIndex;
          const value = context.dataset.data[index] as number;
          return value < 0 ? 'red' : 'black';
        }
      }
    ],
  };

  const details = [
    { month: '4月', amount: 'XXXXX円' },
    { month: '5月', amount: 'XXXXX円' },
    { month: '6月', amount: 'XXXXX円' },
    { month: '7月', amount: '▲XXXXX円', alert: true },
    { month: '8月', amount: 'XXXXX円' },
    { month: '9月', amount: 'XXXXX円' },
    { month: '10月', amount: 'XXXXX円' },
    { month: '11月', amount: '▲XXXXX円', alert: true },
    { month: '12月', amount: 'XXXXX円' },
    { month: '1月', amount: 'XXXXX円' },
    { month: '2月', amount: '▲XXXXX円', alert: true },
    { month: '3月', amount: 'XXXXX円' }
  ];

  const options = {
    responsive: true,
    plugins: {
      legend: {
        position: 'top' as const,
        labels: {
          font: {
            size: 16 // 修改图例文字大小
          }
        }
      },
      title: {
        display: true,
        text: '期中資金繰り表',
        font: {
          size: 24 // 修改标题文字大小
        }
      },
      tooltip: {
        callbacks: {
          label: function(context: any) {
            const label = context.dataset.label || '';
            const value = context.raw;
            return `${label}: ${value}`;
          }
        }
      },
    },
    scales: {
      y: {
        beginAtZero: true,
      }
    }
  };

  return (
    <div className="content-wrapper metrics-details">
      <section className="page-cover">
        <div className="page-cover-title-frame">
          <h1>{test}</h1>
        </div>
      </section>
      {/* 警告弹窗 */}
      {showAlert && (
        <div className="alert-container">
          <AlertBox
            message="期中に資金がマイナスとなる期間があります"
            onClose={() => setShowAlert(false)}
          />
        </div>
      )}
      <div className="main-content">
        {/* 資金繰り表のグラフ */}
        <div className="graph-container">
          <Chart ref={chartRef} type="bar" data={data} options={options} />
        </div>
        {/* 可折叠面板 */}
        <div className="collapsible-panels">
          <CollapsiblePanel title="営業キャッシュフロー" money="343,564円" details={details}/>
          <CollapsiblePanel title="投資キャッシュフロー" money="435,435円" details={details} />
          <CollapsiblePanel title="財務キャッシュフロー" money="567,232円" details={details} />
        </div>
      </div>
    </div>
  );
};

export default EmptyPage;
kirin-ri commented 3 months ago
.content-wrapper {
  padding: 0; /* 确保没有内边距 */
}

.page-cover {
  margin: 0; /* 确保没有外边距 */
}

.page-cover-title-frame {
  margin: 0; /* 确保没有外边距 */
  padding: 0; /* 确保没有内边距 */
}

.alert-container {
  padding: 0;
  margin: 0;
}

.alert-box {
  background-color: white;
  color: black;
  border: 1px solid #ccc;
  border-radius: 5px;
  padding: 10px;
  margin: 0; /* 确保没有外边距 */
  display: flex;
  justify-content: space-between; /* 确保按钮在右侧 */
  align-items: center;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  position: relative;
}

.alert-content {
  display: flex;
  align-items: center;
  justify-content: center; /* 确保内容居中 */
  flex-grow: 1;
}

.alert-icon {
  color: red;
  font-size: 2rem; /* 调整感叹号的大小 */
  margin-right: 10px; /* 感叹号与文字之间的间距 */
}

.alert-message {
  text-align: center; /* 文本居中对齐 */
  font-size: 1.2rem;
}

.close-btn {
  background-color: transparent;
  border: 1px solid red;
  border-radius: 15px; /* 椭圆形 */
  color: red;
  cursor: pointer;
  font-size: 1rem;
  padding: 5px 10px;
  margin-left: 10px;
}

.main-content {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  width: 100%;
  margin-top: 20px;
  padding-left: 20px; /* 增加左侧边距,避免图表贴边 */
  box-sizing: border-box; /* 确保内外边距包含在宽度内 */
}

.graph-container {
  flex: 1;
  max-width: 48%; /* 图表宽度 */
  height: auto; /* 图表高度 */
  padding: 20px;
  margin-left: 2%;
  background-color: white;
  border-radius: 5px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}

.collapsible-panels {
  flex: 1;
  max-width: 40%; /* 折叠面板宽度,减少30% */
  padding: 20px;
  background-color: #f9f9f9;
  border-radius: 5px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  margin-right: 5%;
}

.collapsible-panel {
  margin-bottom: 10px;
  width: 100%; /* 确保折叠面板宽度填满容器 */
}

.panel-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 10px;
  background-color: #f1f1f1;
  cursor: pointer;
  border-radius: 5px;
}

.panel-title {
  font-weight: bold;
  font-size: 1.2rem;
}

.panel-money {
  font-weight: bold;
  margin-left: auto;
  font-size: 1.2rem;
}

.panel-content {
  padding: 10px;
  background-color: #ffffff;
  border: 1px solid #f1f1f1;
  border-radius: 0 0 5px 5px;
}

.details-container {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 10px;
}

.detail-item {
  display: flex;
  align-items: center;
}

.detail-item span {
  margin-right: 5px;
}
kirin-ri commented 3 months ago

0801-2

kirin-ri commented 3 months ago
import { BarElement, CategoryScale, Chart as ChartJS, Legend, LinearScale, LineElement, PointElement, ScriptableContext, Title, Tooltip } from 'chart.js';
import { useEffect, useRef, useState } from "react";
import { Chart } from 'react-chartjs-2';

ChartJS.register(CategoryScale, LinearScale, BarElement, LineElement, PointElement, Title, Tooltip, Legend);

const CollapsiblePanel = ({ title, money, details }: { title: string; money: string; details: { month: string, amount: string, alert?: boolean }[] }) => {
  const [isOpen, setIsOpen] = useState(false);

  const togglePanel = () => {
    setIsOpen(!isOpen);
  };

  return (
    <div className="collapsible-panel">
      <div className="panel-header" onClick={togglePanel}>
        <div className="panel-title">{title}</div>
        <div className="panel-money">{money}</div>
      </div>
      {isOpen && (
        <div className="panel-content">
          <div className="details-container">
            {details.map((detail, index) => (
              <div key={index} className="detail-item" style={{ color: detail.alert ? 'red' : 'black' }}>
                <span>{detail.month}</span>
                <span>{detail.alert && <i style={{ color: 'red', marginRight: '5px' }} />}</span>
                <span>{detail.amount}</span>
              </div>
            ))}
          </div>
        </div>
      )}
    </div>
  );
};

const EmptyPage = () => {
  const test = '資金繰り表';
  const [showAlert, setShowAlert] = useState(true); // 控制警告弹窗的显示
  const chartRef = useRef<ChartJS | null>(null);

  useEffect(() => {
    const chart = chartRef.current;
    if (chart) {
      const ctx = chart.ctx;
      if (ctx && chart.data.datasets[2].data) {
        (chart.data.datasets[2].data as number[]).forEach((value, index) => {
          if (value < 0) {
            const meta = chart.getDatasetMeta(2).data[index];
            if (meta) {
              const x = meta.x;
              const y = meta.y;
              ctx.fillStyle = 'red';
              ctx.font = 'bold 12px Arial';
              ctx.fillText('!', x - 5, y - 10);
            }
          }
        });
      }
    }
  }, []);

  const AlertBox = ({ message, onClose }: { message: string; onClose: () => void }) => {
    return (
      <div className="alert-box">
        <div className="alert-content">
          <i className="fa fa-exclamation-circle alert-icon" aria-hidden="true"></i>
          <span className="alert-message">{message}</span>
        </div>
        <button className="close-btn" onClick={onClose}>非表示</button>
      </div>
    );
  };

  const data = {
    labels: ['4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月', '1月', '2月', '3月'],
    datasets: [
      {
        type: 'bar' as const,
        label: '収入',
        data: [20, 30, 25, 15, 30, 25, 20, 30, 25, 30, 20, 25],
        backgroundColor: 'rgba(153, 102, 255, 0.5)',
      },
      {
        type: 'bar' as const,
        label: '支出',
        data: [-25, -35, -30, -20, -25, -30, -35, -25, -30, -20, -25, -30],
        backgroundColor: 'rgba(54, 162, 235, 0.5)',
      },
      {
        type: 'line' as const,
        label: '資金繰り',
        data: [10, -5, -10, 5, 10, -15, 20, -10, 15, 5, -5, 10],
        borderColor: 'black',
        backgroundColor: 'black',
        fill: false,
        tension: 0.1,
        borderWidth: 2,
        pointStyle: 'rectRot',
        pointRadius: 6,
        pointHoverRadius: 8,
        pointBackgroundColor: (context: ScriptableContext<'line'>) => {
          const index = context.dataIndex;
          const value = context.dataset.data[index] as number;
          return value < 0 ? 'red' : 'black';
        }
      }
    ],
  };

  const details = [
    { month: '4月', amount: 'XXXXX円' },
    { month: '5月', amount: 'XXXXX円' },
    { month: '6月', amount: 'XXXXX円' },
    { month: '7月', amount: '▲XXXXX円', alert: true },
    { month: '8月', amount: 'XXXXX円' },
    { month: '9月', amount: 'XXXXX円' },
    { month: '10月', amount: 'XXXXX円' },
    { month: '11月', amount: '▲XXXXX円', alert: true },
    { month: '12月', amount: 'XXXXX円' },
    { month: '1月', amount: 'XXXXX円' },
    { month: '2月', amount: '▲XXXXX円', alert: true },
    { month: '3月', amount: 'XXXXX円' }
  ];

  const options = {
    responsive: true,
    plugins: {
      legend: {
        position: 'top' as const,
        labels: {
          font: {
            size: 16 // 修改图例文字大小
          }
        }
      },
      title: {
        display: true,
        text: '期中資金繰り表',
        font: {
          size: 24 // 修改标题文字大小
        }
      },
      tooltip: {
        callbacks: {
          label: function(context: any) {
            const label = context.dataset.label || '';
            const value = context.raw;
            return `${label}: ${value}`;
          }
        }
      },
    },
    scales: {
      y: {
        beginAtZero: true,
      }
    }
  };

  return (
    <div className="content-wrapper metrics-details">
      <section className="page-cover">
        <div className="page-cover-title-frame">
          <h1>{test}</h1>
        </div>
      </section>
      {/* 警告弹窗 */}
      {showAlert && (
        <div className="alert-container">
          <AlertBox
            message="期中に資金がマイナスとなる期間があります"
            onClose={() => setShowAlert(false)}
          />
        </div>
      )}
      <div className="main-content">
        {/* 資金繰り表のグラフ */}
        <div className="graph-container">
          <Chart ref={chartRef} type="bar" data={data} options={options} />
        </div>
        {/* 可折叠面板 */}
        <div className="collapsible-panels">
          <CollapsiblePanel title="営業キャッシュフロー" money="343,564円" details={details} />
          <CollapsiblePanel title="投資キャッシュフロー" money="435,435円" details={details} />
          <CollapsiblePanel title="財務キャッシュフロー" money="567,232円" details={details} />
        </div>
      </div>
      {/* データ絞り込み と データ比較 */}
      <div className="additional-section">
        <div className="data-filter">
          <h2>データ絞り込み</h2>
          <div className="filter-group">
            <button className="filter-btn">収入</button>
            <select className="filter-select">
              <option>楽観</option>
              <option>中立</option>
              <option>悲観</option>
            </select>
          </div>
          <div className="filter-group">
            <button className="filter-btn">支出</button>
            <select className="filter-select">
              <option>楽観</option>
              <option>中立</option>
              <option>悲観</option>
            </select>
          </div>
        </div>
        <div className="data-comparison">
          <h2>データ比較</h2>
          <button className="comparison-btn active">時系列比較</button>
          <button className="comparison-btn">製品別比較</button>
          <button className="comparison-btn">顧客別比較</button>
        </div>
      </div>
    </div>
  );
};

export default EmptyPage;
kirin-ri commented 3 months ago
.content-wrapper {
  padding: 0; /* 确保没有内边距 */
}

.page-cover {
  margin: 0; /* 确保没有外边距 */
}

.page-cover-title-frame {
  margin: 0; /* 确保没有外边距 */
  padding: 0; /* 确保没有内边距 */
}

.alert-container {
  padding: 0;
  margin: 0;
}

.alert-box {
  background-color: white;
  color: black;
  border: 1px solid #ccc;
  border-radius: 5px;
  padding: 10px;
  margin: 0; /* 确保没有外边距 */
  display: flex;
  justify-content: space-between; /* 确保按钮在右侧 */
  align-items: center;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  position: relative;
}

.alert-content {
  display: flex;
  align-items: center;
  justify-content: center; /* 确保内容居中 */
  flex-grow: 1;
}

.alert-icon {
  color: red;
  font-size: 2rem; /* 调整感叹号的大小 */
  margin-right: 10px; /* 感叹号与文字之间的间距 */
}

.alert-message {
  text-align: center; /* 文本居中对齐 */
  font-size: 1.2rem;
}

.close-btn {
  background-color: transparent;
  border: 1px solid red;
  border-radius: 15px; /* 椭圆形 */
  color: red;
  cursor: pointer;
  font-size: 1rem;
  padding: 5px 10px;
  margin-left: 10px;
}

.main-content {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  width: 100%;
  margin-top: 20px;
  padding-left: 20px; /* 增加左侧边距,避免图表贴边 */
  box-sizing: border-box; /* 确保内外边距包含在宽度内 */
}

.graph-container {
  flex: 1;
  max-width: 48%; /* 图表宽度 */
  height: auto; /* 图表高度 */
  padding: 20px;
  margin-left: 2%;
  background-color: white;
  border-radius: 5px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}

.collapsible-panels {
  flex: 1;
  max-width: 40%; /* 折叠面板宽度,减少30% */
  padding: 20px;
  background-color: #f9f9f9;
  border-radius: 5px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  margin-right: 5%;
}

.collapsible-panel {
  margin-bottom: 10px;
  width: 100%; /* 确保折叠面板宽度填满容器 */
}

.panel-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 10px;
  background-color: #f1f1f1;
  cursor: pointer;
  border-radius: 5px;
}

.panel-title {
  font-weight: bold;
  font-size: 1.2rem;
}

.panel-money {
  font-weight: bold;
  margin-left: auto;
  font-size: 1.2rem;
}

.panel-content {
  padding: 10px;
  background-color: #ffffff;
  border: 1px solid #f1f1f1;
  border-radius: 0 0 5px 5px;
}

.details-container {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 10px;
}

.detail-item {
  display: flex;
  align-items: center;
}

.detail-item span {
  margin-right: 5px;
}

/* 新增样式 */
.additional-section {
  display: flex;
  justify-content: space-between;
  margin-top: 20px;
}

.data-filter, .data-comparison {
  width: 45%;
  padding: 20px;
  background-color: #f9f9f9;
  border-radius: 5px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}

.data-filter h2, .data-comparison h2 {
  margin-bottom: 20px;
  text-align: center;
  font-size: 1.5rem;
}

.filter-group, .comparison-group {
  margin-bottom: 10px;
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.filter-btn, .comparison-btn {
  width: 45%;
  padding: 10px;
  background-color: #3498db;
  color: white;
  border: none;
  border-radius: 5px;
  cursor: pointer;
  font-size: 1rem;
}

.filter-select {
  width: 45%;
  padding: 10px;
  border: 1px solid #ccc;
  border-radius: 5px;
  font-size: 1rem;
}

.comparison-btn {
  width: 100%;
  margin-bottom: 10px;
}

.comparison-btn.active {
  background-color: #2980b9;
}
kirin-ri commented 3 months ago
.content-wrapper {
  padding: 0; /* 确保没有内边距 */
}

.page-cover {
  margin: 0; /* 确保没有外边距 */
}

.page-cover-title-frame {
  margin: 0; /* 确保没有外边距 */
  padding: 0; /* 确保没有内边距 */
}

.alert-container {
  padding: 0;
  margin: 0;
}

.alert-box {
  background-color: white;
  color: black;
  border: 1px solid #ccc;
  border-radius: 5px;
  padding: 10px;
  margin: 0; /* 确保没有外边距 */
  display: flex;
  justify-content: space-between; /* 确保按钮在右侧 */
  align-items: center;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  position: relative;
}

.alert-content {
  display: flex;
  align-items: center;
  justify-content: center; /* 确保内容居中 */
  flex-grow: 1;
}

.alert-icon {
  color: red;
  font-size: 2rem; /* 调整感叹号的大小 */
  margin-right: 10px; /* 感叹号与文字之间的间距 */
}

.alert-message {
  text-align: center; /* 文本居中对齐 */
  font-size: 1.2rem;
}

.close-btn {
  background-color: transparent;
  border: 1px solid red;
  border-radius: 15px; /* 椭圆形 */
  color: red;
  cursor: pointer;
  font-size: 1rem;
  padding: 5px 10px;
  margin-left: 10px;
}

.main-content {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  width: 100%;
  margin-top: 20px;
  padding-left: 20px; /* 增加左侧边距,避免图表贴边 */
  box-sizing: border-box; /* 确保内外边距包含在宽度内 */
  position: relative;
}

.graph-container {
  flex: 1;
  max-width: 48%; /* 图表宽度 */
  height: auto; /* 图表高度 */
  padding: 20px;
  margin-left: 2%;
  background-color: white;
  border-radius: 5px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}

.collapsible-panels {
  flex: 1;
  max-width: 40%; /* 折叠面板宽度,减少30% */
  padding: 20px;
  background-color: #f9f9f9;
  border-radius: 5px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  margin-right: 5%;
}

.collapsible-panel {
  margin-bottom: 10px;
  width: 100%; /* 确保折叠面板宽度填满容器 */
}

.panel-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 10px;
  background-color: #f1f1f1;
  cursor: pointer;
  border-radius: 5px;
}

.panel-title {
  font-weight: bold;
  font-size: 1.2rem;
}

.panel-money {
  font-weight: bold;
  margin-left: auto;
  font-size: 1.2rem;
}

.panel-content {
  padding: 10px;
  background-color: #ffffff;
  border: 1px solid #f1f1f1;
  border-radius: 0 0 5px 5px;
}

.details-container {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 10px;
}

.detail-item {
  display: flex;
  align-items: center;
}

.detail-item span {
  margin-right: 5px;
}

/* 新增样式 */
.additional-section {
  display: flex;
  flex-direction: column;
  position: absolute;
  bottom: 20px; /* 调整距离底部的距离 */
  left: 20px; /* 调整距离左侧的距离 */
}

.data-filter, .data-comparison {
  width: 300px; /* 固定宽度 */
  padding: 20px;
  background-color: #f9f9f9;
  border-radius: 5px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  margin-bottom: 20px; /* 两个框之间的间距 */
}

.data-filter h2, .data-comparison h2 {
  margin-bottom: 20px;
  text-align: center;
  font-size: 1.5rem;
}

.filter-group, .comparison-group {
  margin-bottom: 10px;
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.filter-btn, .comparison-btn {
  width: 45%;
  padding: 10px;
  background-color: #3498db;
  color: white;
  border: none;
  border-radius: 5px;
  cursor: pointer;
  font-size: 1rem;
}

.filter-select {
  width: 45%;
  padding: 10px;
  border: 1px solid #ccc;
  border-radius: 5px;
  font-size: 1rem;
}

.comparison-btn {
  width: 100%;
  margin-bottom: 10px;
}

.comparison-btn.active {
  background-color: #2980b9;
}
kirin-ri commented 3 months ago
.content-wrapper {
  padding: 0; /* 确保没有内边距 */
}

.page-cover {
  margin: 0; /* 确保没有外边距 */
}

.page-cover-title-frame {
  margin: 0; /* 确保没有外边距 */
  padding: 0; /* 确保没有内边距 */
}

.alert-container {
  padding: 0;
  margin: 0;
}

.alert-box {
  background-color: white;
  color: black;
  border: 1px solid #ccc;
  border-radius: 5px;
  padding: 10px;
  margin: 0; /* 确保没有外边距 */
  display: flex;
  justify-content: space-between; /* 确保按钮在右侧 */
  align-items: center;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  position: relative;
}

.alert-content {
  display: flex;
  align-items: center;
  justify-content: center; /* 确保内容居中 */
  flex-grow: 1;
}

.alert-icon {
  color: red;
  font-size: 2rem; /* 调整感叹号的大小 */
  margin-right: 10px; /* 感叹号与文字之间的间距 */
}

.alert-message {
  text-align: center; /* 文本居中对齐 */
  font-size: 1.2rem;
}

.close-btn {
  background-color: transparent;
  border: 1px solid red;
  border-radius: 15px; /* 椭圆形 */
  color: red;
  cursor: pointer;
  font-size: 1rem;
  padding: 5px 10px;
  margin-left: 10px;
}

.main-content {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  width: 100%;
  margin-top: 20px;
  padding-left: 20px; /* 增加左侧边距,避免图表贴边 */
  box-sizing: border-box; /* 确保内外边距包含在宽度内 */
  position: relative;
}

.graph-container {
  flex: 1;
  max-width: 48%; /* 图表宽度 */
  height: auto; /* 图表高度 */
  padding: 20px;
  margin-left: 2%;
  background-color: white;
  border-radius: 5px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}

.collapsible-panels {
  flex: 1;
  max-width: 40%; /* 折叠面板宽度,减少30% */
  padding: 20px;
  background-color: #f9f9f9;
  border-radius: 5px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  margin-right: 5%;
}

.collapsible-panel {
  margin-bottom: 10px;
  width: 100%; /* 确保折叠面板宽度填满容器 */
}

.panel-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 10px;
  background-color: #f1f1f1;
  cursor: pointer;
  border-radius: 5px;
}

.panel-title {
  font-weight: bold;
  font-size: 1.2rem;
}

.panel-money {
  font-weight: bold;
  margin-left: auto;
  font-size: 1.2rem;
}

.panel-content {
  padding: 10px;
  background-color: #ffffff;
  border: 1px solid #f1f1f1;
  border-radius: 0 0 5px 5px;
}

.details-container {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 10px;
}

.detail-item {
  display: flex;
  align-items: center;
}

.detail-item span {
  margin-right: 5px;
}

/* 新增样式 */
.additional-section {
  display: flex;
  justify-content: space-between;
  position: absolute;
  bottom: 20px; /* 调整距离底部的距离 */
  left: 20px; /* 调整距离左侧的距离 */
  width: calc(100% - 40px); /* 根据需求调整宽度 */
}

.data-filter, .data-comparison {
  width: 48%; /* 固定宽度 */
  padding: 20px;
  background-color: #f9f9f9;
  border-radius: 5px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}

.data-filter h2, .data-comparison h2 {
  margin-bottom: 20px;
  text-align: center;
  font-size: 1.5rem;
}

.filter-group, .comparison-group {
  margin-bottom: 10px;
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.filter-btn, .comparison-btn {
  width: 45%;
  padding: 10px;
  background-color: #3498db;
  color: white;
  border: none;
  border-radius: 5px;
  cursor: pointer;
  font-size: 1rem;
}

.filter-select {
  width: 45%;
  padding: 10px;
  border: 1px solid #ccc;
  border-radius: 5px;
  font-size: 1rem;
}

.comparison-btn {
  width: 100%;
  margin-bottom: 10px;
}

.comparison-btn.active {
  background-color: #2980b9;
}
kirin-ri commented 3 months ago
.content-wrapper {
  padding: 0; /* 确保没有内边距 */
}

.page-cover {
  margin: 0; /* 确保没有外边距 */
}

.page-cover-title-frame {
  margin: 0; /* 确保没有外边距 */
  padding: 0; /* 确保没有内边距 */
}

.alert-container {
  padding: 0;
  margin: 0;
}

.alert-box {
  background-color: white;
  color: black;
  border: 1px solid #ccc;
  border-radius: 5px;
  padding: 10px;
  margin: 0; /* 确保没有外边距 */
  display: flex;
  justify-content: space-between; /* 确保按钮在右侧 */
  align-items: center;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  position: relative;
}

.alert-content {
  display: flex;
  align-items: center;
  justify-content: center; /* 确保内容居中 */
  flex-grow: 1;
}

.alert-icon {
  color: red;
  font-size: 2rem; /* 调整感叹号的大小 */
  margin-right: 10px; /* 感叹号与文字之间的间距 */
}

.alert-message {
  text-align: center; /* 文本居中对齐 */
  font-size: 1.2rem;
}

.close-btn {
  background-color: transparent;
  border: 1px solid red;
  border-radius: 15px; /* 椭圆形 */
  color: red;
  cursor: pointer;
  font-size: 1rem;
  padding: 5px 10px;
  margin-left: 10px;
}

.main-content {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  width: 100%;
  margin-top: 20px;
  padding-left: 20px; /* 增加左侧边距,避免图表贴边 */
  box-sizing: border-box; /* 确保内外边距包含在宽度内 */
  position: relative;
}

.graph-container {
  flex: 1;
  max-width: 25%; /* 图表宽度 */
  height: auto; /* 图表高度 */
  padding: 20px;
  margin-left: 2%;
  background-color: white;
  border-radius: 5px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}

.collapsible-panels {
  flex: 1;
  max-width: 25%; /* 折叠面板宽度 */
  padding: 20px;
  background-color: #f9f9f9;
  border-radius: 5px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  margin-right: 5%;
}

.collapsible-panel {
  margin-bottom: 10px;
  width: 100%; /* 确保折叠面板宽度填满容器 */
}

.panel-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 10px;
  background-color: #f1f1f1;
  cursor: pointer;
  border-radius: 5px;
}

.panel-title {
  font-weight: bold;
  font-size: 1.2rem;
}

.panel-money {
  font-weight: bold;
  margin-left: auto;
  font-size: 1.2rem;
}

.panel-content {
  padding: 10px;
  background-color: #ffffff;
  border: 1px solid #f1f1f1;
  border-radius: 0 0 5px 5px;
}

.details-container {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 10px;
}

.detail-item {
  display: flex;
  align-items: center;
}

.detail-item span {
  margin-right: 5px;
}

/* 新增样式 */
.additional-section {
  display: flex;
  justify-content: space-between;
  position: absolute;
  bottom: 20px; /* 调整距离底部的距离 */
  left: 20px; /* 调整距离左侧的距离 */
  width: calc(50% - 40px); /* 根据需求调整宽度 */
}

.data-filter, .data-comparison {
  width: 45%; /* 固定宽度 */
  padding: 20px;
  background-color: #f9f9f9;
  border-radius: 5px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}

.data-filter h2, .data-comparison h2 {
  margin-bottom: 20px;
  text-align: center;
  font-size: 1.5rem;
}

.filter-group, .comparison-group {
  margin-bottom: 10px;
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.filter-btn, .comparison-btn {
  width: 45%;
  padding: 10px;
  background-color: #3498db;
  color: white;
  border: none;
  border-radius: 5px;
  cursor: pointer;
  font-size: 1rem;
}

.filter-select {
  width: 45%;
  padding: 10px;
  border: 1px solid #ccc;
  border-radius: 5px;
  font-size: 1rem;
}

.comparison-btn {
  width: 100%;
  margin-bottom: 10px;
}

.comparison-btn.active {
  background-color: #2980b9;
}
kirin-ri commented 3 months ago
.content-wrapper {
  padding: 0; /* 确保没有内边距 */
}

.page-cover {
  margin: 0; /* 确保没有外边距 */
}

.page-cover-title-frame {
  margin: 0; /* 确保没有外边距 */
  padding: 0; /* 确保没有内边距 */
}

.alert-container {
  padding: 0;
  margin: 0;
}

.alert-box {
  background-color: white;
  color: black;
  border: 1px solid #ccc;
  border-radius: 5px;
  padding: 10px;
  margin: 0; /* 确保没有外边距 */
  display: flex;
  justify-content: space-between; /* 确保按钮在右侧 */
  align-items: center;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  position: relative;
}

.alert-content {
  display: flex;
  align-items: center;
  justify-content: center; /* 确保内容居中 */
  flex-grow: 1;
}

.alert-icon {
  color: red;
  font-size: 2rem; /* 调整感叹号的大小 */
  margin-right: 10px; /* 感叹号与文字之间的间距 */
}

.alert-message {
  text-align: center; /* 文本居中对齐 */
  font-size: 1.2rem;
}

.close-btn {
  background-color: transparent;
  border: 1px solid red;
  border-radius: 15px; /* 椭圆形 */
  color: red;
  cursor: pointer;
  font-size: 1rem;
  padding: 5px 10px;
  margin-left: 10px;
}

.main-content {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  width: 100%;
  margin-top: 20px;
  padding-left: 20px; /* 增加左侧边距,避免图表贴边 */
  box-sizing: border-box; /* 确保内外边距包含在宽度内 */
  position: relative;
}

.graph-container {
  flex: 1;
  max-width: 25%; /* 图表宽度 */
  height: auto; /* 图表高度 */
  padding: 20px;
  margin-left: 2%;
  background-color: white;
  border-radius: 5px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}

.collapsible-panels {
  flex: 1;
  max-width: 25%; /* 折叠面板宽度 */
  padding: 20px;
  background-color: #f9f9f9;
  border-radius: 5px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  margin-right: 5%;
}

.collapsible-panel {
  margin-bottom: 10px;
  width: 100%; /* 确保折叠面板宽度填满容器 */
}

.panel-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 10px;
  background-color: #f1f1f1;
  cursor: pointer;
  border-radius: 5px;
}

.panel-title {
  font-weight: bold;
  font-size: 1.2rem;
}

.panel-money {
  font-weight: bold;
  margin-left: auto;
  font-size: 1.2rem;
}

.panel-content {
  padding: 10px;
  background-color: #ffffff;
  border: 1px solid #f1f1f1;
  border-radius: 0 0 5px 5px;
}

.details-container {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 10px;
}

.detail-item {
  display: flex;
  align-items: center;
}

.detail-item span {
  margin-right: 5px;
}

/* 新增样式 */
.additional-section {
  display: flex;
  justify-content: space-between;
  position: absolute;
  bottom: 20px; /* 调整距离底部的距离 */
  left: 20px; /* 调整距离左侧的距离 */
  width: calc(50% - 40px); /* 根据需求调整宽度 */
}

.data-filter, .data-comparison {
  width: 45%; /* 固定宽度 */
  padding: 20px;
  background-color: #f9f9f9;
  border-radius: 5px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}

.data-filter h2, .data-comparison h2 {
  margin-bottom: 20px;
  text-align: center;
  font-size: 1.5rem;
}

.filter-group, .comparison-group {
  margin-bottom: 10px;
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.filter-btn, .comparison-btn {
  width: 45%;
  padding: 10px;
  background-color: #3498db;
  color: white;
  border: none;
  border-radius: 5px;
  cursor: pointer;
  font-size: 1rem;
}

.filter-select {
  width: 45%;
  padding: 10px;
  border: 1px solid #ccc;
  border-radius: 5px;
  font-size: 1rem;
}

.comparison-btn {
  width: 100%;
  margin-bottom: 10px;
}

.comparison-btn.active {
  background-color: #2980b9;
}
kirin-ri commented 3 months ago
import { BarElement, CategoryScale, Chart as ChartJS, Legend, LinearScale, LineElement, PointElement, ScriptableContext, Title, Tooltip } from 'chart.js';
import { useEffect, useRef, useState } from "react";
import { Chart } from 'react-chartjs-2';

ChartJS.register(CategoryScale, LinearScale, BarElement, LineElement, PointElement, Title, Tooltip, Legend);

const CollapsiblePanel = ({ title, money, details }: { title: string; money: string; details: { month: string, amount: string, alert?: boolean }[] }) => {
  const [isOpen, setIsOpen] = useState(false);

  const togglePanel = () => {
    setIsOpen(!isOpen);
  };

  return (
    <div className="collapsible-panel">
      <div className="panel-header" onClick={togglePanel}>
        <div className="panel-title">{title}</div>
        <div className="panel-money">{money}</div>
      </div>
      {isOpen && (
        <div className="panel-content">
          <div className="details-container">
            {details.map((detail, index) => (
              <div key={index} className="detail-item" style={{ color: detail.alert ? 'red' : 'black' }}>
                <span>{detail.month}</span>
                <span>{detail.alert && <i style={{ color: 'red', marginRight: '5px' }} />}</span>
                <span>{detail.amount}</span>
              </div>
            ))}
          </div>
        </div>
      )}
    </div>
  );
};

const EmptyPage = () => {
  const test = '資金繰り表';
  const [showAlert, setShowAlert] = useState(true); // 控制警告弹窗的显示
  const chartRef = useRef<ChartJS | null>(null);

  useEffect(() => {
    const chart = chartRef.current;
    if (chart) {
      const ctx = chart.ctx;
      if (ctx && chart.data.datasets[2].data) {
        (chart.data.datasets[2].data as number[]).forEach((value, index) => {
          if (value < 0) {
            const meta = chart.getDatasetMeta(2).data[index];
            if (meta) {
              const x = meta.x;
              const y = meta.y;
              ctx.fillStyle = 'red';
              ctx.font = 'bold 12px Arial';
              ctx.fillText('!', x - 5, y - 10);
            }
          }
        });
      }
    }
  }, []);

  const AlertBox = ({ message, onClose }: { message: string; onClose: () => void }) => {
    return (
      <div className="alert-box">
        <div className="alert-content">
          <i className="fa fa-exclamation-circle alert-icon" aria-hidden="true"></i>
          <span className="alert-message">{message}</span>
        </div>
        <button className="close-btn" onClick={onClose}>非表示</button>
      </div>
    );
  };

  const data = {
    labels: ['4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月', '1月', '2月', '3月'],
    datasets: [
      {
        type: 'bar' as const,
        label: '収入',
        data: [20, 30, 25, 15, 30, 25, 20, 30, 25, 30, 20, 25],
        backgroundColor: 'rgba(153, 102, 255, 0.5)',
      },
      {
        type: 'bar' as const,
        label: '支出',
        data: [-25, -35, -30, -20, -25, -30, -35, -25, -30, -20, -25, -30],
        backgroundColor: 'rgba(54, 162, 235, 0.5)',
      },
      {
        type: 'line' as const,
        label: '資金繰り',
        data: [10, -5, -10, 5, 10, -15, 20, -10, 15, 5, -5, 10],
        borderColor: 'black',
        backgroundColor: 'black',
        fill: false,
        tension: 0.1,
        borderWidth: 2,
        pointStyle: 'rectRot',
        pointRadius: 6,
        pointHoverRadius: 8,
        pointBackgroundColor: (context: ScriptableContext<'line'>) => {
          const index = context.dataIndex;
          const value = context.dataset.data[index] as number;
          return value < 0 ? 'red' : 'black';
        }
      }
    ],
  };

  const details = [
    { month: '4月', amount: 'XXXXX円' },
    { month: '5月', amount: 'XXXXX円' },
    { month: '6月', amount: 'XXXXX円' },
    { month: '7月', amount: '▲XXXXX円', alert: true },
    { month: '8月', amount: 'XXXXX円' },
    { month: '9月', amount: 'XXXXX円' },
    { month: '10月', amount: 'XXXXX円' },
    { month: '11月', amount: '▲XXXXX円', alert: true },
    { month: '12月', amount: 'XXXXX円' },
    { month: '1月', amount: 'XXXXX円' },
    { month: '2月', amount: '▲XXXXX円', alert: true },
    { month: '3月', amount: 'XXXXX円' }
  ];

  const options = {
    responsive: true,
    plugins: {
      legend: {
        position: 'top' as const,
        labels: {
          font: {
            size: 16 // 修改图例文字大小
          }
        }
      },
      title: {
        display: true,
        text: '期中資金繰り表',
        font: {
          size: 24 // 修改标题文字大小
        }
      },
      tooltip: {
        callbacks: {
          label: function(context: any) {
            const label = context.dataset.label || '';
            const value = context.raw;
            return `${label}: ${value}`;
          }
        }
      },
    },
    scales: {
      y: {
        beginAtZero: true,
      }
    }
  };

  return (
    <div className="content-wrapper metrics-details">
      <section className="page-cover">
        <div className="page-cover-title-frame">
          <h1>{test}</h1>
        </div>
      </section>
      {/* 警告弹窗 */}
      {showAlert && (
        <div className="alert-container">
          <AlertBox
            message="期中に資金がマイナスとなる期間があります"
            onClose={() => setShowAlert(false)}
          />
        </div>
      )}
      <div className="main-content">
        {/* 左半部分 */}
        <div className="left-section">
          {/* 資金繰り表のグラフ */}
          <div className="graph-container">
            <Chart ref={chartRef} type="bar" data={data} options={options} />
          </div>
          {/* 额外的两个框 */}
          <div className="additional-section">
            <div className="data-filter">
              <h2>データ絞り込み</h2>
              <div className="filter-group">
                <button className="filter-btn">収入</button>
                <select className="filter-select">
                  <option value="1">楽観</option>
                  <option value="2">悲観</option>
                </select>
              </div>
              <div className="filter-group">
                <button className="filter-btn">支出</button>
                <select className="filter-select">
                  <option value="1">楽観</option>
                  <option value="2">悲観</option>
                </select>
              </div>
            </div>
            <div className="data-comparison">
              <h2>データ比較</h2>
              <button className="comparison-btn active">時系列比較</button>
              <button className="comparison-btn">製品別比較</button>
              <button className="comparison-btn">顧客別比較</button>
            </div>
          </div>
        </div>
        {/* 右半部分 */}
        <div className="collapsible-panels">
          <CollapsiblePanel title="営業キャッシュフロー" money="343,564円" details={details}/>
          <CollapsiblePanel title="投資キャッシュフロー" money="435,435円" details={details} />
          <CollapsiblePanel title="財務キャッシュフロー" money="567,232円" details={details} />
        </div>
      </div>
    </div>
  );
};

export default EmptyPage;