Open kirin-ri opened 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;
.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;
}
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;
.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;
}
.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;
}
.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;
}
.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;
}
.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;
}
.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;
}
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;
.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;
}
.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;
}
.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;
}
.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: 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; }
.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;
}
.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;
}
.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;
}
.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;
}
.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;
}
.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;
}
.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;
}
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;
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 |
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;
.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;
}
<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 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円' }
];
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;
.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;
}
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円' }
];
style={{ color: detail.alert ? 'red' : 'black' }}>
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;
.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;
}
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;
.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;
}
.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;
}
.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;
}
.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;
}
.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;
}
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;
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