Collaboration.vue 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405
  1. <template>
  2. <div class="chartsContainer">
  3. <div class="chartsContainer-title">协同配合</div>
  4. <div class="chartsContainer-list">
  5. <div class="chartsContainer-content">
  6. <div class="chartsContainer-content-top">
  7. <div class="chartsContainer-content-name">性格特征</div>
  8. </div>
  9. <div class="chartsContainer-content-content" ref="bubblesRef" />
  10. </div>
  11. <div class="chartsContainer-content-right">
  12. <div class="chartsContainer-content-item">
  13. <div class="chartsContainer-content-top">
  14. <div class="chartsContainer-content-name">工作风格</div>
  15. </div>
  16. <div class="chartsContainer-content-content" ref="pie" />
  17. </div>
  18. <div class="chartsContainer-content-item">
  19. <div class="chartsContainer-content-top">
  20. <div class="chartsContainer-content-name">性别人数</div>
  21. </div>
  22. <div class="chartsContainer-content-content" ref="bar" />
  23. </div>
  24. </div>
  25. </div>
  26. </div>
  27. </template>
  28. <script setup>
  29. import { getCollaborationProfile } from '@/api/item/items'
  30. import { useTimeOut } from './useTimeOut';
  31. import { useECharts } from '@/hooks/useEcharts.js';
  32. import { ref, computed, inject } from 'vue'
  33. const personalityData = ref([])
  34. const params = inject('provideParams')
  35. const bubblesRef = ref(null)
  36. const getBubblesOption = computed(() => {
  37. let maxValue = 1; // 防止数据全都为 0,后面会除以 maxValue,为 0 会出错
  38. let valueList = personalityData.value.map((item) => item.value);
  39. maxValue = Math.max(maxValue, ...valueList);
  40. // 设定尺寸的大小
  41. let minSymbolSize = 70; // 最小尺寸
  42. let symbolSize = 80; // 大小
  43. let repulsion = symbolSize * 1.3; // 斥力 为了防止重叠
  44. // 可以根据数据的多少,动态调整 symbolSize 的大小
  45. let valueListLen = valueList.length;
  46. if (valueListLen < 3) {
  47. symbolSize = 120;
  48. } else if (valueListLen < 5) {
  49. symbolSize = 100;
  50. }
  51. // 获取要渲染的数据
  52. let data = personalityData.value.map((item) => {
  53. // 根据比例与最小尺寸,算出每个元素的大小
  54. let size = Math.max(symbolSize * (item.value / maxValue), minSymbolSize);
  55. return {
  56. name: item.name,
  57. value: item.value,
  58. code: item.code,
  59. symbolSize: size,
  60. itemStyle: {
  61. color: item.type === 'I' ? {
  62. type: 'radial',
  63. x: 0.4, y: 0.3, r: 1,
  64. colorStops: [
  65. { offset: 0, color: '#FFD700' },
  66. { offset: 0.6, color: '#FFA500' },
  67. { offset: 1, color: '#333333' }
  68. ]
  69. } : {
  70. type: 'radial',
  71. x: 0.4, y: 0.3, r: 1,
  72. colorStops: [
  73. { offset: 0, color: '#87CEEB' },
  74. { offset: 0.6, color: '#1E90FF' },
  75. { offset: 1, color: '#333333' }
  76. ]
  77. }
  78. }
  79. };
  80. });
  81. return {
  82. xAxis: {
  83. show: false,
  84. },
  85. yAxis: {
  86. show: false,
  87. },
  88. series: [
  89. {
  90. data,
  91. type: "graph", // 关系图
  92. layout: "force", // 采用力引导布局
  93. force: {
  94. repulsion, // 值越大则斥力越大 每个元素间隔越大
  95. gravity: 0.11,
  96. edgeLength: 2,
  97. },
  98. roam: false,
  99. draggable: true,
  100. emphasis: {
  101. scale: 1.1,
  102. focus: 'series'
  103. },
  104. // 设置 label
  105. label: {
  106. show: true,
  107. position: "inside",
  108. formatter: (params) => {
  109. const data = params.data
  110. return [ data.code, data.name, `${data.value}人` ].join("\n")
  111. },
  112. fontSize: 12,
  113. color: "#FFF",
  114. align: "center",
  115. lineHeight: 14,
  116. fontWeight: 900
  117. },
  118. // 设置元素的样式
  119. itemStyle: {
  120. borderWidth: 1
  121. },
  122. },
  123. ],
  124. };
  125. })
  126. useECharts(bubblesRef, getBubblesOption)
  127. const pie = ref(null)
  128. const pieData = ref([])
  129. const pieALLCount = ref(0)
  130. const pieOption = computed(() => {
  131. return {
  132. tooltip: {
  133. trigger: 'axis',
  134. axisPointer: {
  135. type: 'shadow'
  136. },
  137. formatter: function (params) {
  138. return `${params[ 0 ].seriesName}: ${params[ 0 ].name}<br/>${params[ 0 ].marker}人数: ${params[ 0 ].value}人 (${Math.round(params[0].value / pieALLCount.value * 1000) / 10}%)`
  139. }
  140. },
  141. grid: {
  142. left: '20%',
  143. right: '10%',
  144. bottom: '10%',
  145. top: '5%',
  146. },
  147. xAxis: {
  148. type: 'value',
  149. show: true,
  150. axisLabel: {
  151. color: '#fff',
  152. },
  153. axisLine: {
  154. show: true,
  155. },
  156. minInterval: 1,
  157. },
  158. yAxis: {
  159. type: 'category',
  160. data: pieData.value.map(item => item.name),
  161. axisLabel: {
  162. color: '#fff',
  163. formatter: (value) => {
  164. return `${value.substring(0, 2)} \n${value.substring(2).replace('(', '(').replace(')', ')')}`
  165. }
  166. }
  167. },
  168. series: [
  169. {
  170. name: '工作风格',
  171. type: 'bar',
  172. stack: 'total',
  173. label: {
  174. show: true,
  175. color: '#fff'
  176. },
  177. color: '#408CFF',
  178. emphasis: {
  179. focus: 'series'
  180. },
  181. data: pieData.value.map(item => item.value),
  182. }
  183. ]
  184. }
  185. })
  186. useECharts(pie, pieOption)
  187. const bar = ref(null)
  188. const sexList = ref([])
  189. const sexALLCount = ref(0)
  190. const barOption = computed(() => {
  191. return {
  192. tooltip: {
  193. trigger: 'axis',
  194. axisPointer: {
  195. type: 'shadow'
  196. },
  197. formatter: function (params) {
  198. return `${params[ 0 ].name}<br/>${params[ 0 ].marker}人数: ${params[ 0 ].value}人 (${Math.round(params[0].value / sexALLCount.value * 1000) / 10}%)`
  199. }
  200. },
  201. grid: {
  202. left: '20%',
  203. right: '10%',
  204. bottom: '10%',
  205. top: '5%',
  206. },
  207. xAxis: {
  208. type: 'value',
  209. show: true,
  210. axisLabel: {
  211. color: '#fff'
  212. },
  213. axisLine: {
  214. show: true,
  215. color: '#fff'
  216. },
  217. minInterval: 1,
  218. },
  219. yAxis: {
  220. type: 'category',
  221. data: sexList.value.map(item => item.name),
  222. axisLabel: {
  223. color: '#fff'
  224. }
  225. },
  226. series: [ {
  227. name: '人数',
  228. type: 'bar',
  229. stack: 'total',
  230. label: {
  231. show: true,
  232. color: '#fff'
  233. },
  234. color: '#408CFF',
  235. emphasis: {
  236. focus: 'series'
  237. },
  238. data: sexList.value.map(item => item.value)
  239. } ]
  240. }
  241. })
  242. useECharts(bar, barOption)
  243. useTimeOut(() => {
  244. getCollaborationProfile({ deptId: params.value.deptId }).then(res => {
  245. let pieALLCountNumber = 0
  246. let sexALLCountNumber = 0
  247. pieData.value = (res.data || { workingStyleList: [] }).workingStyleList.map(item => {
  248. pieALLCountNumber += item.count
  249. return { value: item.count, name: item.name }
  250. })
  251. pieALLCount.value = pieALLCountNumber
  252. sexList.value = (res.data || { sexList: [] }).sexList.map(item => {
  253. sexALLCountNumber += item.count
  254. return { name: item.name, value: item.count }
  255. })
  256. sexALLCount.value = sexALLCountNumber
  257. personalityData.value = (res.data || { characterCharacteristicsList: [] }).characterCharacteristicsList.map(item => {
  258. return {
  259. name: item.name.substring(4),
  260. value: item.count,
  261. code: item.code,
  262. type: item.code.substring(0, 1)
  263. }
  264. })
  265. })
  266. }, [ params ])
  267. </script>
  268. <style lang="scss" scoped>
  269. .chartsContainer {
  270. width: 100%;
  271. height: 100%;
  272. position: relative;
  273. background: linear-gradient(to bottom, rgba(8, 97, 117, 0) 0%, #13a2d6 200%);
  274. border-radius: 4px;
  275. overflow: hidden;
  276. display: flex;
  277. flex-direction: column;
  278. .chartsContainer-title {
  279. height: 42px;
  280. width: 100%;
  281. background: linear-gradient(to right, #004387 0%, #090B18 100%);
  282. border-left: 1px solid #1892CE;
  283. line-height: 42px;
  284. color: #fff;
  285. font-weight: 900;
  286. font-size: 20px;
  287. text-indent: 1em;
  288. }
  289. .chartsContainer-list {
  290. padding: 20px 15px 10px;
  291. box-sizing: border-box;
  292. display: flex;
  293. flex: 1;
  294. column-gap: 10px;
  295. overflow: hidden;
  296. }
  297. .chartsContainer-content {
  298. display: flex;
  299. flex-direction: column;
  300. row-gap: 10px;
  301. flex: 1;
  302. .chartsContainer-content-top {
  303. display: flex;
  304. align-items: center;
  305. color: #78DEF5;
  306. .chartsContainer-content-name {
  307. color: #fff;
  308. font-weight: bold;
  309. font-size: 16px;
  310. display: flex;
  311. align-items: center;
  312. &::before {
  313. content: '';
  314. display: inline-block;
  315. border-left: 9px solid #1CB6FF;
  316. border-top: 8px solid transparent;
  317. border-right: 9px solid transparent;
  318. border-bottom: 8px solid transparent;
  319. }
  320. }
  321. }
  322. .chartsContainer-content-describe {
  323. color: #78DEF5;
  324. font-size: 12px;
  325. font-weight: bold;
  326. display: flex;
  327. align-items: center;
  328. }
  329. .chartsContainer-content-content {
  330. flex: 1;
  331. width: 100%;
  332. }
  333. }
  334. .chartsContainer-content-right {
  335. flex: 1;
  336. display: flex;
  337. flex-direction: column;
  338. row-gap: 15px;
  339. .chartsContainer-content-item {
  340. display: flex;
  341. flex-direction: column;
  342. row-gap: 10px;
  343. flex: 1;
  344. .chartsContainer-content-top {
  345. display: flex;
  346. align-items: center;
  347. color: #78DEF5;
  348. .chartsContainer-content-name {
  349. color: #fff;
  350. font-weight: bold;
  351. font-size: 16px;
  352. display: flex;
  353. align-items: center;
  354. &::before {
  355. content: '';
  356. display: inline-block;
  357. border-left: 9px solid #1CB6FF;
  358. border-top: 8px solid transparent;
  359. border-right: 9px solid transparent;
  360. border-bottom: 8px solid transparent;
  361. }
  362. }
  363. }
  364. .chartsContainer-content-describe {
  365. color: #78DEF5;
  366. font-size: 12px;
  367. font-weight: bold;
  368. display: flex;
  369. align-items: center;
  370. }
  371. .chartsContainer-content-content {
  372. flex: 1;
  373. width: 100%;
  374. }
  375. }
  376. }
  377. }
  378. </style>