260 lines
7.7 KiB
Vue
260 lines
7.7 KiB
Vue
![]() |
<template>
|
|||
|
<div ref="chart" style="width: 100%; height: 400px"></div>
|
|||
|
</template>
|
|||
|
|
|||
|
<script>
|
|||
|
import * as echarts from "echarts";
|
|||
|
import entityApi from "~/apis/entityApi.js";
|
|||
|
import { mapState } from "pinia";
|
|||
|
import { useEntityStore } from "~/stores/entityStore";
|
|||
|
|
|||
|
/**
|
|||
|
* @vue-prop {Object|null} [selectedItem=null] - آیتم انتخاب شده
|
|||
|
* @vue-prop {String} [title="تغییرات زمانی"] - عنوان
|
|||
|
* @vue-prop {String} [time_unit="day"] - واحد زمانی
|
|||
|
* @vue-data {String} [gantt_time_unit="day"] - واحد زمانی گانت
|
|||
|
* @vue-data {Array} [gantt_data=[]] - دادههای گانت
|
|||
|
* @vue-data {Object|null} [httpService=null] - سرویس HTTP
|
|||
|
* @vue-data {Number} [x_min=0] - حداقل مقدار محور x
|
|||
|
* @vue-data {Number} [x_max=10] - حداکثر مقدار محور x
|
|||
|
* @vue-data {Object|undefined} [entity=undefined] - موجودیت
|
|||
|
*
|
|||
|
* * @vue-computed {Function} [mapState.activeEntityViewSchemaGetter] - دریافتکننده طرح نمای موجودیت فعال
|
|||
|
*/
|
|||
|
|
|||
|
export default {
|
|||
|
props: {
|
|||
|
selectedItem: {
|
|||
|
default: null,
|
|||
|
},
|
|||
|
title: {
|
|||
|
default: "تغییرات زمانی",
|
|||
|
},
|
|||
|
time_unit: {
|
|||
|
default: "day",
|
|||
|
},
|
|||
|
},
|
|||
|
data() {
|
|||
|
return {
|
|||
|
gantt_time_unit: "day",
|
|||
|
gantt_data: [],
|
|||
|
x_min: 0,
|
|||
|
x_max: 10,
|
|||
|
entity: undefined,
|
|||
|
};
|
|||
|
},
|
|||
|
computed: {
|
|||
|
...mapState(useEntityStore, ["activeEntityViewSchemaGetter"]),
|
|||
|
},
|
|||
|
mounted() {
|
|||
|
if (this.selectedItem) {
|
|||
|
this.entity = this.selectedItem;
|
|||
|
this.gantt_time_unit = "month";
|
|||
|
this.getGanttData();
|
|||
|
} else {
|
|||
|
this.gantt_title = this.title;
|
|||
|
this.gantt_time_unit = this.time_unit;
|
|||
|
this.gantt_data = this.data;
|
|||
|
// this.setChartOptions(this.gantt_time_unit);
|
|||
|
}
|
|||
|
},
|
|||
|
watch: {
|
|||
|
selectedItem: {
|
|||
|
handler(newVal) {
|
|||
|
if (newVal) {
|
|||
|
this.getGanttData();
|
|||
|
}
|
|||
|
},
|
|||
|
},
|
|||
|
},
|
|||
|
methods: {
|
|||
|
/**
|
|||
|
* دریافت دادههای نمودار گانت.
|
|||
|
* این متد با ارسال درخواست به سرور، دادههای نمودار گانت را دریافت کرده و سپس نمودار را با استفاده از دادههای دریافتی رندر میکند.
|
|||
|
*/
|
|||
|
async getGanttData() {
|
|||
|
if (!this.selectedItem) {
|
|||
|
this.renderChart();
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
let payload = {
|
|||
|
id: this.selectedItem._id,
|
|||
|
};
|
|||
|
|
|||
|
let url = entityApi.chart.ganttchart;
|
|||
|
let key = this.activeEntityViewSchemaGetter?.key;
|
|||
|
url = url.replace("{{index_key}}", key);
|
|||
|
if (key === "qasection" || key === "rgsection") {
|
|||
|
payload.qanon_id = this.selectedItem.qanon_id;
|
|||
|
}
|
|||
|
|
|||
|
try {
|
|||
|
const { $api } = useNuxtApp();
|
|||
|
const res = await $api(url, {
|
|||
|
method: "post",
|
|||
|
baseURL: baseUrl(),
|
|||
|
body: payload,
|
|||
|
});
|
|||
|
|
|||
|
this.x_min = res.meta.min;
|
|||
|
this.x_max = res.meta.max;
|
|||
|
this.gantt_data = res.data[0].data.map((item) => ({
|
|||
|
...item,
|
|||
|
start: new Date(item.start),
|
|||
|
end: new Date(item.end),
|
|||
|
}));
|
|||
|
} catch (err) {
|
|||
|
} finally {
|
|||
|
this.renderChart();
|
|||
|
}
|
|||
|
|
|||
|
// this.httpService
|
|||
|
// .postRequest(url, payload)
|
|||
|
// .then((res) => {
|
|||
|
// this.x_min = res.meta.min;
|
|||
|
// this.x_max = res.meta.max;
|
|||
|
// this.gantt_data = res.data[0].data.map((item) => ({
|
|||
|
// ...item,
|
|||
|
// start: new Date(item.start),
|
|||
|
// end: new Date(item.end),
|
|||
|
// }));
|
|||
|
// })
|
|||
|
// .finally(() => {
|
|||
|
// this.renderChart();
|
|||
|
// })
|
|||
|
// .catch((error) => {
|
|||
|
// console.error("Error fetching Gantt data:", error);
|
|||
|
// });
|
|||
|
},
|
|||
|
|
|||
|
/**
|
|||
|
* رندر کردن نمودار.
|
|||
|
* این متد با استفاده از دادههای نمودار گانت، نمودار را در DOM رندر میکند.
|
|||
|
*/
|
|||
|
renderChart() {
|
|||
|
const chartDom = this.$refs.chart;
|
|||
|
const myChart = echarts.init(chartDom);
|
|||
|
const categories = this.gantt_data.map((item) => item.name);
|
|||
|
|
|||
|
myChart.setOption({
|
|||
|
title: {
|
|||
|
text: this.title,
|
|||
|
},
|
|||
|
tooltip: {
|
|||
|
trigger: "axis",
|
|||
|
axisPointer: {
|
|||
|
type: "shadow",
|
|||
|
},
|
|||
|
},
|
|||
|
grid: {
|
|||
|
left: "3%",
|
|||
|
right: "4%",
|
|||
|
bottom: "3%",
|
|||
|
containLabel: true,
|
|||
|
},
|
|||
|
xAxis: {
|
|||
|
type: "time",
|
|||
|
},
|
|||
|
yAxis: {
|
|||
|
type: "category",
|
|||
|
data: categories,
|
|||
|
},
|
|||
|
series: [
|
|||
|
{
|
|||
|
name: "Gantt",
|
|||
|
type: "custom",
|
|||
|
renderItem: function (params, api) {
|
|||
|
var categoryIndex = api.value(1);
|
|||
|
var start = api.coord([api.value(0), categoryIndex]);
|
|||
|
var end = api.coord([api.value(2), categoryIndex]);
|
|||
|
var height = api.size([0, 1])[1] * 0.6;
|
|||
|
|
|||
|
return {
|
|||
|
type: "rect",
|
|||
|
shape: echarts.graphic.clipRectByRect(
|
|||
|
{
|
|||
|
x: start[0],
|
|||
|
y: start[1] - height / 2,
|
|||
|
width: end[0] - start[0],
|
|||
|
height: height,
|
|||
|
},
|
|||
|
{
|
|||
|
x: params.coordSys.x,
|
|||
|
y: params.coordSys.y,
|
|||
|
width: params.coordSys.width,
|
|||
|
height: params.coordSys.height,
|
|||
|
}
|
|||
|
),
|
|||
|
style: {
|
|||
|
fill: this.getRandomColor(),
|
|||
|
transition: "fill 1s cubicInOut",
|
|||
|
},
|
|||
|
};
|
|||
|
}.bind(this),
|
|||
|
data: this.gantt_data.map((item) => {
|
|||
|
return [
|
|||
|
item.start.getTime(),
|
|||
|
categories.indexOf(item.name),
|
|||
|
item.end.getTime(),
|
|||
|
];
|
|||
|
}),
|
|||
|
animationDurationUpdate: 1000,
|
|||
|
animationEasingUpdate: "cubicInOut",
|
|||
|
},
|
|||
|
],
|
|||
|
});
|
|||
|
},
|
|||
|
|
|||
|
/**
|
|||
|
* دریافت یک رنگ تصادفی.
|
|||
|
* این متد یک رنگ تصادفی از یک لیست از پیش تعیین شده برمیگرداند.
|
|||
|
*
|
|||
|
* @returns {string} - یک رشته حاوی کد رنگ به فرمت rgb
|
|||
|
*/
|
|||
|
getRandomColor() {
|
|||
|
const colors = [
|
|||
|
"rgb(255, 99, 71)", // Tomato
|
|||
|
"rgb(255, 140, 0)", // Dark Orange
|
|||
|
"rgb(34, 139, 34)", // Forest Green
|
|||
|
"rgb(30, 144, 255)", // Dodger Blue
|
|||
|
"rgb(138, 43, 226)", // Blue Violet
|
|||
|
"rgb(255, 20, 147)", // Deep Pink
|
|||
|
"rgb(0, 191, 255)", // Deep Sky Blue
|
|||
|
"rgb(218, 165, 32)", // Goldenrod
|
|||
|
"rgb(220, 20, 60)", // Crimson
|
|||
|
"rgb(75, 0, 130)", // Indigo
|
|||
|
];
|
|||
|
|
|||
|
const randomIndex = Math.floor(Math.random() * colors.length);
|
|||
|
return colors[randomIndex];
|
|||
|
},
|
|||
|
|
|||
|
/**
|
|||
|
* دریافت فرمت محور X.
|
|||
|
* این متد فرمت تاریخ و زمان محور X را بر اساس واحد زمانی گانت تعیین میکند.
|
|||
|
*
|
|||
|
* @returns {object} - یک شیء حاوی فرمت تاریخ و زمان
|
|||
|
*/
|
|||
|
getXAxisFormat() {
|
|||
|
let format = {
|
|||
|
day: '%e<br><span style="opacity: 0.5; font-size: 1em">%a</span>',
|
|||
|
};
|
|||
|
if (this.gantt_time_unit == "day")
|
|||
|
format = {
|
|||
|
day: '%e<br><span style="opacity: 0.5; font-size: 1em">%a</span>',
|
|||
|
};
|
|||
|
else if (this.gantt_time_unit == "week") format = { week: "%e. %b" };
|
|||
|
else if (this.gantt_time_unit == "month") format = { month: "%b '%y" };
|
|||
|
else if (this.gantt_time_unit == "year") format = { year: "%Y" };
|
|||
|
|
|||
|
return format;
|
|||
|
},
|
|||
|
},
|
|||
|
};
|
|||
|
</script>
|
|||
|
|
|||
|
<style>
|
|||
|
/* افزودن هرگونه سبک مورد نیاز */
|
|||
|
</style>
|