在动态图表中实现本地存储- chart js

voase2hg  于 2023-10-18  发布在  Chart.js
关注(0)|答案(2)|浏览(201)

我正试图为我的学校项目制作一个成本图表,衡量项目的计划和实际成本。我得到了本地存储工作在一个正常的条形图感谢另一篇文章在这里,但不能让它工作在这个图表,因为它需要两个数据集。

<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>LS&INPUTS - Colum</title>

    <link rel="stylesheet" href="style.css">

  </head>
  <body>

    <div class="card">
    <p class="chart-heading">Job progress</p>

    <div class="container">
      
      <div class="chart-container">
        <canvas id="myChart"></canvas>
      </div>

        <div class="inputs-container">
          <div class="input-title">
            <p>Job</p>
            <p>Job progress(%)</p>
          </div>
              <!-- Input 1 -->
              <div class="inputfield">
                <input class="labelinput" type="text" id="input1text" onkeyup="updateLabel()" maxlength="10">
                <input class="plannedcost" type="number" id="plannedinput1" onchange ="updatePC()">
                <input class="actualcost" type="number" id="actualinput1" onchange ="updateAC()">
              </div>
              
              <!-- Input 2 -->
              <div class="inputfield">
                <input class="labelinput" type="text" id="input2text" onkeyup="updateLabel()" maxlength="10">
                <input class="plannedcost" type="number" id="plannedinput2" onchange="updatePC()">
                <input class="actualcost" type="number" id="actualinput2" onchange ="updateAC()">
              </div>

              <!-- Input 3 -->
              <div class="inputfield">
                <input class="labelinput" type="text" id="input3text" onkeyup="updateLabel()" maxlength="10">
                <input class="plannedcost" type="number" id="plannedinput3" onchange="updatePC()">
                <input class="actualcost" type="number" id="actualinput3" onchange ="updateAC()">
              </div>

              <!-- Input 3 -->
              <div class="inputfield">
                <input class="labelinput" type="text" id="input4text" onkeyup="updateLabel()" maxlength="10">
                <input class="plannedcost" type="number" id="plannedinput4" onchange="updatePC()">
                <input class="actualcost" type="number" id="actualinput4" onchange ="updateAC()">
              </div>

              <!-- Input 5 -->
              <div class="inputfield">
                <input class="labelinput" type="text" id="input5text" onkeyup="updateLabel()" maxlength="10">
                <input class="plannedcost" type="number" id="plannedinput5" onchange="updatePC()">
                <input class="actualcost" type="number" id="actualinput5" onchange ="updateAC()">
              </div>

            </div>
          </div>
        </div>    

    <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
    <script src="script.js"></script>
  </body>
</html>
let labelData = loadLabelFromLocalStorage();
let plannedCostData = loadPCFromLocalStorage();
let actualCostData = loadACFromLocalStorage();
setUILabel(labelData);
setUIPCData(plannedCostData);
setUIACData(actualCostData);

const ctx = document.getElementById('myChart');
var myChart = new Chart(ctx, {
  type: 'bar',
  data: {
    labels: labelData,
    datasets: [{
      data: actualCostData,
      backgroundColor: "#2E9CCA",
    },{
      data: plannedCostData,
      backgroundColor: "#AAABB8",
    }]
  },
  options: {
    plugins: {
      legend: {
          display: false,
        }
      },

    scales: {
      y: {
        beginAtZero: true,
        min: 0,
      }
    }
  }
});

// add label
function addLabel(chart, data) {
  chart.data.labels.forEach((dataset) => {
    dataset.data = [...data]
  });
  chart.update();
  } 
  
  function updateLabel(event) {
      if (event) event.preventDefault();
      labelData = getUILabel();
      saveLabelToLocalStorage(labelData);
      addLabel(myChart, labelData);
  }
  
  //Updating chart with values
  function setUILabel(data) {
    document.querySelectorAll('.labelinput').forEach((el, i) => el.value = data[i] || '');
  }
  
  function getUILabel() {
    return Array.from(document.querySelectorAll('.labelinput'))
        .map(el => String(el.value));
  }
  
  function saveLabelToLocalStorage(labelData) {
    localStorage.setItem('Label', JSON.stringify(labelData));
  }
  
  function loadLabelFromLocalStorage() {
    return JSON.parse(localStorage.getItem('Label') || '[]');
  }

  ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

 //add "planned cost" data
 function addPC(chart, data) {
  chart.data.datasets.forEach((dataset) => {
      dataset.data = [...data]
  });
  chart.update();
} 

