qualityControl.vue 29 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052
  1. <template>
  2. <view class="quality-control mobile-section">
  3. <!-- 质控活动标题 -->
  4. <view class="section-title">
  5. <text class="title-text">质控活动</text>
  6. </view>
  7. <!-- 任务计划安排统计 -->
  8. <view class="task-planning" v-show="!isTeamType && !isUserType && !isDepartmentType">
  9. <view class="planning-header">
  10. <text class="header-text">任务计划安排统计</text>
  11. </view>
  12. <!-- 统计卡片 -->
  13. <view class="stat-cards">
  14. <view class="stat-card">
  15. <view class="stat-title">专项任务</view>
  16. <view class="stat-count">
  17. <text class="count-number">{{ specialTask && specialTask.count ? specialTask.count : 0 }}</text>
  18. <text class="count-unit">次</text>
  19. </view>
  20. <view class="stat-trend">
  21. <text>同比</text>
  22. <text :class="specialTask && specialTask.yearOnYearTrend ? specialTask.yearOnYearTrend : 'trend-neutral'">{{
  23. specialTask && specialTask.yearOnYear ? specialTask.yearOnYear : '0%' }}</text>
  24. <template v-if="queryForm.dateRangeQueryType !== 'YEAR'">
  25. <text>,环比</text>
  26. <text
  27. :class="specialTask && specialTask.chainRatioTrend ? specialTask.chainRatioTrend : 'trend-neutral'">{{
  28. specialTask && specialTask.chainRatio ? specialTask.chainRatio : '0%' }}</text>
  29. </template>
  30. </view>
  31. </view>
  32. <view class="stat-card">
  33. <view class="stat-title">日常任务</view>
  34. <view class="stat-count">
  35. <text class="count-number">{{ dailyTask && dailyTask.count ? dailyTask.count : 0 }}</text>
  36. <text class="count-unit">次</text>
  37. </view>
  38. <view class="stat-trend">
  39. <text>同比</text>
  40. <text :class="dailyTask && dailyTask.yearOnYearTrend ? dailyTask.yearOnYearTrend : 'trend-neutral'">{{
  41. dailyTask &&
  42. dailyTask.yearOnYear ? dailyTask.yearOnYear : '0%' }}</text>
  43. <template v-if="queryForm.dateRangeQueryType !== 'YEAR'">
  44. <text>,环比</text>
  45. <text :class="dailyTask && dailyTask.chainRatioTrend ? dailyTask.chainRatioTrend : 'trend-neutral'">{{
  46. dailyTask && dailyTask.chainRatio ? dailyTask.chainRatio : '0%' }}</text>
  47. </template>
  48. </view>
  49. </view>
  50. </view>
  51. <!-- 图表区域 -->
  52. <view class="chart-container">
  53. <view ref="planScheduleChartRef" class="echarts-chart"></view>
  54. </view>
  55. </view>
  56. <!-- 问题发现统计 -->
  57. <view class="problem-discovery">
  58. <view class="discovery-header">
  59. <text class="header-text">问题发现统计</text>
  60. </view>
  61. <!-- 描述卡片 -->
  62. <view class="describe-card" v-if="checkProblemDiscovery && checkProblemDiscovery.desc">
  63. <view class="describe-content">
  64. {{ checkProblemDiscovery.desc }}
  65. </view>
  66. </view>
  67. <!-- 图表区域 -->
  68. <view class="chart-container">
  69. <view ref="problemDiscoveryChartRef" class="echarts-chart"></view>
  70. </view>
  71. </view>
  72. <!-- 问题分布统计 -->
  73. <view class="problem-distribution">
  74. <view class="distribution-header">
  75. <text class="header-text">问题分布统计</text>
  76. </view>
  77. <!-- 描述卡片 -->
  78. <view class="describe-card" v-if="checkProblemDistribution && checkProblemDistribution.desc">
  79. <view class="describe-content">
  80. {{ checkProblemDistribution.desc }}
  81. </view>
  82. </view>
  83. <!-- 图表区域 -->
  84. <view class="chart-container">
  85. <view ref="problemDistributionChartRef" class="echarts-chart"></view>
  86. </view>
  87. </view>
  88. <!-- 问题整改统计 -->
  89. <view class="problem-rectification">
  90. <view class="rectification-header">
  91. <text class="header-text">问题整改统计</text>
  92. </view>
  93. <!-- 描述卡片 -->
  94. <view class="describe-card" v-if="checkProblemCorrection && checkProblemCorrection.desc">
  95. <view class="describe-content">
  96. {{ checkProblemCorrection.desc }}
  97. </view>
  98. </view>
  99. <!-- 图表区域 -->
  100. <view class="chart-container">
  101. <view ref="problemRectificationChartRef" class="echarts-chart"></view>
  102. </view>
  103. </view>
  104. </view>
  105. </template>
  106. <script>
  107. import * as echarts from 'echarts'
  108. import { getAnalysisReport } from '@/api/qualityControlAnalysisReport/qualityControlAnalysisReport'
  109. export default {
  110. name: 'QualityControl',
  111. props: {
  112. queryForm: {
  113. type: Object,
  114. default: () => ({})
  115. }
  116. },
  117. data() {
  118. return {
  119. // API返回的数据
  120. checkTask: {},
  121. checkProblemDiscovery: {},
  122. checkProblemDistribution: {},
  123. checkProblemCorrection: {},
  124. // 专项任务和日常任务数据
  125. specialTask: {
  126. count: 0,
  127. yearOnYear: '',
  128. yearOnYearTrend: 'trend-neutral',
  129. chainRatio: '',
  130. chainRatioTrend: 'trend-neutral'
  131. },
  132. dailyTask: {
  133. count: 0,
  134. yearOnYear: '',
  135. yearOnYearTrend: 'trend-neutral',
  136. chainRatio: '',
  137. chainRatioTrend: 'trend-neutral'
  138. },
  139. loading: false,
  140. // 图表实例
  141. planScheduleChart: null,
  142. problemDiscoveryChart: null,
  143. problemDistributionChart: null,
  144. problemRectificationChart: null
  145. }
  146. },
  147. computed: {
  148. // 计算属性:检查是否为TEAM类型
  149. isTeamType() {
  150. const roles = this.$store.state?.user?.roles || []
  151. // 如果是班组长且选择了班组视图,则视为班组类型
  152. if (roles.includes('xiaozuzhang') && this.queryForm.scopedType === 'TEAMS') {
  153. return true
  154. }
  155. return roles.includes('xiaozuzhang')
  156. },
  157. // 计算属性:检查是否为USER类型
  158. isUserType() {
  159. const roles = this.$store.state?.user?.roles || []
  160. // 如果是班组长且选择了个人视图,则视为用户类型
  161. if (roles.includes('xiaozuzhang') && this.queryForm.scopedType === 'USER') {
  162. return true
  163. }
  164. return roles.includes('SecurityCheck')
  165. },
  166. isBrigadeType() {
  167. const roles = this.$store.state?.user?.roles || []
  168. return roles.includes('bumenjingli') || roles.includes('xingzheng')
  169. },
  170. // 计算属性:检查是否为DEPARTMENT类型
  171. isDepartmentType() {
  172. const roles = this.$store.state?.user?.roles || []
  173. return roles.includes('banzuzhang')
  174. }
  175. },
  176. mounted() {
  177. this.loadData()
  178. this.$nextTick(() => {
  179. this.initCharts()
  180. })
  181. },
  182. beforeDestroy() {
  183. this.disposeCharts()
  184. },
  185. watch: {
  186. queryForm: {
  187. handler() {
  188. this.loadData()
  189. },
  190. deep: true
  191. },
  192. },
  193. methods: {
  194. // 解析desc数据并更新专项任务和日常任务数据
  195. parseTaskData(desc) {
  196. if (!desc) return
  197. // 解析desc格式:"专项任务:发布7次,同比上升0.00%,环比上升133.33%;\n日常任务:发布2次,同比上升0.00%,环比下降33.33%"
  198. const lines = desc.split(';')
  199. lines.forEach(line => {
  200. if (line.includes('专项任务')) {
  201. const countMatch = line.match(/发布(\d+)次/)
  202. const yearOnYearMatch = line.match(/同比(上升|下降|持平)([\d.-]+)%/)
  203. const chainRatioMatch = line.match(/环比(上升|下降|持平)([\d.-]+)%/)
  204. if (countMatch) {
  205. this.specialTask.count = parseInt(countMatch[1])
  206. }
  207. if (yearOnYearMatch) {
  208. const trendDirection = yearOnYearMatch[1]
  209. const trendValue = yearOnYearMatch[2]
  210. let trendType = 'trend-neutral'
  211. let displayValue = trendValue
  212. // 处理趋势值为"--"的情况
  213. if (trendValue === '--') {
  214. trendType = 'trend-neutral'
  215. displayValue = '--'
  216. } else {
  217. // 正常数值处理
  218. const value = parseFloat(trendValue)
  219. if (trendDirection === '上升') {
  220. trendType = 'trend-up'
  221. displayValue = '+' + trendValue
  222. } else if (trendDirection === '下降') {
  223. trendType = 'trend-down'
  224. displayValue = '-' + trendValue
  225. } else if (trendDirection === '持平') {
  226. trendType = 'trend-neutral'
  227. displayValue = trendValue
  228. }
  229. }
  230. this.specialTask.yearOnYearTrend = trendType
  231. this.specialTask.yearOnYear = displayValue + '%'
  232. }
  233. if (chainRatioMatch) {
  234. const trendDirection = chainRatioMatch[1]
  235. const trendValue = chainRatioMatch[2]
  236. let trendType = 'trend-neutral'
  237. let displayValue = trendValue
  238. // 处理趋势值为"--"的情况
  239. if (trendValue === '--') {
  240. trendType = 'trend-neutral'
  241. displayValue = '--'
  242. } else {
  243. // 正常数值处理
  244. const value = parseFloat(trendValue)
  245. if (trendDirection === '上升') {
  246. trendType = 'trend-up'
  247. displayValue = '+' + trendValue
  248. } else if (trendDirection === '下降') {
  249. trendType = 'trend-down'
  250. displayValue = '-' + trendValue
  251. } else if (trendDirection === '持平') {
  252. trendType = 'trend-neutral'
  253. displayValue = trendValue
  254. }
  255. }
  256. this.specialTask.chainRatioTrend = trendType
  257. this.specialTask.chainRatio = displayValue + '%'
  258. }
  259. } else if (line.includes('日常任务')) {
  260. const countMatch = line.match(/发布(\d+)次/)
  261. const yearOnYearMatch = line.match(/同比(上升|下降|持平)([\d.-]+)%/)
  262. const chainRatioMatch = line.match(/环比(上升|下降|持平)([\d.-]+)%/)
  263. if (countMatch) {
  264. this.dailyTask.count = parseInt(countMatch[1])
  265. }
  266. if (yearOnYearMatch) {
  267. const trendDirection = yearOnYearMatch[1]
  268. const trendValue = yearOnYearMatch[2]
  269. let trendType = 'trend-neutral'
  270. let displayValue = trendValue
  271. // 处理趋势值为"--"的情况
  272. if (trendValue === '--') {
  273. trendType = 'trend-neutral'
  274. displayValue = '--'
  275. } else {
  276. // 正常数值处理
  277. const value = parseFloat(trendValue)
  278. if (trendDirection === '上升') {
  279. trendType = 'trend-up'
  280. displayValue = '+' + trendValue
  281. } else if (trendDirection === '下降') {
  282. trendType = 'trend-down'
  283. displayValue = '-' + trendValue
  284. } else if (trendDirection === '持平') {
  285. trendType = 'trend-neutral'
  286. displayValue = trendValue
  287. }
  288. }
  289. this.dailyTask.yearOnYearTrend = trendType
  290. this.dailyTask.yearOnYear = displayValue + '%'
  291. }
  292. if (chainRatioMatch) {
  293. const trendDirection = chainRatioMatch[1]
  294. const trendValue = chainRatioMatch[2]
  295. let trendType = 'trend-neutral'
  296. let displayValue = trendValue
  297. // 处理趋势值为"--"的情况
  298. if (trendValue === '--') {
  299. trendType = 'trend-neutral'
  300. displayValue = '--'
  301. } else {
  302. // 正常数值处理
  303. const value = parseFloat(trendValue)
  304. if (trendDirection === '上升') {
  305. trendType = 'trend-up'
  306. displayValue = '+' + trendValue
  307. } else if (trendDirection === '下降') {
  308. trendType = 'trend-down'
  309. displayValue = '-' + trendValue
  310. } else if (trendDirection === '持平') {
  311. trendType = 'trend-neutral'
  312. displayValue = trendValue
  313. }
  314. }
  315. this.dailyTask.chainRatioTrend = trendType
  316. this.dailyTask.chainRatio = displayValue + '%'
  317. }
  318. }
  319. })
  320. },
  321. // 初始化所有图表
  322. initCharts() {
  323. this.initPlanScheduleChart()
  324. this.initProblemDiscoveryChart()
  325. this.initProblemDistributionChart()
  326. this.initProblemRectificationChart()
  327. },
  328. // 销毁所有图表
  329. disposeCharts() {
  330. if (this.planScheduleChart) {
  331. this.planScheduleChart.dispose()
  332. this.planScheduleChart = null
  333. }
  334. if (this.problemDiscoveryChart) {
  335. this.problemDiscoveryChart.dispose()
  336. this.problemDiscoveryChart = null
  337. }
  338. if (this.problemDistributionChart) {
  339. this.problemDistributionChart.dispose()
  340. this.problemDistributionChart = null
  341. }
  342. if (this.problemRectificationChart) {
  343. this.problemRectificationChart.dispose()
  344. this.problemRectificationChart = null
  345. }
  346. },
  347. // 创建ECharts实例(适配uni-app环境)
  348. createEChartsInstance(chartRef, options) {
  349. if (!chartRef) return null
  350. // 获取DOM元素
  351. const chartDom = chartRef.$el || chartRef
  352. if (!chartDom) return null
  353. // 创建ECharts实例
  354. const chartInstance = echarts.init(chartDom)
  355. // 设置初始选项
  356. if (options) {
  357. chartInstance.setOption(options)
  358. }
  359. return chartInstance
  360. },
  361. // 初始化任务计划安排图表(柱状图)
  362. initPlanScheduleChart() {
  363. const options = {
  364. tooltip: {
  365. trigger: 'axis',
  366. axisPointer: {
  367. type: 'shadow'
  368. },
  369. formatter: function (params) {
  370. let result = `${params[0].axisValue}<br/>`
  371. params.forEach(param => {
  372. result += `${param.seriesName}: <span style="color:${param.color};font-weight:bold">${param.data}</span>次<br/>`
  373. })
  374. return result
  375. }
  376. },
  377. legend: {
  378. data: ['专项任务', '日常任务']
  379. },
  380. grid: {
  381. left: '3%',
  382. right: '4%',
  383. bottom: '3%',
  384. containLabel: true
  385. },
  386. xAxis: {
  387. type: 'category',
  388. data: [],
  389. axisLine: {
  390. lineStyle: {
  391. color: '#999'
  392. }
  393. },
  394. axisLabel: {
  395. fontSize: 12
  396. }
  397. },
  398. yAxis: {
  399. type: 'value',
  400. name: '次数',
  401. axisLine: {
  402. lineStyle: {
  403. color: '#999'
  404. }
  405. },
  406. splitLine: {
  407. lineStyle: {
  408. color: '#f0f0f0'
  409. }
  410. }
  411. },
  412. series: [
  413. {
  414. name: '专项任务',
  415. type: 'bar',
  416. barWidth: '20%',
  417. itemStyle: {
  418. color: '#1890ff'
  419. },
  420. label: {
  421. show: true,
  422. position: 'top',
  423. formatter: '{c}次'
  424. },
  425. data: []
  426. },
  427. {
  428. name: '日常任务',
  429. type: 'bar',
  430. barWidth: '20%',
  431. itemStyle: {
  432. color: '#52c41a'
  433. },
  434. label: {
  435. show: true,
  436. position: 'top',
  437. formatter: '{c}次'
  438. },
  439. data: []
  440. }
  441. ]
  442. }
  443. this.planScheduleChart = this.createEChartsInstance(this.$refs.planScheduleChartRef, options)
  444. },
  445. // 初始化问题发现统计图表
  446. initProblemDiscoveryChart() {
  447. const options = {
  448. tooltip: {
  449. trigger: 'axis'
  450. },
  451. legend: {
  452. data: ['问题数量']
  453. },
  454. grid: {
  455. left: '3%',
  456. right: '4%',
  457. bottom: '3%',
  458. containLabel: true
  459. },
  460. xAxis: {
  461. type: 'category',
  462. data: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月']
  463. },
  464. yAxis: {
  465. type: 'value',
  466. name: '问题数量',
  467. axisLine: {
  468. lineStyle: {
  469. color: '#999'
  470. }
  471. },
  472. axisLabel: {
  473. fontSize: 12,
  474. margin: 8
  475. },
  476. splitLine: {
  477. lineStyle: {
  478. color: '#f0f0f0'
  479. }
  480. }
  481. },
  482. series: [
  483. {
  484. name: '问题数量',
  485. type: 'line',
  486. smooth: true,
  487. data: [],
  488. itemStyle: {
  489. color: '#fa541c'
  490. }
  491. },
  492. ]
  493. }
  494. this.problemDiscoveryChart = this.createEChartsInstance(this.$refs.problemDiscoveryChartRef, options)
  495. },
  496. // 初始化问题分布统计图表
  497. initProblemDistributionChart() {
  498. const options = {
  499. tooltip: {
  500. trigger: 'item',
  501. formatter: '{a} <br/>{b}: {c} ({d}%)'
  502. },
  503. legend: {
  504. orient: 'vertical',
  505. left: 'top',
  506. show: false
  507. },
  508. series: [
  509. {
  510. name: '问题分布',
  511. type: 'pie',
  512. radius: '50%',
  513. data: [],
  514. label: {
  515. show: true,
  516. formatter: '{b}: {c}个',
  517. fontSize: 12
  518. },
  519. emphasis: {
  520. itemStyle: {
  521. shadowBlur: 10,
  522. shadowOffsetX: 0,
  523. shadowColor: 'rgba(0, 0, 0, 0.5)'
  524. }
  525. }
  526. }
  527. ]
  528. }
  529. this.problemDistributionChart = this.createEChartsInstance(this.$refs.problemDistributionChartRef, options)
  530. },
  531. // 初始化问题整改统计图表
  532. initProblemRectificationChart() {
  533. const options = {
  534. tooltip: {
  535. trigger: 'axis'
  536. },
  537. legend: {
  538. data: ['按期已完成', '超期已完成', '按期整改中', '超期整改中', '其他'],
  539. },
  540. grid: {
  541. left: '3%',
  542. right: '4%',
  543. bottom: '3%',
  544. top: '40%',
  545. containLabel: true
  546. },
  547. xAxis: {
  548. type: 'category',
  549. data: [],
  550. axisLabel: {
  551. rotate: 45, // 倾斜45度
  552. interval: 0, // 显示所有标签
  553. fontSize: 12,
  554. margin: 10
  555. }
  556. },
  557. yAxis: {
  558. type: 'value',
  559. name: '问题数量',
  560. axisLine: {
  561. lineStyle: {
  562. color: '#999'
  563. }
  564. },
  565. splitLine: {
  566. lineStyle: {
  567. color: '#f0f0f0'
  568. }
  569. }
  570. },
  571. series: [
  572. {
  573. name: '按期已完成',
  574. type: 'bar',
  575. barWidth: '10%',
  576. itemStyle: {
  577. color: '#4ECDC4'
  578. },
  579. label: {
  580. show: false,
  581. position: 'top',
  582. formatter: '{c}个'
  583. },
  584. data: []
  585. },
  586. {
  587. name: '超期已完成',
  588. type: 'bar',
  589. barWidth: '10%',
  590. itemStyle: {
  591. color: '#FF6B6B'
  592. },
  593. label: {
  594. show: false,
  595. position: 'top',
  596. formatter: '{c}个'
  597. },
  598. data: []
  599. },
  600. {
  601. name: '按期整改中',
  602. type: 'bar',
  603. barWidth: '10%',
  604. itemStyle: {
  605. color: '#FFD166'
  606. },
  607. label: {
  608. show: false,
  609. position: 'top',
  610. formatter: '{c}个'
  611. },
  612. data: []
  613. },
  614. {
  615. name: '超期整改中',
  616. type: 'bar',
  617. barWidth: '10%',
  618. itemStyle: {
  619. color: '#9B59B6'
  620. },
  621. label: {
  622. show: false,
  623. position: 'top',
  624. formatter: '{c}个'
  625. },
  626. data: []
  627. },
  628. {
  629. name: '其他',
  630. type: 'bar',
  631. barWidth: '10%',
  632. itemStyle: {
  633. color: '#95A5A6'
  634. },
  635. label: {
  636. show: false,
  637. position: 'top',
  638. formatter: '{c}个'
  639. },
  640. data: []
  641. }
  642. ]
  643. }
  644. this.problemRectificationChart = this.createEChartsInstance(this.$refs.problemRectificationChartRef, options)
  645. },
  646. // 加载数据
  647. async loadData() {
  648. if (this.loading) return
  649. this.loading = true
  650. try {
  651. let params = {
  652. ...this.queryForm,
  653. userId: this.queryForm.scopedType == 'USER' ? this.queryForm.scopedId : "",
  654. deptId: ["DEPARTMENT", "TEAMS", "MANAGER", "BRIGADE"].includes(this.queryForm.scopedType) ? this.queryForm.scopedId : ""
  655. }
  656. // 加载质控活动分析数据
  657. const analysisResponse = await getAnalysisReport(params)
  658. if (analysisResponse.code === 200) {
  659. // 解构赋值API返回的数据
  660. const { checkTaskDto, checkProblemDiscoveryDto, checkProblemDistributionDto, checkProblemCorrectionDto } = analysisResponse.data || {}
  661. // 赋值到响应式数据
  662. this.checkTask = checkTaskDto || {}
  663. this.checkProblemDiscovery = checkProblemDiscoveryDto || {}
  664. this.checkProblemDistribution = checkProblemDistributionDto || {}
  665. this.checkProblemCorrection = checkProblemCorrectionDto || {}
  666. // 如果checkTaskDto中有desc字段,解析并更新任务数据
  667. if (checkTaskDto && checkTaskDto.desc) {
  668. this.parseTaskData(checkTaskDto.desc)
  669. }
  670. // 更新图表和统计信息
  671. this.updateChartsWithData()
  672. }
  673. // 检查是否为团队或用户类型
  674. this.checkDeptType()
  675. } catch (error) {
  676. console.error('加载质控活动数据失败:', error)
  677. } finally {
  678. this.loading = false
  679. }
  680. },
  681. // 处理分析数据(已废弃,使用真实API数据)
  682. processAnalysisData(data) {
  683. // 此方法已不再使用,保留以兼容旧代码
  684. },
  685. // 处理分类数据(已废弃,使用真实API数据)
  686. processCategoryData(data) {
  687. // 此方法已不再使用,保留以兼容旧代码
  688. },
  689. // 检查部门类型(已废弃,使用computed属性替代)
  690. checkDeptType() {
  691. // 此方法已废弃,角色判断现在通过computed属性实现
  692. },
  693. // 更新任务计划安排图表数据
  694. updatePlanScheduleChart() {
  695. if (this.checkTask && this.checkTask.checkTaskItemDtoList && this.planScheduleChart) {
  696. const taskList = this.checkTask.checkTaskItemDtoList
  697. // 提取横坐标数据(desc字段)
  698. const xAxisData = [...new Set(taskList.map(item => item.desc))]
  699. // 提取专项任务数据
  700. const specialTaskData = taskList.filter(item => item.typeDesc === '专项任务').map(item => item.count)
  701. // 提取日常任务数据
  702. const dailyTaskData = taskList.filter(item => item.typeDesc === '日常任务').map(item => item.count)
  703. // 更新图表配置
  704. const newOptions = {
  705. xAxis: {
  706. data: xAxisData
  707. },
  708. series: [
  709. {
  710. data: specialTaskData
  711. },
  712. {
  713. data: dailyTaskData
  714. }
  715. ]
  716. }
  717. this.planScheduleChart.setOption(newOptions)
  718. }
  719. },
  720. // 更新问题发现统计图表数据
  721. updateProblemDiscoveryChart() {
  722. if (this.checkProblemDiscovery && this.checkProblemDiscovery.checkProblemDiscoveryItemDtoList && this.problemDiscoveryChart) {
  723. const discoveryList = this.checkProblemDiscovery.checkProblemDiscoveryItemDtoList
  724. // 提取横坐标数据(desc或name字段)
  725. const xAxisData = discoveryList.map(item => item.desc || item.name)
  726. // 提取问题数量数据
  727. const problemData = discoveryList.map(item => item.count || 0)
  728. // 更新图表配置
  729. const newOptions = {
  730. xAxis: {
  731. data: xAxisData
  732. },
  733. series: [
  734. {
  735. data: problemData
  736. }
  737. ]
  738. }
  739. this.problemDiscoveryChart.setOption(newOptions)
  740. }
  741. },
  742. // 更新问题分布统计图表数据
  743. updateProblemDistributionChart() {
  744. if (this.checkProblemDistribution && this.checkProblemDistribution.checkProblemDistributionItemDtoList && this.problemDistributionChart) {
  745. const distributionList = this.checkProblemDistribution.checkProblemDistributionItemDtoList
  746. // 提取饼图数据
  747. const pieData = distributionList.map(item => ({
  748. value: item.total || 0,
  749. name: item.desc || item.name
  750. }))
  751. // 更新图表配置
  752. const newOptions = {
  753. series: [
  754. {
  755. data: pieData
  756. }
  757. ]
  758. }
  759. this.problemDistributionChart.setOption(newOptions)
  760. }
  761. },
  762. // 更新问题整改统计图表数据
  763. updateProblemRectificationChart() {
  764. if (this.checkProblemCorrection && this.checkProblemCorrection.checkProblemCorrectionItemDtoList && this.problemRectificationChart) {
  765. const rectificationList = this.checkProblemCorrection.checkProblemCorrectionItemDtoList
  766. // 提取横坐标数据(主管名称)
  767. const xAxisData = rectificationList.map(item => item.deptName || '未知主管')
  768. // 提取各个数据系列
  769. const onTimeCompletedData = rectificationList.map(item => item.onTimeCompletedCount || 0)
  770. const overTimeCompletedData = rectificationList.map(item => item.overTimeCompletedCount || 0)
  771. const onTimeUnfinishedData = rectificationList.map(item => item.onTimeUnfinishedCount || 0)
  772. const overTimeUnfinishedData = rectificationList.map(item => item.overTimeUnfinishedCount || 0)
  773. const otherData = rectificationList.map(item => item.otherCount || 0)
  774. // 更新图表配置
  775. const newOptions = {
  776. xAxis: {
  777. data: xAxisData
  778. },
  779. series: [
  780. {
  781. data: onTimeCompletedData
  782. },
  783. {
  784. data: overTimeCompletedData
  785. },
  786. {
  787. data: onTimeUnfinishedData
  788. },
  789. {
  790. data: overTimeUnfinishedData
  791. },
  792. {
  793. data: otherData
  794. }
  795. ]
  796. }
  797. this.problemRectificationChart.setOption(newOptions)
  798. }
  799. },
  800. // 根据实际数据更新图表
  801. updateChartsWithData() {
  802. // 更新任务计划安排图表
  803. this.updatePlanScheduleChart()
  804. // 更新问题发现统计图表
  805. this.updateProblemDiscoveryChart()
  806. // 更新问题分布统计图表
  807. this.updateProblemDistributionChart()
  808. // 更新问题整改统计图表
  809. this.updateProblemRectificationChart()
  810. }
  811. }
  812. }
  813. </script>
  814. <style lang="scss" scoped>
  815. .quality-control {
  816. // padding: 24rpx;
  817. background: #fff;
  818. border-radius: 16rpx;
  819. margin-bottom: 24rpx;
  820. }
  821. .section-title {
  822. margin-bottom: 24rpx;
  823. .title-text {
  824. font-size: 32rpx;
  825. font-weight: bold;
  826. color: #333;
  827. }
  828. }
  829. .task-planning,
  830. .problem-discovery,
  831. .problem-distribution,
  832. .problem-rectification {
  833. border: 1px solid #e8e8e8;
  834. border-radius: 10rpx;
  835. padding: 20rpx;
  836. margin-bottom: 32rpx;
  837. }
  838. .planning-header,
  839. .discovery-header,
  840. .distribution-header,
  841. .rectification-header {
  842. margin-bottom: 16rpx;
  843. .header-text {
  844. font-size: 28rpx;
  845. font-weight: 600;
  846. color: #333;
  847. }
  848. }
  849. .stat-cards {
  850. display: flex;
  851. flex-direction: row;
  852. gap: 16rpx;
  853. margin-bottom: 24rpx;
  854. }
  855. .stat-card {
  856. background: #f8f9fa;
  857. border-radius: 12rpx;
  858. padding: 20rpx;
  859. flex: 1;
  860. .stat-title {
  861. font-size: 24rpx;
  862. color: #666;
  863. margin-bottom: 12rpx;
  864. }
  865. .stat-count {
  866. display: flex;
  867. align-items: baseline;
  868. margin-bottom: 12rpx;
  869. .count-number {
  870. font-size: 32rpx;
  871. font-weight: bold;
  872. color: #1890ff;
  873. margin-right: 8rpx;
  874. }
  875. .count-unit {
  876. font-size: 24rpx;
  877. color: #999;
  878. }
  879. }
  880. .stat-trend {
  881. font-size: 24rpx;
  882. color: #666;
  883. .trend-up {
  884. color: #f5222d;
  885. }
  886. .trend-down {
  887. color: #52c41a;
  888. }
  889. .trend-neutral {
  890. color: #999;
  891. }
  892. }
  893. }
  894. .describe-card {
  895. background: #f8f9fa;
  896. border-radius: 12rpx;
  897. padding: 20rpx;
  898. margin-bottom: 16rpx;
  899. .describe-content {
  900. font-size: 26rpx;
  901. line-height: 1.5;
  902. color: #666;
  903. }
  904. }
  905. .chart-container {
  906. // background: #f8f9fa;
  907. border-radius: 12rpx;
  908. // padding: 40rpx 20rpx;
  909. text-align: center;
  910. height: 400rpx;
  911. .echarts-chart {
  912. width: 100% !important;
  913. height: 100% !important;
  914. min-height: 400rpx;
  915. display: block;
  916. }
  917. .chart-placeholder {
  918. .placeholder-text {
  919. font-size: 24rpx;
  920. color: #999;
  921. }
  922. }
  923. }
  924. .trend-up {
  925. color: #f5222d;
  926. }
  927. .trend-down {
  928. color: #52c41a;
  929. }
  930. .trend-neutral {
  931. color: #999;
  932. }
  933. </style>