최초 커밋
This commit is contained in:
@@ -0,0 +1,275 @@
|
||||
<section class="card individual">
|
||||
<div class="chart-wrap">
|
||||
<div class="card-header">
|
||||
<h3>연간 개인별 판매 실적<span class="tail"></span></h3>
|
||||
<div class="legend">
|
||||
<span>
|
||||
<span class="legend-dot line-area01"></span>
|
||||
권혁진 수석
|
||||
<span class="badge-individual01"></span>
|
||||
</span>
|
||||
<span>
|
||||
<span class="legend-dot line-area02"></span>
|
||||
염승호 수석
|
||||
<span class="badge-individual02"></span>
|
||||
</span>
|
||||
<span class="unit">(단위:copy) </span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="chart-body">
|
||||
<!-- <div class="badge-box">
|
||||
|
||||
|
||||
</div> -->
|
||||
<canvas id="individual_chart"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="person-box">
|
||||
<div class="person-name"><span>권혁진</span> <small>수석</small></div>
|
||||
<div class="stat-row individual01">
|
||||
<div class="label-box">
|
||||
<span class="label">누적 판매수</span>
|
||||
<!-- <div class="change-down">전월대비 <em>27</em></div> -->
|
||||
</div>
|
||||
<div>
|
||||
<span class="big-num">162</span><span class="unit copy">copy</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat-row">
|
||||
<div class="label-box">
|
||||
<span class="label">목표 달성률</span>
|
||||
<!-- <div class="change-up">전월대비 <em>15p</em></div> -->
|
||||
</div>
|
||||
<div><span class="big-num">81</span><span class="unit">%</span></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="person-box">
|
||||
<div class="person-name">
|
||||
<span>염승호</span>
|
||||
<small>수석</small>
|
||||
</div>
|
||||
<div class="stat-row individual02">
|
||||
<div class="label-box">
|
||||
<span class="label">누적 판매수</span>
|
||||
<!-- <div class="change-up">전월대비 <em>105</em></div> -->
|
||||
</div>
|
||||
<div>
|
||||
<span class="big-num">508</span><span class="unit copy">copy</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat-row">
|
||||
<div class="label-box">
|
||||
<span class="label">목표 달성률</span>
|
||||
<!-- <div class="change-up">전월대비 <em>63p</em></div> -->
|
||||
</div>
|
||||
<div><span class="big-num">169.3</span><span class="unit">%</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<script>
|
||||
(() => {
|
||||
|
||||
let INDIVIDUAL_DATA = null;
|
||||
|
||||
// -----------------------------
|
||||
// 1) UI 숫자 반영
|
||||
// -----------------------------
|
||||
function updateIndividualUI() {
|
||||
if (!INDIVIDUAL_DATA || INDIVIDUAL_DATA.length < 2) return;
|
||||
|
||||
const p1 = INDIVIDUAL_DATA[0];
|
||||
const p2 = INDIVIDUAL_DATA[1];
|
||||
|
||||
// 이름
|
||||
document.querySelector('.person-box:nth-child(1) .person-name span').textContent = p1.name;
|
||||
document.querySelector('.person-box:nth-child(2) .person-name span').textContent = p2.name;
|
||||
|
||||
// 누적 판매수
|
||||
document.querySelector('.individual01 .big-num').textContent = p1.total;
|
||||
document.querySelector('.individual02 .big-num').textContent = p2.total;
|
||||
|
||||
// 달성률
|
||||
document.querySelector('.person-box:nth-child(1) .stat-row:nth-child(3) .big-num').textContent = p1.rate;
|
||||
document.querySelector('.person-box:nth-child(2) .stat-row:nth-child(3) .big-num').textContent = p2.rate;
|
||||
|
||||
// 개인별 목표(배지)
|
||||
document.querySelector('.badge-individual01').textContent = `목표 ${p1.target_qty}`;
|
||||
document.querySelector('.badge-individual02').textContent = `목표 ${p2.target_qty}`;
|
||||
}
|
||||
|
||||
// -----------------------------
|
||||
// 2) 차트 생성
|
||||
// -----------------------------
|
||||
const labels12 = Array.from({ length: 12 }, (_, i) => String(i + 1));
|
||||
|
||||
const currentMonth = new Date().getMonth() + 1;
|
||||
const currentMonthStr = String(currentMonth);
|
||||
|
||||
let individualInstance = null;
|
||||
|
||||
function createIndividualChart() {
|
||||
const ctx = document.getElementById("individual_chart");
|
||||
if (!ctx || !INDIVIDUAL_DATA || INDIVIDUAL_DATA.length < 2) return;
|
||||
|
||||
if (individualInstance) individualInstance.destroy();
|
||||
|
||||
individualInstance = new Chart(ctx, {
|
||||
type: "line",
|
||||
data: {
|
||||
labels: labels12,
|
||||
datasets: [
|
||||
{
|
||||
label: "권혁진 수석",
|
||||
data: [...INDIVIDUAL_DATA[0].monthly, null],
|
||||
fill: true,
|
||||
backgroundColor: (ctx) => {
|
||||
const a = ctx.chart.chartArea;
|
||||
return !a
|
||||
? cssVar("--bg-chart-area01")
|
||||
: safeGrad(
|
||||
ctx.chart.ctx,
|
||||
a,
|
||||
[
|
||||
{ stop: 0, color: cssVar("--bg-chart-area01") },
|
||||
{ stop: 1, color: "rgba(255, 255, 255, 0.3)" },
|
||||
],
|
||||
true
|
||||
);
|
||||
},
|
||||
borderColor: cssVar("--bg-chart-linearea01"),
|
||||
borderWidth: 1,
|
||||
pointRadius: 4,
|
||||
tension: 0,
|
||||
},
|
||||
{
|
||||
label: "염승호 수석",
|
||||
data: [...INDIVIDUAL_DATA[1].monthly, null],
|
||||
fill: true,
|
||||
backgroundColor: (ctx) => {
|
||||
const a = ctx.chart.chartArea;
|
||||
return !a
|
||||
? cssVar("--bg-chart-area02")
|
||||
: safeGrad(
|
||||
ctx.chart.ctx,
|
||||
a,
|
||||
[
|
||||
{ stop: 0, color: cssVar("--bg-chart-area02") },
|
||||
{ stop: 1, color: "rgba(255, 255, 255, 0.3)" },
|
||||
],
|
||||
true
|
||||
);
|
||||
},
|
||||
borderColor: cssVar("--bg-chart-linearea02"),
|
||||
borderWidth: 1,
|
||||
pointRadius: 4,
|
||||
tension: 0,
|
||||
},
|
||||
],
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
layout: { padding: { top: 16 } },
|
||||
plugins: {
|
||||
legend: { display: false },
|
||||
tooltip: { enabled: false },
|
||||
},
|
||||
scales: {
|
||||
y: {
|
||||
afterBuildTicks: (scale) => {
|
||||
const maxValue = scale.chart.data.datasets
|
||||
.flatMap((d) => d.data)
|
||||
.filter((n) => typeof n === "number");
|
||||
|
||||
const realMax = Math.max(...maxValue);
|
||||
const rawMax = realMax + 50;
|
||||
const targetMax = Math.ceil(rawMax / 100) * 100;
|
||||
const mid = Math.round(targetMax / 2);
|
||||
|
||||
scale.max = targetMax;
|
||||
scale.min = 0;
|
||||
scale.ticks = [
|
||||
{ value: 0 },
|
||||
{ value: mid },
|
||||
{ value: targetMax },
|
||||
];
|
||||
scale._midIndex = 1;
|
||||
},
|
||||
grid: {
|
||||
drawTicks: false,
|
||||
color: (ctx) =>
|
||||
ctx.index === ctx.scale._midIndex
|
||||
? cssVar("--grid-border-dashed")
|
||||
: "rgba(0,0,0,0)",
|
||||
lineWidth: (ctx) =>
|
||||
ctx.index === ctx.scale._midIndex ? 1 : 0,
|
||||
},
|
||||
border: { display: false },
|
||||
ticks: {
|
||||
padding: 4,
|
||||
color: cssVar("--chart-base"),
|
||||
},
|
||||
},
|
||||
x: {
|
||||
offset: true,
|
||||
grid: {
|
||||
drawTicks: false,
|
||||
display: true,
|
||||
color: cssVar("--grid-border") || "#e0e0e0",
|
||||
drawOnChartArea: true,
|
||||
z: -1,
|
||||
},
|
||||
ticks: {
|
||||
color: (ctx) =>
|
||||
ctx.tick.label === currentMonthStr
|
||||
? cssVar("--chart-point")
|
||||
: cssVar("--chart-base"),
|
||||
font: (ctx) =>
|
||||
ctx.tick.label === currentMonthStr
|
||||
? { weight: "bold", size: 18 }
|
||||
: { weight: "normal", size: 18 },
|
||||
padding: 10,
|
||||
callback: (_, index) => labels12[index],
|
||||
},
|
||||
border: {
|
||||
display: true,
|
||||
dash: [3, 3],
|
||||
color: cssVar("--grid-border-dashed"),
|
||||
width: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: [
|
||||
highlightXFactory(currentMonthStr),
|
||||
hideNullPointsPlugin,
|
||||
createPointGradientPluginWithBorder(),
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
// -----------------------------
|
||||
// 3) 개인별 데이터 로딩 함수 (🔥 핵심)
|
||||
// -----------------------------
|
||||
function loadIndividualDashboardData() {
|
||||
return fetch(`/egbim/bbs/sales_dashboard.php?year=${window.APP_YEAR}&_=${Date.now()}`)
|
||||
.then(r => r.json())
|
||||
.then(json => {
|
||||
INDIVIDUAL_DATA = json.individual || [];
|
||||
updateIndividualUI();
|
||||
createIndividualChart();
|
||||
});
|
||||
}
|
||||
|
||||
// -----------------------------
|
||||
// 4) 전역 등록 + 최초 실행
|
||||
// -----------------------------
|
||||
window.loadIndividualDashboardData = loadIndividualDashboardData;
|
||||
loadIndividualDashboardData();
|
||||
|
||||
})();
|
||||
</script>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user