Schedule.vue 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  1. <template>
  2. <el-row style="display: flex;flex-direction: row;align-items: center;margin-bottom: 10px;color: #333333">
  3. <h4>项目概览</h4>
  4. </el-row>
  5. <el-card shadow="never" class='description-card'>
  6. <el-row>
  7. <el-col :span="12">
  8. <div class="description-card-text" style="font-size: 18px;font-weight: bold">{{selectedProject.name}}</div>
  9. <div class="description-card-text">项目地址:
  10. <span>
  11. <el-link
  12. target="_blank"
  13. type="primary"
  14. @click.prevent="copy(selectedProject.gitRemoteUrl)"
  15. >
  16. {{selectedProject.gitRemoteUrl}}
  17. </el-link>
  18. </span>
  19. </div>
  20. <div class="description-card-text">项目介绍:
  21. <span>{{selectedProject.description}}</span>
  22. </div>
  23. <div class="description-card-text">项目成员:
  24. <span>
  25. <el-tag v-for="item in projectMembers" :key="item.id" style="margin: 0 5px;">
  26. {{item.username}}
  27. </el-tag>
  28. </span>
  29. </div>
  30. </el-col>
  31. <el-col :span="6">
  32. <div style="height: 140px;width: 140px" id="requirement_pie"></div>
  33. </el-col>
  34. <el-col :span="6">
  35. <div style="height: 140px;width: 140px" id="bug_pie"></div>
  36. </el-col>
  37. </el-row>
  38. </el-card>
  39. <el-row style="display: flex;flex-direction: row;align-items: center;margin-bottom: 10px;color: #333333">
  40. <h4>项目规划</h4>
  41. </el-row>
  42. <Tree
  43. :data="requirements"
  44. v-loading="loading"
  45. @change="handleTreeChange"
  46. @nodeClick="handleNodeClick"
  47. ref="tree"
  48. ></Tree>
  49. <RequirementDrawer
  50. ref="drawer"
  51. v-bind="requirementInDrawer"
  52. @change="handleDrawerContentChange"
  53. @requirement-update="getTreeData"
  54. ></RequirementDrawer>
  55. </template>
  56. <script>
  57. import Tree from '@/components/Tree.vue';
  58. import * as echarts from 'echarts';
  59. import { BugListAPI, RequirementAPI } from '@/api';
  60. import RequirementDrawer from '@/components/RequirementDrawer.vue';
  61. import { copy } from '@/utils/clipboard';
  62. import { useProjectList } from '../../composable/useProject';
  63. export default {
  64. name: 'Schedule',
  65. components: { RequirementDrawer, Tree },
  66. setup() {
  67. const { selectedProject } = useProjectList();
  68. return {
  69. selectedProject,
  70. };
  71. },
  72. data() {
  73. return {
  74. loading: false,
  75. requirements: [],
  76. showRequirementDrawer: false,
  77. addRequirementDrawer: false,
  78. parentOfAddingRequirement: null,
  79. requirementInDrawer: {},
  80. projectMembers: [],
  81. };
  82. },
  83. computed: {
  84. projectId() {
  85. return this.$route.params.projectId;
  86. },
  87. },
  88. watch: {
  89. $route: {
  90. async handler() {
  91. this.projectMembers = this.$store.state.workspace.currentProjectMembers;
  92. await this.getTreeData();
  93. this.drawChart();
  94. },
  95. immediate: true,
  96. },
  97. },
  98. methods: {
  99. copy,
  100. async getTreeData() {
  101. this.loading = true;
  102. try {
  103. this.requirements = this.addChild2Node(await RequirementAPI.getTreeNodesByProjectId(this.projectId));
  104. } catch (e) {
  105. console.error(e);
  106. }
  107. this.loading = false;
  108. },
  109. addChild2Node(array) {
  110. const nodeMap = new Map();
  111. array.forEach((item) => nodeMap.set(item.id, item));
  112. array.forEach((item) => {
  113. if (item.parentId) {
  114. const tempParent = nodeMap.get(item.parentId);
  115. if (!tempParent.childs) {
  116. tempParent.childs = [];
  117. }
  118. tempParent.childs.push(item);
  119. }
  120. });
  121. // 返回前序遍历的列表
  122. const newArr = [];
  123. const preOrder = (node) => {
  124. newArr.push(node);
  125. if (!node.childs || node.childs.length === 0) return;
  126. node.childs.sort((node1, node2) => node1.id - node2.id);
  127. newArr.concat(node.childs);
  128. node.childs.forEach((_node) => preOrder(_node));
  129. };
  130. const root = array.find((item) => !item.parentId);
  131. preOrder(root);
  132. return newArr;
  133. },
  134. async handleTreeChange(msg) {
  135. const { type, id } = msg;
  136. await RequirementAPI[type](id);
  137. await this.getTreeData();
  138. },
  139. handleNodeClick(id) {
  140. this.$refs.drawer.show();
  141. this.requirementInDrawer = Array.prototype.find.call(this.requirements, (requirement) => requirement.id === id);
  142. },
  143. handleDrawerContentChange(id) {
  144. this.requirementInDrawer = Array.prototype.find.call(this.requirements, (requirement) => requirement.id === id);
  145. },
  146. async drawChart() {
  147. const bugList = await BugListAPI.getBugListByProjectId(this.projectId);
  148. const requirementsValuesMap = new Map();
  149. const requirementsValues = [];
  150. const bugListValuesMap = new Map();
  151. const bugListValues = [];
  152. this.requirements.forEach((item) => {
  153. if (item.type === '任务') {
  154. if (!requirementsValuesMap.get(item.state)) requirementsValuesMap.set(item.state, 0);
  155. requirementsValuesMap.set(item.state, requirementsValuesMap.get(item.state) + 1);
  156. }
  157. });
  158. requirementsValuesMap.forEach((value, key) => {
  159. requirementsValues.push(
  160. { value, name: key },
  161. );
  162. });
  163. bugList.forEach((item) => {
  164. if (!bugListValuesMap.get(item.state)) bugListValuesMap.set(item.state, 0);
  165. bugListValuesMap.set(item.state, bugListValuesMap.get(item.state) + 1);
  166. });
  167. bugListValuesMap.forEach((value, key) => {
  168. bugListValues.push(
  169. { value, name: key },
  170. );
  171. });
  172. if (bugListValues.length === 0) {
  173. bugListValues.push({
  174. name: '无记录', value: 0,
  175. });
  176. }
  177. if (requirementsValues.length === 0) {
  178. requirementsValues.push({
  179. name: '无记录', value: 0,
  180. });
  181. }
  182. const requirementPie = echarts.init(document.getElementById('requirement_pie'));
  183. requirementPie.setOption({
  184. title: {
  185. text: '任务完成情况',
  186. left: 'center',
  187. textStyle: {
  188. fontSize: 12,
  189. },
  190. },
  191. tooltip: {
  192. trigger: 'item',
  193. },
  194. series: [
  195. {
  196. name: '任务情况',
  197. type: 'pie',
  198. radius: ['40%', '70%'],
  199. avoidLabelOverlap: false,
  200. label: {
  201. show: false,
  202. position: 'center',
  203. },
  204. emphasis: {
  205. label: {
  206. show: true,
  207. fontSize: '10',
  208. fontWeight: 'bold',
  209. },
  210. },
  211. labelLine: {
  212. show: false,
  213. },
  214. data: requirementsValues,
  215. },
  216. ],
  217. });
  218. const bugPie = echarts.init(document.getElementById('bug_pie'));
  219. bugPie.setOption({
  220. title: {
  221. text: 'Bug处理情况',
  222. left: 'center',
  223. textStyle: {
  224. fontSize: 12,
  225. },
  226. },
  227. tooltip: {
  228. trigger: 'item',
  229. },
  230. series: [
  231. {
  232. name: 'Bug情况',
  233. type: 'pie',
  234. radius: ['40%', '70%'],
  235. avoidLabelOverlap: false,
  236. label: {
  237. show: false,
  238. position: 'center',
  239. },
  240. emphasis: {
  241. label: {
  242. show: true,
  243. fontSize: '10',
  244. fontWeight: 'bold',
  245. },
  246. },
  247. labelLine: {
  248. show: false,
  249. },
  250. data: bugListValues,
  251. },
  252. ],
  253. });
  254. },
  255. },
  256. };
  257. </script>
  258. <style>
  259. .description-card{
  260. margin: 10px 0;
  261. }
  262. .description-card-text{
  263. margin: 5px 0;
  264. }
  265. </style>