ProfilePositionDistribution.vue 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. <template>
  2. <InfoCard title="成员职位情况分布">
  3. <div class="position-distribution">
  4. <div class="distribution-item">
  5. <div ref="skillChartRef" class="distribution-chart"></div>
  6. <div class="distribution-label">职业资格等级分布</div>
  7. </div>
  8. <div class="distribution-item">
  9. <div ref="operateChartRef" class="distribution-chart"></div>
  10. <div class="distribution-label">开机年限分布</div>
  11. </div>
  12. <div class="distribution-item">
  13. <div ref="postChartRef" class="distribution-chart"></div>
  14. <div class="distribution-label">岗位资质分布</div>
  15. </div>
  16. </div>
  17. </InfoCard>
  18. </template>
  19. <script setup>
  20. import { ref, onMounted, onUnmounted, nextTick, watch } from 'vue'
  21. import * as echarts from 'echarts'
  22. import InfoCard from './card.vue'
  23. const props = defineProps({
  24. chartData1: {
  25. type: Array,
  26. default: () => []
  27. },
  28. chartData2: {
  29. type: Array,
  30. default: () => []
  31. },
  32. chartData3: {
  33. type: Array,
  34. default: () => []
  35. }
  36. })
  37. const defaultSkillData = {
  38. categories: [],
  39. values: [],
  40. colors: ['#4da6ff', '#0f46fa']
  41. }
  42. const defaultOperateData = {
  43. categories: [],
  44. values: [],
  45. colors: ['#6bcb77', '#2ecc71']
  46. }
  47. const defaultPostData = {
  48. categories: [],
  49. values: [],
  50. colors: ['#ff6b6b', '#ee5a24']
  51. }
  52. const skillColors = ['#4da6ff', '#0f46fa']
  53. const operateColors = ['#6bcb77', '#2ecc71']
  54. const postColors = ['#ff6b6b', '#ee5a24']
  55. const skillChartRef = ref(null)
  56. let skillChart = null
  57. const operateChartRef = ref(null)
  58. let operateChart = null
  59. const postChartRef = ref(null)
  60. let postChart = null
  61. const transformData = (data, defaultData) => {
  62. if (!Array.isArray(data) || data.length === 0) {
  63. return {
  64. categories: defaultData.categories,
  65. values: defaultData.values,
  66. colors: defaultData.colors
  67. }
  68. }
  69. return {
  70. categories: data.map(item => item.name),
  71. values: data.map(item => item.value),
  72. colors: defaultData.colors
  73. }
  74. }
  75. const updateSkillChart = (data) => {
  76. if (!skillChartRef.value) return
  77. if (!skillChart) {
  78. skillChart = echarts.init(skillChartRef.value)
  79. }
  80. const chartData = transformData(data, defaultSkillData)
  81. skillChart.setOption({
  82. xAxis: {
  83. type: 'category',
  84. data: chartData.categories,
  85. axisLabel: { color: '#a0c4ff', fontSize: 10 },
  86. axisLine: { lineStyle: { color: 'rgba(15,70,250,0.3)' } }
  87. },
  88. yAxis: {
  89. type: 'value',
  90. axisLabel: { color: '#a0c4ff', fontSize: 10 },
  91. axisLine: { lineStyle: { color: 'rgba(15,70,250,0.3)' } },
  92. splitLine: { lineStyle: { color: 'rgba(15,70,250,0.2)' } }
  93. },
  94. series: [{
  95. type: 'bar',
  96. data: chartData.values,
  97. itemStyle: {
  98. color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
  99. { offset: 0, color: chartData.colors[0] },
  100. { offset: 1, color: chartData.colors[1] }
  101. ])
  102. },
  103. barWidth: '50%'
  104. }]
  105. })
  106. }
  107. const updateOperateChart = (data) => {
  108. if (!operateChartRef.value) return
  109. if (!operateChart) {
  110. operateChart = echarts.init(operateChartRef.value)
  111. }
  112. const chartData = transformData(data, defaultOperateData)
  113. operateChart.setOption({
  114. xAxis: {
  115. type: 'category',
  116. data: chartData.categories,
  117. axisLabel: { color: '#a0c4ff', fontSize: 10 },
  118. axisLine: { lineStyle: { color: 'rgba(15,70,250,0.3)' } }
  119. },
  120. yAxis: {
  121. type: 'value',
  122. axisLabel: { color: '#a0c4ff', fontSize: 10 },
  123. axisLine: { lineStyle: { color: 'rgba(15,70,250,0.3)' } },
  124. splitLine: { lineStyle: { color: 'rgba(15,70,250,0.2)' } }
  125. },
  126. series: [{
  127. type: 'bar',
  128. data: chartData.values,
  129. itemStyle: {
  130. color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
  131. { offset: 0, color: chartData.colors[0] },
  132. { offset: 1, color: chartData.colors[1] }
  133. ])
  134. },
  135. barWidth: '50%'
  136. }]
  137. })
  138. }
  139. const updatePostChart = (data) => {
  140. if (!postChartRef.value) return
  141. if (!postChart) {
  142. postChart = echarts.init(postChartRef.value)
  143. }
  144. const chartData = transformData(data, defaultPostData)
  145. postChart.setOption({
  146. xAxis: {
  147. type: 'category',
  148. data: chartData.categories,
  149. axisLabel: { color: '#a0c4ff', fontSize: 10 },
  150. axisLine: { lineStyle: { color: 'rgba(15,70,250,0.3)' } }
  151. },
  152. yAxis: {
  153. type: 'value',
  154. axisLabel: { color: '#a0c4ff', fontSize: 10 },
  155. axisLine: { lineStyle: { color: 'rgba(15,70,250,0.3)' } },
  156. splitLine: { lineStyle: { color: 'rgba(15,70,250,0.2)' } }
  157. },
  158. series: [{
  159. type: 'bar',
  160. data: chartData.values,
  161. itemStyle: {
  162. color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
  163. { offset: 0, color: chartData.colors[0] },
  164. { offset: 1, color: chartData.colors[1] }
  165. ])
  166. },
  167. barWidth: '50%'
  168. }]
  169. })
  170. }
  171. watch(() => props.chartData1, (val) => {
  172. updateSkillChart(val)
  173. }, { deep: true })
  174. watch(() => props.chartData2, (val) => {
  175. updateOperateChart(val)
  176. }, { deep: true })
  177. watch(() => props.chartData3, (val) => {
  178. updatePostChart(val)
  179. }, { deep: true })
  180. const initCharts = () => {
  181. updateSkillChart(props.chartData1)
  182. updateOperateChart(props.chartData2)
  183. updatePostChart(props.chartData3)
  184. }
  185. const handleResize = () => {
  186. if (skillChart) skillChart.resize()
  187. if (operateChart) operateChart.resize()
  188. if (postChart) postChart.resize()
  189. }
  190. onMounted(() => {
  191. nextTick(() => {
  192. setTimeout(() => {
  193. initCharts()
  194. window.addEventListener('resize', handleResize)
  195. }, 100)
  196. })
  197. })
  198. onUnmounted(() => {
  199. window.removeEventListener('resize', handleResize)
  200. if (skillChart) skillChart.dispose()
  201. if (operateChart) operateChart.dispose()
  202. if (postChart) postChart.dispose()
  203. })
  204. </script>
  205. <style lang="scss" scoped>
  206. .position-distribution {
  207. display: flex;
  208. justify-content: space-around;
  209. align-items: stretch;
  210. min-height: 280px;
  211. gap: 15px;
  212. padding: 10px 0;
  213. .distribution-item {
  214. flex: 1;
  215. display: flex;
  216. flex-direction: column;
  217. align-items: center;
  218. justify-content: center;
  219. min-width: 0;
  220. .distribution-chart {
  221. width: 100%;
  222. height: 200px;
  223. }
  224. .distribution-label {
  225. color: #a0c4ff;
  226. font-size: 12px;
  227. margin-top: 10px;
  228. text-align: center;
  229. }
  230. }
  231. }
  232. </style>