//Calling input values to update chart
function updatePC(event) {
    if (event) event.preventDefault();
    plannedCostData = getUIPCData();
    savePCToLocalStorage(plannedCostData);
    addPC(myChart, plannedCostData);
}

//Updating chart with values
function setUIPCData(data) {
  document.querySelectorAll('.plannedcost').forEach((el, i) => el.value = data[i] || '');
}

function getUIPCData() {
  return Array.from(document.querySelectorAll('.plannedcost'))
      .map(el => Number(el.value));
}

function savePCToLocalStorage(plannedCostData) {
  localStorage.setItem('number2', JSON.stringify(plannedCostData));
}

function loadPCFromLocalStorage() {
  return JSON.parse(localStorage.getItem('number2') || '[]');
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

//add actual cost 
 function addAC(chart, data) {
  chart.data.datasets.forEach((dataset) => {
      dataset.data = [...data]
  });
  chart.update();
} 

//Calling input values to update chart
function updateAC(event) {
    if (event) event.preventDefault();
    actualCostData = getUIACData();
    saveACToLocalStorage(actualCostData);
    addAC(myChart, actualCostData);
}

//Updating chart with values
function setUIACData(data) {
  document.querySelectorAll('.actualcost').forEach((el, i) => el.value = data[i] || '');
}

function getUIACData() {
  return Array.from(document.querySelectorAll('.actualcost'))
      .map(el => Number(el.value));
}

function saveACToLocalStorage(actualCostData) {
  localStorage.setItem('number3', JSON.stringify(actualCostData));
}

function loadACFromLocalStorage() {
  return JSON.parse(localStorage.getItem('number3') || '[]');
}
body {
  background-color:#25274D;
}

.container {
  flex: 0 0 auto;
  width: 100%;
  box-sizing: border-box;
  display: block;
  max-width: 100%;
}

.card {
  width: 57.5rem;
  height: 18rem;
  background-color: white;
  border-radius: 4px;
}

.container {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 1rem;
}

.inputfield {
  display: flex;
  grid-template-columns: 1fr 1fr;
  gap: 0.65rem;
  border-radius: 4px;
  width: 0.5rem;
}

.inputs-container {
  display: grid;
  gap: 0.65rem;
}

.chart-heading {
  text-align: left;
  padding: 1.2rem 0 1.2rem 1rem;
  border-bottom: 1px solid rgba(0, 0, 0, 0.15);
  width: 100%;
}

.input-title {
  display: flex;
  gap: 8rem;
}

.plannedcost {
  width: 3.5rem;
}

.actualcost {
  width: 3.5rem;
}

我尝试了上面的代码,但是当增加计划成本条的值时,相邻的实际成本条的值也会增加。
更改标签可以很好地工作,但是当增加值时,它就不能按预期工作了。我假设这是因为下面的代码用于计划和实际成本,但我不确定如何选择哪个数据集用于每个酒吧。

function addBarData(chart, data) {
  chart.data.datasets.forEach((dataset) => {
    dataset.data = [...data]
  });
  chart.update();
}

更新
现在所有的图表都是单独工作的,但当它们在同一页面上时,它们开始出现故障并共享数据。

let stackedLabelData = loadLabelFromLocalStorage();
let plannedData = loadPlanedFromLocalStorage();
let startedData = loadStartedFromLocalStorage();
let completedData = loadCompletedFromLocalStorage();

setUILabel(stackedLabelData);
setPlannedUIData(plannedData);
setStartedUIData(startedData);
setCompletedUIData(completedData);

let ctx = document.getElementById('myChart');
var myChart = new Chart(ctx, {
  type: 'bar',
  data: {
    labels: stackedLabelData,
    datasets: [{
      data: plannedData,
      backgroundColor: "#AAABB8",
    },{
      data: startedData,
      backgroundColor: "#2E9CCA",
    },{
      data: completedData,
      backgroundColor: "#296484",
    }] 
  },
  options: {
    indexAxis: 'y',
    plugins: {
      legend: {
          display: false,
        }
      },
      scales: {
      x: {
        stacked: true
      },
      y: {
        stacked: true
      }
    },
  }
});

////////////////////////////////////// update labels ///////////////////////////////////

  function addBarLabel(chart, data) {
    chart.data.labels = data;
    chart.update();
  } 
  //Calling input values to update chart
  function updateLabel(event) {
      if (event) event.preventDefault();
      stackedLabelData = getUILabel();
      saveLabelToLocalStorage(stackedLabelData);
      addBarLabel(myChart, stackedLabelData);
  }
  
  //Updating chart with values
  function setUILabel(data) {
    document.querySelectorAll('.Labelinput').forEach((el, i) => el.value = data[i] || '');
  }
  
  function getUILabel() {
    return Array.from(document.querySelectorAll('.Labelinput'))
        .map(el => String(el.value));
  }

  function saveLabelToLocalStorage(stackedLabelData) {
    localStorage.setItem('Label', JSON.stringify(stackedLabelData));
  }

  function loadLabelFromLocalStorage() {
    return JSON.parse(localStorage.getItem('Label') || '[]');
  }

////////////////////////////////////// update planned ///////////////////////////////////

  function addBarData(chart, data, whichData) {
    const idx = {"planned":0,"started":1, "completed":2 }[whichData]
    chart.data.datasets[idx].data = data;
    chart.update();
  } 

  //Calling input values to update chart
  function updatePlanned(event) {
      if (event) event.preventDefault();
      plannedData = getPlannedUIData();
      savePlannedToLocalStorage(plannedData);
      addBarData(myChart, plannedData, "planned");
  }
  
  //Updating chart with values
  function setPlannedUIData(data) {
    document.querySelectorAll('.planned').forEach((el, i) => el.value = data[i] || '');
  }
  
  function getPlannedUIData() {
    return Array.from(document.querySelectorAll('.planned'))
        .map(el => Number(el.value));
  }

  function savePlannedToLocalStorage(plannedData) {
    localStorage.setItem('plannedjobs', JSON.stringify(plannedData));
  }

  function loadPlanedFromLocalStorage() {
    return JSON.parse(localStorage.getItem('plannedjobs') || '[]');
  }
  
////////////////////////////////////// update started ///////////////////////////////////

  //Calling input values to update chart
  function updateStarted(event) {
      if (event) event.preventDefault();
      startedData = getStartedUIData();
      saveStartedToLocalStorage(startedData);
      addBarData(myChart, startedData, "started");
  }
  
  //Updating chart with values
  function setStartedUIData(data) {
    document.querySelectorAll('.started').forEach((el, i) => el.value = data[i] || '');
  }
  
  function getStartedUIData() {
    return Array.from(document.querySelectorAll('.started'))
        .map(el => Number(el.value));
  }

  function saveStartedToLocalStorage(startedData) {
    localStorage.setItem('startedjobs', JSON.stringify(startedData));
  }

  function loadStartedFromLocalStorage() {
    return JSON.parse(localStorage.getItem('startedjobs') || '[]');
  }

////////////////////////////////////// update completed //////////////////////////////////
  //Calling input values to update chart
  function updateCompleted(event) {
      if (event) event.preventDefault();
      completedData = getCompletedUIData();
      saveCompletedFromLocalStorage(completedData);
      addBarData(myChart, completedData, "completed");
  }
  
  //Updating chart with values
  function setCompletedUIData(data) {
    document.querySelectorAll('.completed').forEach((el, i) => el.value = data[i] || '');
  }
  
  function getCompletedUIData() {
    return Array.from(document.querySelectorAll('.completed'))
        .map(el => Number(el.value));
  }

  function saveCompletedFromLocalStorage(completedData) {
    localStorage.setItem('completedjobs', JSON.stringify(completedData));
  }

  function loadCompletedFromLocalStorage() {
    return JSON.parse(localStorage.getItem('completedjobs') || '[]');
  }
  
  
  

  ///////////////////////////////////Second file////////////////////////////////////////
  
  let labelData = loadLabelFromLocalStorage();
let plannedCostData = loadPCFromLocalStorage();
let actualCostData = loadACFromLocalStorage();

setUILabel(labelData);
setUIPCData(plannedCostData);
setUIACData(actualCostData);

ctx = document.getElementById('column');
var myChart = new Chart(ctx, {
  type: 'bar',
  data: {
    labels: labelData,
    datasets: [{
      data: actualCostData,
      backgroundColor: "#2E9CCA",
    },{
      data: plannedCostData,
      backgroundColor: "#AAABB8",
    }]
  },
  options: {
    plugins: {
      legend: {
          display: false,
        }
      },

    scales: {
      y: {
        beginAtZero: true,
        min: 0,
      }
    }
  }
});

function addBarLabel(chart, data) {
  chart.data.labels = data;
  chart.update();
} 
  
  function updateLabel(event) {
      if (event) event.preventDefault();
      labelData = getUILabel();
      saveLabelToLocalStorage(labelData);
      addBarLabel(myChart, labelData);
  }
  
  //Updating chart with values
  function setUILabel(data) {
    document.querySelectorAll('.labelinput').forEach((el, i) => el.value = data[i] || '');
  }
  
  function getUILabel() {
    return Array.from(document.querySelectorAll('.labelinput'))
        .map(el => String(el.value));
  }
  
  function saveLabelToLocalStorage(labelData) {
    localStorage.setItem('Label', JSON.stringify(labelData));
  }
  
  function loadLabelFromLocalStorage() {
    return JSON.parse(localStorage.getItem('Label') || '[]');
  }

  ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

function addBarData(chart, data, whichData) {
  const idx = whichData === "actual" ? 0:1;
  chart.data.datasets[idx].data = data;
  chart.update();
} 
  

//Calling input values to update chart
function updatePC(event) {
  if (event) event.preventDefault();
  plannedCostData = getUIPCData();
  savePCToLocalStorage(plannedCostData);
  addBarData(myChart, plannedCostData, "planned");
}

//Updating chart with values
function setUIPCData(data) {
  document.querySelectorAll('.plannedcost').forEach((el, i) => el.value = data[i] || '');
}

function getUIPCData() {
  return Array.from(document.querySelectorAll('.plannedcost'))
      .map(el => Number(el.value));
}

function savePCToLocalStorage(plannedCostData) {
  localStorage.setItem('number2', JSON.stringify(plannedCostData));
}

function loadPCFromLocalStorage() {
  return JSON.parse(localStorage.getItem('number2') || '[]');
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

//Calling input values to update chart
function updateAC(event) {
  if (event) event.preventDefault();
  actualCostData = getUIACData();
  saveACToLocalStorage(actualCostData);
  addBarData(myChart, actualCostData, "actual");
}

//Updating chart with values
function setUIACData(data) {
  document.querySelectorAll('.actualcost').forEach((el, i) => el.value = data[i] || '');
}

function getUIACData() {
  return Array.from(document.querySelectorAll('.actualcost'))
      .map(el => Number(el.value));
}

function saveACToLocalStorage(actualCostData) {
  localStorage.setItem('number3', JSON.stringify(actualCostData));
}

function loadACFromLocalStorage() {
  return JSON.parse(localStorage.getItem('number3') || '[]');
}
body {
  background-color:#25274D;
}

.container {
  flex: 0 0 auto;
  width: 100%;
  box-sizing: border-box;
  display: block;
  max-width: 100%;
}

.card {
  width: 57.5rem;
  height: 18rem;
  background-color: white;
  border-radius: 4px;
}

.container {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 1rem;
}

.inputfield {
  display: flex;
  grid-template-columns: 1fr 1fr;
  gap: 0.65rem;
  border-radius: 4px;
  width: 0.5rem;
}

.inputs-container {
  display: grid;
  gap: 0.65rem;
}

.chart-heading {
  text-align: left;
  padding: 1.2rem 0 1.2rem 1rem;
  border-bottom: 1px solid rgba(0, 0, 0, 0.15);
  width: 100%;
}

.input-title {
  display: flex;
  gap: 8rem;
}

.planned {
  width: 3.5rem;
}

.started {
  width: 3.5rem;
}

.completed {
  width: 3.5rem;
}
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>WORKING INPUTS - Stacked bar</title>

    <link rel="stylesheet" href="style.css">

  </head>
  <body>

    <div class="card">
    <p class="chart-heading">Job progress</p>

    <div class="container">
      
      <div class="chart-container">
        <canvas id="myChart"></canvas>
      </div>

        <div class="inputs-container">
          <div class="input-title">
            <p>Job</p>
            <p>Job progress(%)</p>
          </div>
              <!-- Input 1 -->
              <div class="inputfield">
                <input class="Labelinput" type="text" onkeyup="updateLabel()" value="Client 1" maxlength="10">
                <input class="planned" type="number" onchange ="updatePlanned()">
                <input class="started" type="number" onchange ="updateStarted()">
                <input class="completed" type="number" onchange ="updateCompleted()">
              </div>
              
              <!-- Input 2 -->
              <div class="inputfield">
                <input class="Labelinput" type="text" id="input2text" onkeyup="updateLabel()" value="Client 2" maxlength="10">
                <input class="planned" type="number" id="plannedinput2" onchange ="updatePlanned()">
                <input class="started" type="number" id="startedinput2" onchange ="updateStarted()">
                <input class="completed" type="number" id="completedinput2" onchange ="updateCompleted()">
              </div>

              <!-- Input 3 -->
              <div class="inputfield">
                <input class="Labelinput" type="text" id="input3text" onkeyup="updateLabel()" value="Client 3" maxlength="10">
                <input class="planned" type="number" id="plannedinput3" onchange ="updatePlanned()">
                <input class="started" type="number" id="startedinput3" onchange ="updateStarted()">
                <input class="completed" type="number" id="completedinput3" onchange ="updateCompleted()">
              </div>

              <!-- Input 3 -->
              <div class="inputfield">
                <input class="Labelinput" type="text" id="input4text" onkeyup="updateLabel()" value="Client 4" maxlength="10">
                <input class="planned" type="number" id="plannedinput4" onchange ="updatePlanned()">
                <input class="started" type="number" id="startedinput4" onchange ="updateStarted()">
                <input class="completed" type="number" id="completedinput4" onchange ="updateCompleted()">
              </div>

              <!-- Input 5 -->
              <div class="inputfield">
                <input class="Labelinput" type="text" id="input5text" onkeyup="updateLabel()" value="Client 5" maxlength="10">
                <input class="planned" type="number" id="plannedinput5" onchange ="updatePlanned()">
                <input class="started" type="number" id="startedinput5" onchange ="updateStarted()">
                <input class="completed" type="number" id="completedinput5" onchange ="updateCompleted()">
              </div>

            </div>
          </div>
        </div>    

        <div class="card">
          <p class="chart-heading">Job progress</p>
      
          <div class="container">
            
            <div class="chart-container">
              <canvas id="column"></canvas>
            </div>
      
              <div class="inputs-container">
                <div class="input-title">
                  <p>Job</p>
                  <p>Job progress(%)</p>
                </div>
                    <!-- Input 1 -->
                    <div class="inputfield">
                      <input class="labelinput" type="text" id="input1text" onkeyup="updateLabel()" maxlength="10">
                      <input class="plannedcost" type="number" id="plannedinput1" oninput ="updatePC()">
                      <input class="actualcost" type="number" id="actualinput1" oninput ="updateAC()">
                    </div>
                    
                    <!-- Input 2 -->
                    <div class="inputfield">
                      <input class="labelinput" type="text" id="input2text" onkeyup="updateLabel()" maxlength="10">
                      <input class="plannedcost" type="number" id="plannedinput2" onchange="updatePC()">
                      <input class="actualcost" type="number" id="actualinput2" onchange ="updateAC()">
                    </div>
      
                    <!-- Input 3 -->
                    <div class="inputfield">
                      <input class="labelinput" type="text" id="input3text" onkeyup="updateLabel()" maxlength="10">
                      <input class="plannedcost" type="number" id="plannedinput3" onchange="updatePC()">
                      <input class="actualcost" type="number" id="actualinput3" onchange ="updateAC()">
                    </div>
      
                    <!-- Input 3 -->
                    <div class="inputfield">
                      <input class="labelinput" type="text" id="input4text" onkeyup="updateLabel()" maxlength="10">
                      <input class="plannedcost" type="number" id="plannedinput4" onchange="updatePC()">
                      <input class="actualcost" type="number" id="actualinput4" onchange ="updateAC()">
                    </div>
      
                    <!-- Input 5 -->
                    <div class="inputfield">
                      <input class="labelinput" type="text" id="input5text" onkeyup="updateLabel()" maxlength="10">
                      <input class="plannedcost" type="number" id="plannedinput5" onchange="updatePC()">
                      <input class="actualcost" type="number" id="actualinput5" onchange ="updateAC()">
                    </div>
      
                  </div>
                </div>
              </div>  
    <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
    <script src="stackedbar.js"></script>
    <script src="column.js"></script>

  </body>
</html>
kxe2p93d

kxe2p93d1#

更新
请考虑更DRY(不要重复自己),例如像这个(不完整)的例子

// Configurations
const configs = {
  label: {
    localStorageKey: 'Label',
    querySelector: '.Labelinput',
    mapper: String,
  },
  planned: {
    localStorageKey: 'plannedjobs',
    querySelector: '.planned',
    mapper: Number,
  },
  started: {
    localStorageKey: 'startedjobs',
    querySelector: '.started',
    mapper: Number,
  },
  completed: {
    localStorageKey: 'completedjobs',
    querySelector: '.completed',
    mapper: Number,
  },
};

function loadFromLocalStorage(key) {
  return JSON.parse(localStorage.getItem(key) || '[]');
}

function saveToLocalStorage(key, data) {
  localStorage.setItem(key, JSON.stringify(data));
}

function getUIData(querySelector, mapper) {
  return Array.from(document.querySelectorAll(querySelector)).map(el => mapper(el.value));
}

function setUIData(data, querySelector) {
  document.querySelectorAll(querySelector).forEach((el, i) => el.value = data[i] || '');
}

function addBarData(chart, data, whichData) {
  const idxMap = {
    "planned": 0,
    "started": 1,
    "completed": 2
  };
  chart.data.datasets[idxMap[whichData]].data = data;
  chart.update();
}

function update(event, type, chart) {
  if (event) event.preventDefault();
  const config = configs[type];
  const data = getUIData(config.querySelector, config.mapper);
  saveToLocalStorage(config.localStorageKey, data);
  addBarData(chart, data, type);
}

// Main script flow
const dataKeys = ['label', 'planned', 'started', 'completed'];
const data = dataKeys.reduce((acc, key) => {
  acc[key] = loadFromLocalStorage(configs[key].localStorageKey);
  setUIData(acc[key], configs[key].querySelector);
  return acc;
}, {});

let ctx = document.getElementById('myChart');
var myChart = new Chart(ctx, {
  // ... (unchanged chart configuration)
});

// Example of how to use the update function for "planned":
// function onSomeEventForPlanned(event) {
//   update(event, 'planned', myChart);
// }
rn0zuynd

rn0zuynd2#

我预计

function addBarData(chart, actualCostData, plannedCostData) {
  chart.data.datasets = [{ 
    data: actualCostData,
    backgroundColor: "#2E9CCA",
  },{
    data: plannedCostData,
    backgroundColor: "#AAABB8",
  }];
  chart.update();
}

更好地工作
如果你一定要试试这个

function addBarData(chart, data, whichData) {
  const idx = whichData === "actual" ? 0:1;
  chart.data.datasets[idx].data = data;
  chart.update();
} 

and 

function updateAC(event) {
  if (event) event.preventDefault();
  actualCostData = getUIACData();
  saveACToLocalStorage(actualCostData);
  addBarData(myChart, actualCostData, "actual");
}

function updatePC(event) {
  if (event) event.preventDefault();
  plannedCostData = getUIPCData();
  savePCToLocalStorage(plannedCostData);
  addBarData(myChart, plannedCostData, "planned");
}

相关问题