index.vue 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518
  1. <template>
  2. <div class="home-contain" v-loading="loading">
  3. <div style="position: relative; padding: 16px 0; background-color: #fff;">
  4. <div style="position: absolute; top: 20px; right: 20px; z-index: 99;">
  5. <div style="display: flex; align-items: center;">
  6. <year-month-select v-model="currYear" :showMonth="false" style="margin: 0;" />
  7. <el-button type="primary" size="mini" icon="el-icon-search" style="margin-left: 12px;" @click="handleSearchBtn">查询</el-button>
  8. </div>
  9. </div>
  10. <el-row gutter="20">
  11. <el-col :span="9">
  12. <basic-card title="研发项目情况">
  13. <avue-data-display :option="projectTotalOpt"></avue-data-display>
  14. <div style="position: relative;">
  15. <basic-chart
  16. style="width: 100%; height: 300px"
  17. :options="projectStatusOpt"
  18. />
  19. </div>
  20. </basic-card>
  21. </el-col>
  22. <el-col :span="9">
  23. <basic-card title="研发费用情况">
  24. <avue-data-display :option="RD_costOpt"></avue-data-display>
  25. <div style="position: relative;">
  26. <basic-chart
  27. style="width: 100%; height: 300px"
  28. :options="yffyPieOptions"
  29. />
  30. </div>
  31. </basic-card>
  32. </el-col>
  33. </el-row>
  34. <el-divider></el-divider>
  35. <el-row gutter="20">
  36. <el-col :span="9">
  37. <basic-card title="研发人员情况">
  38. <avue-data-display :option="RD_person_zb_opt"></avue-data-display>
  39. <div style="position: relative;">
  40. <basic-chart
  41. style="width: 100%; height: 300px"
  42. :options="RD_personOpt"
  43. />
  44. </div>
  45. </basic-card>
  46. </el-col>
  47. <el-col :span="9">
  48. <basic-card title="知识产权情况">
  49. <avue-data-display :option="zscqTotalOpt"></avue-data-display>
  50. <div style="position: relative;">
  51. <basic-chart
  52. style="width: 100%; height: 300px"
  53. :options="zscqOpt"
  54. />
  55. </div>
  56. </basic-card>
  57. </el-col>
  58. </el-row>
  59. <el-divider></el-divider>
  60. <basic-card title="本年度月研发归集趋势图">
  61. <el-row style="margin-top: 45px">
  62. <el-col :span="24">
  63. <basic-chart
  64. style="width: 100%; height: 300px"
  65. :options="monthAggregationBarOpt"
  66. />
  67. </el-col>
  68. </el-row>
  69. </basic-card>
  70. <el-divider></el-divider>
  71. <basic-card title="项目预算与归集趋势图">
  72. <el-row :gutter="24">
  73. <el-col :span="12">
  74. <basic-chart
  75. style="width: 100%; height: 300px"
  76. :options="currYearBudgetOpt"
  77. />
  78. </el-col>
  79. <el-col :span="12">
  80. <basic-chart
  81. style="width: 100%; height: 300px"
  82. :options="fullCycleBudgetOpt"
  83. />
  84. </el-col>
  85. </el-row>
  86. </basic-card>
  87. </div>
  88. </div>
  89. </template>
  90. <script>
  91. import basicCard from "@/components/basic-card";
  92. import basicChart from "@/components/basic-chart";
  93. import numStatistics from "./components/num-statistics.vue";
  94. import YearMonthSelect from "@/components/year-month-select";
  95. import { getProjectListByYear } from "@/api/projectManage/projectList";
  96. import { getList as getCostSummaryList } from "@/api/yfCostManage/yfCostStatistics/yfCostSummaryList";
  97. import { getList as getZlList } from "@/api/achievement/patentAchievement";
  98. import { getList as getCyPersonList } from "@/api/techPerson/cyPersonRoster";
  99. import Decimal from "decimal.js";
  100. export default {
  101. name: "home",
  102. components: {
  103. basicCard,
  104. basicChart,
  105. numStatistics,
  106. YearMonthSelect
  107. },
  108. data() {
  109. let monthArr = [];
  110. for(let i=0; i<12; i++) {
  111. monthArr.push((i+1)+"月");
  112. }
  113. return {
  114. loading: false,
  115. currYear: "",
  116. projectTotal: 0,
  117. costAmount: 0,
  118. yfPersonZb: 0, // 研发人员占比
  119. zscqTotal: 0,
  120. // 项目情况
  121. projectStatusOpt: {},
  122. // 研发费用情况
  123. yffyPieOptions: {},
  124. // 研发人员情况
  125. RD_personOpt: {},
  126. // 知识产权情况
  127. zscqOpt: {},
  128. // 本年度月研发归集趋势图
  129. monthAggregationBarOpt: {},
  130. // 本年度项目预算与归集表
  131. currYearBudgetOpt: {
  132. title: {
  133. text: "本年度",
  134. left: "13%",
  135. },
  136. tooltip: {
  137. trigger: "axis",
  138. axisPointer: {
  139. type: "shadow",
  140. },
  141. },
  142. legend: {
  143. left: "center",
  144. bottom: 0,
  145. },
  146. grid: {
  147. top: "16%",
  148. left: "8%",
  149. right: "2%",
  150. bottom: "13%",
  151. containLabel: true,
  152. },
  153. xAxis: {
  154. type: "value",
  155. boundaryGap: [0, 0.01],
  156. },
  157. yAxis: {
  158. type: "category",
  159. data: ["项目一", "项目二", "项目三", "项目四", "项目五", "项目六"],
  160. },
  161. series: [
  162. {
  163. name: "预算",
  164. type: "bar",
  165. data: [18203, 23489, 29034, 104970, 131744, 630230],
  166. },
  167. {
  168. name: "归集",
  169. type: "bar",
  170. data: [19325, 23438, 31000, 121594, 134141, 681807],
  171. },
  172. ],
  173. },
  174. // 全周期项目预算与归集表
  175. fullCycleBudgetOpt: {
  176. title: {
  177. text: "全周期",
  178. left: "13%",
  179. },
  180. tooltip: {
  181. trigger: "axis",
  182. axisPointer: {
  183. type: "shadow",
  184. },
  185. },
  186. legend: {
  187. left: "center",
  188. bottom: 0,
  189. },
  190. grid: {
  191. top: "16%",
  192. left: "8%",
  193. right: "2%",
  194. bottom: "13%",
  195. containLabel: true,
  196. },
  197. xAxis: {
  198. type: "value",
  199. boundaryGap: [0, 0.01],
  200. },
  201. yAxis: {
  202. type: "category",
  203. data: ["项目一", "项目二", "项目三", "项目四", "项目五", "项目六"],
  204. },
  205. series: [
  206. {
  207. name: "预算",
  208. type: "bar",
  209. data: [18203, 23489, 29034, 104970, 131744, 630230],
  210. },
  211. {
  212. name: "归集",
  213. type: "bar",
  214. data: [19325, 23438, 31000, 121594, 134141, 681807],
  215. },
  216. ],
  217. }
  218. };
  219. },
  220. computed: {
  221. projectTotalOpt() {
  222. return {
  223. span: 24,
  224. data: [
  225. {
  226. icon: 'el-icon-view',
  227. color: 'rgb(56, 161, 242)',
  228. count: this.projectTotal,
  229. decimals: 0,
  230. title: "项目总数",
  231. },
  232. ],
  233. }
  234. },
  235. RD_costOpt() {
  236. return {
  237. span: 24,
  238. data: [
  239. {
  240. icon: 'el-icon-view',
  241. color: 'rgb(56, 161, 242)',
  242. count: this.costAmount,
  243. decimals: 2,
  244. title: "研发费用(元)",
  245. },
  246. ],
  247. }
  248. },
  249. RD_person_zb_opt() {
  250. return {
  251. span: 24,
  252. data: [
  253. {
  254. icon: 'el-icon-view',
  255. color: 'rgb(56, 161, 242)',
  256. count: this.yfPersonZb,
  257. decimals: 2,
  258. title: "研发人员占比(%)",
  259. },
  260. ],
  261. }
  262. },
  263. zscqTotalOpt() {
  264. return {
  265. span: 24,
  266. data: [
  267. {
  268. icon: 'el-icon-view',
  269. color: 'rgb(56, 161, 242)',
  270. count: this.zscqTotal,
  271. decimals: 0,
  272. title: "知识产权总数",
  273. },
  274. ],
  275. }
  276. }
  277. },
  278. watch: {
  279. currYear() {
  280. this.getProjectListByYearFunc();
  281. this.getCostSummaryListFunc();
  282. this.getZlListFunc();
  283. this.getCyPersonListFunc();
  284. }
  285. },
  286. methods: {
  287. handleSearchBtn() {
  288. this.getProjectListByYearFunc();
  289. this.getCostSummaryListFunc();
  290. this.getZlListFunc();
  291. this.getCyPersonListFunc();
  292. },
  293. getProjectListByYearFunc() {
  294. getProjectListByYear({ year: this.currYear }).then(({ data }) => {
  295. let _projectList = data.data;
  296. this.projectTotal = _projectList.length;
  297. this.projectStatusOpt = this.getPieOptions("研发项目情况", [
  298. { value: _projectList.filter(item => item.status == 1).length, name: "开展中" },
  299. { value: _projectList.filter(item => item.status == 2).length, name: "已结题" },
  300. { value: _projectList.filter(item => item.status == 3).length, name: "项目暂停" },
  301. { value: _projectList.filter(item => item.status == 4).length, name: "项目终止" },
  302. ]);
  303. });
  304. },
  305. // 获取费用统计数据
  306. getCostSummaryListFunc() {
  307. this.loading = true;
  308. getCostSummaryList({ yearAndMonth: `${this.currYear}12` }).then(({ data }) => {
  309. this.loading = false;
  310. let costList = data.data;
  311. let ryrgfyTotal = new Decimal(0); // 人员人工费用
  312. let zjtrfyTotal = new Decimal(0); // 直接投入费用
  313. let zjfyycqdtfyTotal = new Decimal(0); // 折旧费用和长期待摊费用
  314. let sjfyTotal = new Decimal(0); // 设计费用
  315. let wxzctxfyTotal = new Decimal(0); // 无形资产摊销费用
  316. let zbtsfyysyfyTotal = new Decimal(0); // 装备调试费用与试验费用
  317. let qtfyTotal = new Decimal(0); // 其他费用
  318. let wwfyTotal = new Decimal(0); // 委外费用
  319. let monthArr = [];
  320. let monthSalesArr = [];
  321. costList.forEach(item => {
  322. ryrgfyTotal = ryrgfyTotal.add(new Decimal(item.ryrgfy));
  323. zjtrfyTotal = zjtrfyTotal.add(new Decimal(item.zjtrfy));
  324. zjfyycqdtfyTotal = zjfyycqdtfyTotal.add(new Decimal(item.zjfyycqdtfy));
  325. sjfyTotal = sjfyTotal.add(new Decimal(item.sjfy));
  326. wxzctxfyTotal = wxzctxfyTotal.add(new Decimal(item.wxzctxfy));
  327. zbtsfyysyfyTotal = zbtsfyysyfyTotal.add(new Decimal(item.zbtsfyysyfy));
  328. qtfyTotal = qtfyTotal.add(new Decimal(item.qtfy));
  329. wwfyTotal = wwfyTotal.add(new Decimal(item.wwfy));
  330. monthArr.push(item.yearAndMonth);
  331. monthSalesArr.push(Number(new Decimal(item.total).div(new Decimal(0.8))));
  332. });
  333. // 研发费用总额
  334. let costAmount = ryrgfyTotal
  335. .add(zjtrfyTotal)
  336. .add(zjfyycqdtfyTotal)
  337. .add(sjfyTotal)
  338. .add(wxzctxfyTotal)
  339. .add(zbtsfyysyfyTotal)
  340. .add(qtfyTotal)
  341. .add(wwfyTotal)
  342. this.costAmount = costAmount.toFixed(2);
  343. this.yffyPieOptions = this.getPieOptions("研发费用情况", [
  344. { value: Number(ryrgfyTotal), name: "人员人工费用" },
  345. { value: Number(zjtrfyTotal), name: "直接投入费用" },
  346. { value: Number(zjfyycqdtfyTotal), name: "折旧费用与长期待摊费用" },
  347. { value: Number(wxzctxfyTotal), name: "无形资产摊销费用" },
  348. { value: Number(sjfyTotal), name: "设计费用" },
  349. { value: Number(zbtsfyysyfyTotal), name: "装备调试费用与试验费用" },
  350. { value: Number(wwfyTotal), name: "委托外部研究开发费用" },
  351. { value: Number(qtfyTotal), name: "其他费用" },
  352. ]);
  353. // 本年度月研发归集趋势图
  354. this.monthAggregationBarOpt = {
  355. grid: {
  356. top: "3%",
  357. left: "3%",
  358. right: "3%",
  359. bottom: "10%",
  360. },
  361. tooltip: {
  362. trigger: "axis",
  363. axisPointer: {
  364. type: "shadow",
  365. },
  366. },
  367. xAxis: {
  368. type: 'category',
  369. data: monthArr
  370. },
  371. yAxis: {
  372. type: 'value',
  373. axisLabel: {
  374. formatter: value => {
  375. return value+" 元";
  376. }
  377. }
  378. },
  379. series: [
  380. {
  381. data: monthSalesArr,
  382. type: 'bar',
  383. barWidth: "25%",
  384. label: {
  385. show: true,
  386. position: "top"
  387. }
  388. }
  389. ]
  390. }
  391. }).catch(err => {
  392. this.loading = false;
  393. })
  394. },
  395. // 获取专利数据
  396. getZlListFunc() {
  397. getZlList(1, 99999).then(({ data }) => {
  398. let _zscqList = data.data.records;
  399. this.zscqTotal = _zscqList.length;
  400. // 授权数
  401. let sqListTotal = _zscqList.filter(item => item.flzt == '授权有效').length;
  402. this.zscqOpt = this.getPieOptions("", [
  403. { value: _zscqList.length - sqListTotal, name: "申请数" },
  404. { value: sqListTotal, name: "授权数" },
  405. ]);
  406. });
  407. },
  408. // 获取年度参研人员
  409. getCyPersonListFunc() {
  410. getCyPersonList(1, 99999, { yearAndMonth: this.currYear }).then(({ data }) => {
  411. let _personList = data.data.records;
  412. let yfPersonList = _personList.filter(item => item.personnelType == '研发人员');
  413. this.yfPersonZb = ((yfPersonList.length / _personList.length) * 100).toFixed(2);
  414. this.RD_personOpt = this.getPieOptions("", [
  415. { value: _personList.filter(item => item.situation == '全职').length, name: "全职" },
  416. { value: _personList.filter(item => item.situation == '非全职').length, name: "非全职" },
  417. ]);
  418. });
  419. },
  420. getPieOptions(title, seriesData) {
  421. let options = {
  422. title: {
  423. text: title,
  424. left: "center",
  425. show: false
  426. },
  427. tooltip: {
  428. trigger: "item",
  429. },
  430. legend: {
  431. orient: "vertical",
  432. top: "center",
  433. right: 0,
  434. textStyle: {
  435. rich: {
  436. a: {
  437. verticalAlign: "right",
  438. align: "left",
  439. width: 160,
  440. fontSize: 14,
  441. },
  442. num: {
  443. align: "right",
  444. fontSize: 14,
  445. color: "rgb(64, 158, 255)",
  446. },
  447. },
  448. },
  449. formatter: function (name) {
  450. let currItem = seriesData.find((item) => item.name === name);
  451. let str = "{a|" + name + "}{num|" + currItem.value + "}";
  452. return str;
  453. },
  454. },
  455. series: [
  456. {
  457. name: title,
  458. type: "pie",
  459. radius: "50%",
  460. radius: ["45%", "70%"],
  461. center: ["25%", "50%"],
  462. avoidLabelOverlap: false,
  463. label: {
  464. show: false,
  465. position: "center",
  466. },
  467. labelLine: {
  468. show: false,
  469. },
  470. data: seriesData,
  471. },
  472. ],
  473. };
  474. return options;
  475. },
  476. },
  477. };
  478. </script>
  479. <style lang="scss" scoped>
  480. .home-contain {
  481. min-height: calc(100vh - 50px);
  482. background-color: #eee;
  483. padding: 16px 18px;
  484. box-sizing: border-box;
  485. }
  486. .count {
  487. font-size: 20px;
  488. font-weight: 600;
  489. color: rgb(64, 158, 255);
  490. margin: 0 3px;
  491. }
  492. </style>