userDoughnut.vue 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. <template>
  2. <div
  3. class="db--chart"
  4. >
  5. <Doughnut
  6. v-if="props.maxSubscriber > 0"
  7. :data="myData"
  8. :options="myOptions"
  9. />
  10. </div>
  11. </template>
  12. <script setup>
  13. /***********************
  14. * import
  15. ************************/
  16. import { Doughnut } from "vue-chartjs"
  17. import { Chart as ChartJS, Title, Tooltip, Legend, BarElement, LineController, LineElement, PointElement, ArcElement, CategoryScale, LinearScale } from 'chart.js';
  18. /***********************
  19. * plugins inject
  20. ************************/
  21. // props
  22. const props = defineProps({
  23. maxSubscriber: {
  24. type: Number,
  25. default: 0
  26. },
  27. curSubscriber: {
  28. type: Number,
  29. default: 0
  30. }
  31. })
  32. /** 차트 Cneter Text Plugin */
  33. const centerTextPlugin = {
  34. id: 'centerText',
  35. beforeDraw(chart) {
  36. if(chart.config.type === 'doughnut'){
  37. const { width, height, ctx } = chart;
  38. const centerTextOptions = chart.config.options.plugins.centerText;
  39. if (centerTextOptions && !useUtil.isNull(centerTextOptions.currentValue) && !useUtil.isNull(centerTextOptions.maxValue)) {
  40. const max = centerTextOptions.maxValue;
  41. const curr = centerTextOptions.currentValue;
  42. ctx.restore();
  43. const fontSize = (height / 150).toFixed(2);
  44. ctx.font = `${fontSize}em`;
  45. const text = `${curr}/${max}`;
  46. const textX = Math.round((width - ctx.measureText(text).width) / 2);
  47. const textY = height / 2 - 10;
  48. ctx.fillText(text, textX, textY);
  49. const percentText = `${fnGetPercentValue(max, curr)}%`;
  50. const percentTextX = Math.round((width - ctx.measureText(percentText).width) / 2);
  51. const percentTextY = height / 2 + 15;
  52. ctx.fillText(percentText, percentTextX, percentTextY);
  53. ctx.save();
  54. }
  55. }
  56. }
  57. };
  58. ChartJS.register(
  59. Title,
  60. Tooltip,
  61. Legend,
  62. BarElement,
  63. LineController,
  64. LineElement,
  65. PointElement,
  66. CategoryScale,
  67. ArcElement,
  68. LinearScale,
  69. centerTextPlugin
  70. )
  71. /***********************
  72. * data & created
  73. ************************/
  74. const chartData = ref({
  75. labels: ['현재 가입자 수', '남은 가입자 수'],
  76. datasets: [
  77. {
  78. data: [props.curSubscriber, (props.maxSubscriber - props.curSubscriber)],
  79. backgroundColor: ['#438dff','#EAEAEA'],
  80. borderWidth: 0,
  81. borderRadius: 200,
  82. }
  83. ]
  84. });
  85. const chartOptions = ref({
  86. responsive: true,
  87. maintainAspectRatio: false,
  88. cutout: '60%',
  89. plugins: {
  90. centerText: {
  91. currentValue: 65,
  92. maxValue: 100,
  93. },
  94. legend: {
  95. display: false
  96. },
  97. tooltip: {
  98. callbacks: {
  99. label: function(tooltipItem) {
  100. return tooltipItem.label + ': '+ tooltipItem.raw;// + tooltipItem.raw.toFixed(2) + '%'
  101. }
  102. }
  103. },
  104. centerText: centerTextPlugin
  105. }
  106. });
  107. const myData = computed(() => {
  108. const tempData = ref(_cloneDeep(chartData.value))
  109. tempData.value.datasets[0].data = [props.curSubscriber, (props.maxSubscriber - props.curSubscriber)]
  110. return tempData.value
  111. })
  112. const myOptions = computed(()=>{
  113. const tempOptions = ref(_cloneDeep(chartOptions.value))
  114. tempOptions.value.plugins.centerText = {currentValue: props.curSubscriber, maxValue: props.maxSubscriber}
  115. return tempOptions.value
  116. })
  117. /***********************
  118. * Methods
  119. ************************/
  120. /** make percent data */
  121. const fnGetPercentValue = (max, curr) => {
  122. return useUtil.toRoundFix((curr / max) * 100, 0);
  123. }
  124. </script>