audit-risk-view.vue 39 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268
  1. <template>
  2. <a-spin :spinning="isshowLoading5" :tip="tiptext" size="large" style="background: #f0f2f5">
  3. <a-layout style="height: 100vh;padding: 20px;overflow: scroll">
  4. <a-card style="margin-bottom: 20px">
  5. <!-- 推送时间,业务时间,单位名称 -->
  6. <!-- 审计线索统计 -->
  7. <div :class="$style.title">
  8. <a-form-model layout="inline">
  9. <a-form-model-item>
  10. <div :class="$style.time">
  11. <a-row>
  12. <a-col :span="6">
  13. <a-select v-model="searchForm.timeType">
  14. <a-select-option key="1" value="1">推送时间</a-select-option>
  15. <a-select-option key="2" value="2">业务时间</a-select-option>
  16. </a-select>
  17. </a-col>
  18. <a-col :span="18" style="padding-top: 1px;">
  19. <a-range-picker v-model="searchForm.timeArr" />
  20. </a-col>
  21. </a-row>
  22. </div>
  23. </a-form-model-item>
  24. <a-form-model-item label="单位名称">
  25. <div style="width:200px">
  26. <a-select
  27. v-if="isUnitType === 'group'"
  28. ref="zb"
  29. v-model="targetKeysList"
  30. mode="multiple"
  31. :open="false"
  32. :max-tag-count="2"
  33. @focus="focusZb"
  34. >
  35. <a-select-option v-for="(item, i) in mockData" :key="i" :value="item.key">
  36. {{ item.title }}
  37. </a-select-option>
  38. </a-select>
  39. <SdGroupPicker
  40. v-else
  41. ref="cRelSelect"
  42. v-model="searchForm.unitArr"
  43. :read-only="false"
  44. :root-node="rootNodeData"
  45. @change="changecompany"
  46. />
  47. </div>
  48. </a-form-model-item>
  49. <a-form-model-item>
  50. <a-button type="primary" @click="search('all')">查询</a-button>
  51. <!-- 重置 -->
  52. <a-button @click="resetSearch">重置</a-button>
  53. </a-form-model-item>
  54. </a-form-model>
  55. </div>
  56. <!-- 模型类型 事件标签分类 模型领域 -->
  57. <!-- 查询导出 -->
  58. <a-divider />
  59. <h3 :class="$style.riskviewtitle">审计线索统计</h3>
  60. <div :class="$style.lineBarBody">
  61. <a-form-model layout="inline" :class="$style.formBar">
  62. <a-form-model-item :label="'模型类型'" prop="modelType">
  63. <a-select v-model="linebarSearchForm.modelType" placeholder="请选择模型类型">
  64. <a-select-option value="1">定位型-整改类</a-select-option>
  65. <a-select-option value="2">定位型-关注类</a-select-option>
  66. </a-select>
  67. </a-form-model-item>
  68. <a-form-model-item label="事件标签分类">
  69. <a-select v-model="linebarSearchForm.eventTag" mode="multiple" allow-clear>
  70. <a-select-option v-for="(item, i) in eventList" :key="i" :value="item.id">{{
  71. item.tagName + '-' + item.id
  72. }}</a-select-option>
  73. </a-select>
  74. </a-form-model-item>
  75. <a-form-model-item label="模型领域">
  76. <a-select
  77. v-model="linebarSearchForm.modelField"
  78. placeholder="请选择模型领域"
  79. allow-clear
  80. mode="multiple"
  81. >
  82. <a-select-option v-for="(item, i) in areaOption" :key="i" :value="item.id">{{
  83. item.name
  84. }}</a-select-option>
  85. </a-select>
  86. </a-form-model-item>
  87. <a-form-model-item>
  88. <a-button type="primary" @click="search('lineBer')">查询</a-button>
  89. <a-popover
  90. v-model="visibleExport"
  91. title="请选择导出维度"
  92. placement="bottom"
  93. trigger="click"
  94. >
  95. <div slot="content">
  96. <!-- 按照 事件标签 按责任人 确定取消按钮 -->
  97. <a-row style="margin-bottom: 15px">
  98. <a-radio-group v-model="exportType">
  99. <a-radio :style="radioStyle" value="1">按照事件标签</a-radio>
  100. <a-radio :style="radioStyle" value="2">按照责任人</a-radio>
  101. </a-radio-group>
  102. </a-row>
  103. <div style="text-align: right">
  104. <a-button @click="visibleExport = false">取消</a-button>
  105. <a-button type="primary" @click="onExportEvent">确定</a-button>
  106. </div>
  107. </div>
  108. <a-button type="primary">
  109. 导出
  110. </a-button>
  111. </a-popover>
  112. </a-form-model-item>
  113. </a-form-model>
  114. <a-spin :spinning="isshowLoading1" tip="正在查询请稍后...">
  115. <div id="linebar" :class="$style.linebar"></div>
  116. </a-spin>
  117. </div>
  118. </a-card>
  119. <!-- 整改率统计 -->
  120. <!-- 按单位 按模型 -->
  121. <!-- 单位名称 -->
  122. <a-card style="padding-top: 20px">
  123. <h3 :class="$style.riskviewtitle">整改率统计</h3>
  124. <div :class="$style.searchBtn">
  125. <a-button-group>
  126. <a-button :type="zgSumType ? 'primary' : ''" @click="zgSumType = true">按单位</a-button>
  127. <a-button :type="zgSumType ? '' : 'primary'" @click="zgSumType = false"
  128. >按领域</a-button
  129. >
  130. </a-button-group>
  131. </div>
  132. <a-form layout="inline">
  133. <a-form-model-item v-if="zgSumType" label="单位名称">
  134. <div style="width:200px">
  135. <SdGroupPicker
  136. ref="zgRelSelect"
  137. v-model="lineUnit"
  138. :root-node="rootNodeData"
  139. :read-only="false"
  140. :single="true"
  141. />
  142. </div>
  143. <!-- <a-input v-model=".unitName" /> -->
  144. </a-form-model-item>
  145. <a-form-model-item v-else label="模型领域" mode="multiple">
  146. <div style="width:200px">
  147. <a-select v-model="areaType" placeholder="请选择模型领域" allow-clear>
  148. <a-select-option v-for="(item, i) in areaOption" :key="i" :value="item.id">{{
  149. item.name
  150. }}</a-select-option>
  151. </a-select>
  152. </div>
  153. </a-form-model-item>
  154. <a-form-model-item>
  155. <a-button type="primary" @click="search('line')">查询</a-button>
  156. <a-button v-if="zgSumType" type="primary" @click="exportBar">导出</a-button>
  157. </a-form-model-item>
  158. </a-form>
  159. <a-spin :spinning="isshowLoading2" tip="正在查询请稍后...">
  160. <div id="rectifyRate" :class="$style.rectifyRate"></div>
  161. </a-spin>
  162. <!-- 问题概括统计饼图、模型领域统计饼图 -->
  163. <a-row :gutter="24" style="margin-top: 20px">
  164. <a-col :span="12" :class="$style.exportBody">
  165. <!-- 右上角导出 -->
  166. <h3 :class="$style.riskviewtitle">问题概括统计</h3>
  167. <div :class="$style.exportIcon">
  168. <a-icon type="download" :style="{ fontSize: '22px' }" @click="exportPie1" />
  169. </div>
  170. <a-spin :spinning="isshowLoading3" tip="正在查询请稍后...">
  171. <div v-if="barishow1" id="problemSummary" :class="$style.bingtu"></div>
  172. <a-empty
  173. v-else
  174. style="height: 400px;
  175. line-height: 400px;"
  176. />
  177. </a-spin>
  178. </a-col>
  179. <a-col :span="12" :class="$style.exportBody">
  180. <h3 :class="$style.riskviewtitle">模型领域统计</h3>
  181. <div :class="$style.exportIcon">
  182. <a-icon type="download" :style="{ fontSize: '22px' }" @click="exportPie2" />
  183. </div>
  184. <a-spin :spinning="isshowLoading4" tip="正在查询请稍后...">
  185. <div v-if="barishow2" id="modelField" :class="$style.bingtu"></div>
  186. <a-empty
  187. v-else
  188. style="height: 400px;
  189. line-height: 400px;"
  190. />
  191. </a-spin>
  192. </a-col>
  193. </a-row>
  194. </a-card>
  195. <a-modal v-model="visibleZb" title="公司选择" @ok="zbOk">
  196. <a-transfer
  197. :data-source="mockData"
  198. show-search
  199. :list-style="{
  200. width: '400px',
  201. height: '500px',
  202. }"
  203. :target-keys="targetKeys"
  204. :render="(item) => item.title"
  205. @change="handleChangeZb"
  206. />
  207. </a-modal>
  208. </a-layout>
  209. </a-spin>
  210. </template>
  211. <script>
  212. import components from './_import-components/audit-risk-view-import'
  213. import auditMaintainService from '../maintain/audit-maintain-service'
  214. import echarts from 'echarts'
  215. import AuditRiskbraryService from './riskLibrary'
  216. import moment from 'moment'
  217. import { getUserInfo } from '@/common/store-mixin'
  218. import DataService from '../dataUphold/data-config'
  219. import SdGroupPicker from '@/common/components/sd-group-picker.vue'
  220. import download from '@/common/services/download'
  221. import { message, Modal } from 'ant-design-vue'
  222. export default {
  223. name: 'AuditRiskView',
  224. metaInfo: {
  225. title: '审计问题看板',
  226. },
  227. components: {
  228. ...components,
  229. SdGroupPicker,
  230. },
  231. data() {
  232. return {
  233. isshowLoading1: false,
  234. isshowLoading2: false,
  235. isshowLoading3: false,
  236. isshowLoading4: false,
  237. isshowLoading5: false,
  238. tiptext: '正在导出请稍等……',
  239. formItemLayout: {
  240. labelCol: { span: 6 },
  241. wrapperCol: { span: 18 },
  242. },
  243. formItemLayout2: {
  244. labelCol: { span: 8 },
  245. wrapperCol: { span: 16 },
  246. },
  247. searchForm: {
  248. timeType: '1',
  249. timeArr: [],
  250. unitArr: [],
  251. },
  252. // 单位类型
  253. isUnitType: 'unit',
  254. linebarSearchForm: {
  255. modelType: '1',
  256. eventTag: [],
  257. modelField: [],
  258. },
  259. areaType: '',
  260. lineUnit: [],
  261. barSearchForm: {
  262. unitName: '',
  263. searchType: 'unit',
  264. },
  265. // 模型类型
  266. // 1定位型-整改类 2定位型-关注类
  267. modelTypeOption: [
  268. {
  269. key: '1',
  270. value: '定位型-整改类',
  271. },
  272. {
  273. key: '2',
  274. value: '定位型-关注类',
  275. },
  276. ],
  277. targetKeys: [],
  278. // 模型领域
  279. areaOption: [],
  280. rootNodeData: [],
  281. exportType: '1',
  282. visibleExport: false,
  283. radioStyle: {
  284. display: 'block',
  285. height: '30px',
  286. lineHeight: '30px',
  287. },
  288. eventList: [],
  289. // 整改单位类型
  290. zgSumType: true,
  291. visibleZb: false,
  292. mockData: [],
  293. targetKeysList: [],
  294. unitCodeList: [],
  295. chart1: null,
  296. chart2: null,
  297. chart3: null,
  298. chart4: null,
  299. barishow1: true,
  300. barishow2: true,
  301. // 下钻code
  302. downCode: '',
  303. // 是否有数据
  304. }
  305. },
  306. watch: {
  307. zgSumType(val) {
  308. // 切换单位和领域
  309. // 清空单位或者领域
  310. if (val) {
  311. this.areaType = ''
  312. } else {
  313. this.lineUnit = []
  314. }
  315. this.initRectifyRate()
  316. },
  317. },
  318. mounted() {
  319. this.getAreaOption()
  320. this.initSearchData()
  321. AuditRiskbraryService.getGroups().then((res) => {
  322. this.isUnitType = 'group'
  323. this.mockData = res.data.data.map((item) => ({
  324. key: item.code + '',
  325. title: item.name,
  326. description: item.description,
  327. }))
  328. // 如果长度为0,则显示二级单位
  329. if (this.mockData.length === 0) {
  330. this.getUserUnitCode()
  331. this.isUnitType = 'unit'
  332. }
  333. this.getMinxLinerBarData()
  334. this.initRectifyRate()
  335. this.initProblemSummary()
  336. this.initModelField()
  337. })
  338. // 尺寸变化
  339. window.addEventListener('resize', () => {
  340. this.chart1 && this.chart1.resize()
  341. this.chart2 && this.chart2.resize()
  342. this.chart3 && this.chart3.resize()
  343. this.chart4 && this.chart4.resize()
  344. })
  345. },
  346. methods: {
  347. initSearchData() {
  348. // 开始时间为当年1月1日 结束时间为当前时间
  349. this.searchForm.timeArr = [moment().startOf('year'), moment()]
  350. // 初始化集团
  351. auditMaintainService.getEventTagList().then((res) => {
  352. this.eventList = res.data
  353. })
  354. },
  355. changecompany(val) {
  356. this.searchForm.unitArr = val
  357. },
  358. zbOk() {
  359. this.visibleZb = false
  360. this.searchForm.zb = this.targetKeys
  361. this.targetKeysList = this.targetKeys
  362. },
  363. focusZb() {
  364. this.visibleZb = true
  365. this.$refs.zb.blur()
  366. AuditRiskbraryService.getGroups().then((res) => {
  367. this.isUnitType = 'group'
  368. this.mockData = res.data.data.map((item) => ({
  369. key: item.code + '',
  370. title: item.name,
  371. description: item.description,
  372. }))
  373. // 如果长度为0,则显示二级单位
  374. if (this.mockData.length === 0) {
  375. this.getUserUnitCode()
  376. this.isUnitType = 'unit'
  377. }
  378. })
  379. },
  380. // 获取当前用户单位,并且以当前单位为根节点
  381. getUserUnitCode() {
  382. const userInfo = getUserInfo()
  383. DataService.getCompany(userInfo.account).then((res) => {
  384. this.rootNodeData = {
  385. code: res.data.id,
  386. name: res.data.name,
  387. orgCode: res.data.code,
  388. }
  389. })
  390. },
  391. handleChangeZb(nextTargetKeys, direction, moveKeys) {
  392. this.targetKeys = nextTargetKeys
  393. },
  394. // getGroups 获取组织
  395. // 筛选部分
  396. getAreaOption() {
  397. auditMaintainService.getAreaListAll().then((res) => {
  398. this.areaOption = res.data
  399. })
  400. },
  401. // 搜索
  402. search(type) {
  403. if (type === 'all') {
  404. this.getMinxLinerBarData()
  405. this.initRectifyRate()
  406. this.initProblemSummary()
  407. this.initModelField()
  408. }
  409. if (type === 'lineBer') {
  410. this.getMinxLinerBarData()
  411. }
  412. if (type === 'line') {
  413. this.initRectifyRate()
  414. }
  415. },
  416. // 重置
  417. resetSearch() {
  418. this.searchForm = {
  419. timeType: '1',
  420. timeArr: [moment().startOf('year'), moment()],
  421. unitArr: [],
  422. }
  423. this.targetKeysList = []
  424. this.linebarSearchForm = {
  425. modelType: '1',
  426. eventTag: [],
  427. modelField: [],
  428. }
  429. this.search('all')
  430. },
  431. // onExportEvent 导出
  432. onExportEvent() {
  433. this.visibleExport = false
  434. // 获取导出类型
  435. this.isshowLoading5 = true
  436. const params = this.getSearchData('lineBar')
  437. // 如果下钻code
  438. // 如果是事件标签
  439. if (this.exportType === '1') {
  440. AuditRiskbraryService.exportBoardLine(params)
  441. .then((res) => {
  442. if (res.status === 200) {
  443. const url = URL.createObjectURL(res.data)
  444. const filename = res.headers['content-disposition']
  445. const fname = filename.substring(filename.indexOf('filename=') + 9, filename.length)
  446. download(url, decodeURI(fname))
  447. // 导出成功
  448. message.success('导出成功')
  449. } else {
  450. Modal.warning({
  451. title: '提示',
  452. content: '导出报错,请联系管理员!',
  453. })
  454. return false
  455. }
  456. })
  457. .finally(() => {
  458. this.isshowLoading5 = false
  459. })
  460. }
  461. if (this.exportType === '2') {
  462. AuditRiskbraryService.resPersonExport(params)
  463. .then((res) => {
  464. if (res.status === 200) {
  465. const url = URL.createObjectURL(res.data)
  466. const filename = res.headers['content-disposition']
  467. const fname = filename.substring(filename.indexOf('filename=') + 9, filename.length)
  468. download(url, decodeURI(fname))
  469. // 导出成功
  470. this.isshowLoading5 = false
  471. message.success('导出成功')
  472. } else {
  473. this.isshowLoading5 = false
  474. Modal.warning({
  475. title: '提示',
  476. content: '导出报错,请联系管理员!',
  477. })
  478. return false
  479. }
  480. })
  481. .finally(() => {
  482. this.isshowLoading5 = false
  483. })
  484. }
  485. },
  486. // 获取筛选条件
  487. getSearchData(type = 'all', xzCode = '') {
  488. const params = {}
  489. const timeArr = this.searchForm.timeArr.map((item) =>
  490. item ? moment(item).format('YYYY-MM-DD') : ''
  491. )
  492. // 时间类型
  493. if (this.searchForm.timeType === '1') {
  494. params.pushDateSt = timeArr[0]
  495. params.pushDateEd = timeArr[1]
  496. } else {
  497. // 转化为时间戳
  498. params.bussTimeSt = moment(timeArr[0]).valueOf()
  499. params.bussTimeEd = moment(timeArr[1]).valueOf()
  500. }
  501. // 是否是集团
  502. if (this.isUnitType === 'group') {
  503. params.unitCodeList = this.targetKeysList
  504. params.unitCode = xzCode
  505. if (this.downCode) {
  506. params.unitCode = this.downCode
  507. }
  508. } else {
  509. params.unitCodeList = this.searchForm.unitArr.map((item) => item.code)
  510. }
  511. // linebar筛选项
  512. if (type === 'lineBar') {
  513. params.modelType = this.linebarSearchForm.modelType
  514. params.eventTagList = this.linebarSearchForm.eventTag
  515. params.modelDomainList = this.linebarSearchForm.modelField
  516. }
  517. if (type === 'line') {
  518. params.unitName = this.lineUnit.length > 0 ? this.lineUnit[0].name : ''
  519. params.dimension = this.zgSumType ? 0 : 1
  520. params.domainName = this.areaOption.find((item) => item.id === this.areaType)?.name || ''
  521. }
  522. if (type === 'bar') {
  523. }
  524. return params
  525. },
  526. getMinxLinerBarData(type, xzCode = '') {
  527. this.isshowLoading1 = true
  528. AuditRiskbraryService.reformBoard(this.getSearchData('lineBar', xzCode))
  529. .then((res) => {
  530. const data = res.data.data
  531. const xAxisData = data.map((item) => item.unitName)
  532. // 整改率
  533. const seriesData = data.map((item) => ((item.reformRate || 0) * 100).toFixed(2))
  534. // 期限内已整改
  535. const seriesData1 = data.map((item) => ({ value: item.status0, ...item }))
  536. // 逾期已整改
  537. const seriesData2 = data.map((item) => item.status1)
  538. // 期限内未整改已完成
  539. const seriesData3 = data.map((item) => item.status2)
  540. // 逾期未整改完成
  541. const seriesData4 = data.map((item) => item.status3)
  542. // 已查看
  543. const seriesData5 = data.map((item) => item.viewCount)
  544. // 未查看
  545. const seriesData6 = data.map((item) => item.nonViewCount)
  546. this.unitCodeList = data.map((item) => item.unitCode)
  547. this.initMixLineBar(
  548. xAxisData,
  549. seriesData,
  550. seriesData1,
  551. seriesData2,
  552. seriesData3,
  553. seriesData4,
  554. seriesData5,
  555. seriesData6,
  556. type
  557. )
  558. })
  559. .finally(() => {
  560. this.isshowLoading1 = false
  561. })
  562. },
  563. initMixLineBar(
  564. xAxisData,
  565. seriesData,
  566. seriesData1,
  567. seriesData2,
  568. seriesData3,
  569. seriesData4,
  570. seriesData5,
  571. seriesData6,
  572. type
  573. ) {
  574. const myChart = echarts.init(document.getElementById('linebar'))
  575. const colorList = [
  576. '#fac858',
  577. '#5470c6',
  578. '#91cc75',
  579. '#fac858',
  580. '#ee6666',
  581. '#1890ff',
  582. '#a90000',
  583. ]
  584. const option = {
  585. height: 500,
  586. tooltip: {
  587. trigger: 'axis',
  588. // 调整颜色
  589. backgroundColor: 'rgba(255,255,255,1)',
  590. textStyle: {
  591. color: '#000',
  592. },
  593. // 字体颜色
  594. // 黑色
  595. formatter: function(params) {
  596. const paramsMap = params.map((item) => {
  597. return {
  598. seriesName: item.seriesName,
  599. value: item.value,
  600. }
  601. })
  602. if (params[1].data.modelType === 2 || params[1].data.modelType === '2') {
  603. paramsMap.push({
  604. seriesName: '已查看',
  605. value: params[1].data.viewCount,
  606. })
  607. paramsMap.push({
  608. seriesName: '未查看',
  609. value: params[1].data.nonViewCount,
  610. })
  611. }
  612. let str =
  613. '<div style="text-align:left;font-size:18px;margin-bottom: 10px">' +
  614. params[0].name +
  615. '<br></div>'
  616. paramsMap.forEach((item, index) => {
  617. // 如果名字是整改率
  618. str += `
  619. <div style='display:flex;justify-content: space-between;'>
  620. <div style='display:flex;align-items: center;'>
  621. <div style="display:flex;align-items: center;width: '200px';color:'#333'">
  622. <div style='margin-right:5px;width:${
  623. item.seriesName === '整改率' ? '10px' : '20px'
  624. };height:10px;border-radius:${
  625. item.seriesName === '整改率' ? '50%' : '2px'
  626. };background:${colorList[index]}'></div>
  627. ${item.seriesName}
  628. </div>
  629. </div>
  630. ${item.value} ${item.seriesName === '整改率' ? '%' : '条'}
  631. </div>
  632. `
  633. })
  634. return str
  635. },
  636. },
  637. grid: {
  638. top: 100,
  639. left: '1%', // grid布局设置适当调整避免X轴文字只能部分显示
  640. right: '5%', // grid布局设置适当调整避免X轴文字只能部分显示
  641. bottom: '5%',
  642. containLabel: true,
  643. },
  644. toolbox: {
  645. feature: {
  646. restore: { show: true },
  647. saveAsImage: { show: true },
  648. },
  649. },
  650. // x轴滚动
  651. dataZoom: [
  652. {
  653. type: 'slider',
  654. show: true,
  655. xAxisIndex: [0],
  656. start: 0,
  657. end: 35,
  658. height: 10,
  659. },
  660. {
  661. type: 'inside',
  662. xAxisIndex: [0],
  663. start: 0,
  664. end: 35,
  665. height: 10,
  666. },
  667. ],
  668. title: {
  669. subtext: `注:整改率表示该公司已提交整改材料并经审批通过的问题数(含期限内已整改的问题数)占全部问题数的比例,
  670. 整改率=100%*(期限内已整改问题数 + 逾期已整改问题数) / (期限内已整改问题数 + 逾期已整改问题数 + 期限内未整改问题数 + 逾期未整改问题数))`,
  671. left: 'center',
  672. top: '20px',
  673. itemGap: 10,
  674. subtextStyle: {
  675. fontSize: 14,
  676. },
  677. },
  678. legend: {
  679. show: true,
  680. data: ['整改率', '期限内已整改', '逾期已整改', '期限内未整改完成', '逾期未整改完成'],
  681. // 图例距离图表位置
  682. top: 0,
  683. // 图例颜色
  684. },
  685. color: ['#fac858', '#5470c6', '#91cc75', '#fac858', '#ee6666'],
  686. xAxis: [
  687. {
  688. type: 'category',
  689. data: xAxisData,
  690. axisLabel: {
  691. // 字体大小16
  692. show: true, // 是否显示刻度标签,默认显示
  693. interval: 0, // 坐标轴刻度标签的显示间隔,在类目轴中有效;默认会采用标签不重叠的策略间隔显示标签;可以设置成0强制显示所有标签;如果设置为1,表示『隔一个标签显示一个标签』,如果值为2,表示隔两个标签显示一个标签,以此类推。
  694. inside: false, // 刻度标签是否朝内,默认朝外
  695. margin: 6, // 刻度标签与轴线之间的距离
  696. // formatter: '{value}', // 刻度标签的内容格式器
  697. fontSize: 16,
  698. rotate: -15, // 刻度标签旋转的角度
  699. },
  700. },
  701. ],
  702. yAxis: [
  703. {
  704. type: 'value',
  705. name: '整改率',
  706. min: 0,
  707. interval: 10,
  708. axisLabel: {
  709. formatter: '{value} %',
  710. },
  711. },
  712. {
  713. type: 'value',
  714. name: '问题数据量',
  715. min: 0,
  716. axisLabel: {
  717. formatter: '{value} 条',
  718. },
  719. },
  720. ],
  721. series: [
  722. {
  723. name: '整改率',
  724. type: 'line',
  725. yAxisIndex: 0,
  726. tooltip: {
  727. valueFormatter: function(value) {
  728. return value + '%'
  729. },
  730. },
  731. data: seriesData,
  732. barWidth: 25,
  733. },
  734. {
  735. name: '期限内已整改',
  736. type: 'bar',
  737. yAxisIndex: 1,
  738. // 宽度
  739. tooltip: {
  740. valueFormatter: function(value) {
  741. return value + ' 条'
  742. },
  743. },
  744. // 堆叠
  745. stack: 'a',
  746. data: seriesData1,
  747. barWidth: 25,
  748. },
  749. {
  750. name: '逾期已整改',
  751. barWidth: 25,
  752. type: 'bar',
  753. yAxisIndex: 1,
  754. tooltip: {
  755. valueFormatter: function(value) {
  756. return value + ' 条'
  757. },
  758. },
  759. stack: 'a',
  760. // data: [2.6, 5.9, 9.0, 26.4, 28.7, 70.7, 175.6, 182.2, 48.7, 18.8, 6.0, 2.3],
  761. data: seriesData2,
  762. },
  763. {
  764. name: '期限内未整改完成',
  765. barWidth: 25,
  766. type: 'bar',
  767. yAxisIndex: 1,
  768. tooltip: {
  769. valueFormatter: function(value) {
  770. return value + ' 条'
  771. },
  772. },
  773. stack: 'a',
  774. // data: [2.0, 2.2, 3.3, 4.5, 6.3, 10.2, 20.3, 23.4, 23.0, 16.5, 12.0, 6.2],
  775. data: seriesData3,
  776. },
  777. {
  778. name: '逾期未整改完成',
  779. type: 'bar',
  780. barWidth: 25,
  781. yAxisIndex: 1,
  782. tooltip: {
  783. valueFormatter: function(value) {
  784. return value + ' 条'
  785. },
  786. },
  787. stack: 'a',
  788. // data: [2.6, 5.9, 9.0, 26.4, 28.7, 70.7, 175.6, 182.2, 48.7, 18.8, 6.0, 2.3],
  789. data: seriesData4,
  790. },
  791. ],
  792. }
  793. if (type === 'down') {
  794. option.graphic = [
  795. {
  796. type: 'text',
  797. left: 50,
  798. top: 20,
  799. style: {
  800. text: '返回',
  801. fontSize: 18,
  802. fontWeight: 700,
  803. },
  804. onclick: () => {
  805. // 删除返回
  806. this.downCode = ''
  807. this.getMinxLinerBarData()
  808. },
  809. },
  810. ]
  811. } else {
  812. option.graphic = [
  813. {
  814. type: 'text',
  815. left: 50,
  816. top: 20,
  817. style: {
  818. text: '',
  819. fontSize: 18,
  820. },
  821. },
  822. ]
  823. }
  824. myChart.setOption(option)
  825. // 修改unitCodeList 为全局变量
  826. // 可以下钻并且是集团
  827. if (type !== 'down' && this.isUnitType === 'group') {
  828. myChart.on('click', (params) => {
  829. this.downCode = this.unitCodeList[params.dataIndex]
  830. const unitCode = this.unitCodeList[params.dataIndex]
  831. this.getMinxLinerBarData('down', unitCode)
  832. })
  833. } else {
  834. myChart.off('click')
  835. }
  836. this.chart1 = myChart
  837. },
  838. // 获取整改率统计数据
  839. initRectifyRate(type, xzCode) {
  840. this.isshowLoading2 = true
  841. AuditRiskbraryService.reformRateStatistics(this.getSearchData('line', xzCode))
  842. .then((res) => {
  843. const colors = ['#91cc75']
  844. const data = res.data.data.map((item, i) => {
  845. return {
  846. value: ((item.reformRate || 0) * 100).toFixed(2),
  847. name: this.zgSumType ? item.unitName : item.domainName,
  848. code: item.unitCode,
  849. itemStyle: {
  850. color: colors[i],
  851. },
  852. }
  853. })
  854. const myChart = echarts.init(document.getElementById('rectifyRate'))
  855. const option = {
  856. color: colors,
  857. tooltip: {
  858. trigger: 'axis',
  859. axisPointer: {
  860. type: 'cross',
  861. label: {
  862. backgroundColor: '#6a7985',
  863. },
  864. },
  865. },
  866. legend: {
  867. data: ['整改率'],
  868. },
  869. toolbox: {
  870. feature: {
  871. saveAsImage: {},
  872. },
  873. },
  874. grid: {
  875. left: '1%', // grid布局设置适当调整避免X轴文字只能部分显示
  876. right: '5%', // grid布局设置适当调整避免X轴文字只能部分显示
  877. bottom: '5%',
  878. containLabel: true,
  879. },
  880. xAxis: {
  881. type: 'category',
  882. data: data.map((item) => item.name),
  883. axisLabel: {
  884. show: true, // 是否显示刻度标签,默认显示
  885. interval: 0, // 坐标轴刻度标签的显示间隔,在类目轴中有效;默认会采用标签不重叠的策略间隔显示标签;可以设置成0强制显示所有标签;如果设置为1,表示『隔一个标签显示一个标签』,如果值为2,表示隔两个标签显示一个标签,以此类推。
  886. inside: false, // 刻度标签是否朝内,默认朝外
  887. margin: 6, // 刻度标签与轴线之间的距离
  888. fontSize: 16,
  889. rotate: -15, // 刻度标签旋转的角度
  890. },
  891. },
  892. yAxis: {
  893. type: 'value',
  894. interval: 10,
  895. axisLabel: {
  896. formatter: '{value} %',
  897. },
  898. },
  899. // x轴滚动
  900. dataZoom: [
  901. {
  902. type: 'slider',
  903. show: true,
  904. xAxisIndex: [0],
  905. start: 0,
  906. height: 10,
  907. end: 20,
  908. },
  909. {
  910. type: 'inside',
  911. xAxisIndex: [0],
  912. start: 0,
  913. height: 10,
  914. end: 20,
  915. },
  916. ],
  917. series: [
  918. {
  919. name: '整改率',
  920. type: 'bar',
  921. barWidth: 25,
  922. data: data,
  923. },
  924. ],
  925. }
  926. if (type === 'down') {
  927. option.graphic = [
  928. {
  929. type: 'text',
  930. left: 50,
  931. top: 20,
  932. style: {
  933. text: '返回',
  934. fontSize: 18,
  935. // 加粗
  936. fontWeight: 700,
  937. },
  938. onclick: () => {
  939. // 删除返回
  940. // 清空下钻code
  941. this.initRectifyRate()
  942. },
  943. },
  944. ]
  945. } else {
  946. option.graphic = [
  947. {
  948. type: 'text',
  949. left: 50,
  950. top: 20,
  951. style: {
  952. text: '',
  953. fontSize: 18,
  954. },
  955. },
  956. ]
  957. }
  958. myChart.setOption(option)
  959. // 点击下钻
  960. if (type !== 'down' && this.isUnitType === 'group') {
  961. myChart.on('click', (params) => {
  962. this.initRectifyRate('down', data[params.dataIndex].code)
  963. })
  964. }
  965. this.chart2 = myChart
  966. })
  967. .finally(() => {
  968. this.isshowLoading2 = false
  969. })
  970. },
  971. initProblemSummary(type) {
  972. this.isshowLoading3 = true
  973. // questionNatureStatistics
  974. AuditRiskbraryService.questionNatureStatistics(this.getSearchData('bar'))
  975. .then((res) => {
  976. const list = res.data.data
  977. // 如果没有数据则
  978. // 总数
  979. const sum = list.reduce((prev, cur) => {
  980. return prev + cur.count
  981. }, 0)
  982. // 计算占比
  983. const data = list.map((item) => {
  984. return {
  985. value: ((item.count / sum) * 100).toFixed(2),
  986. name: item.questionNature,
  987. }
  988. })
  989. if (data.length === 0) {
  990. this.barishow1 = false
  991. }
  992. const myChart = echarts.init(document.getElementById('problemSummary'))
  993. const option = {
  994. tooltip: {
  995. trigger: 'item',
  996. formatter: '{a} <br/>{b} : {c} ({d}%)',
  997. },
  998. series: [
  999. {
  1000. name: '问题概括统计',
  1001. type: 'pie',
  1002. radius: '55%',
  1003. center: ['50%', '45%'],
  1004. data: data,
  1005. label: {
  1006. formatter: `{b}
  1007. {d}%`,
  1008. },
  1009. itemStyle: {
  1010. normal: {
  1011. color: function(params) {
  1012. // 自定义颜色
  1013. var colorList = [
  1014. '#fc8251',
  1015. '#5470c6',
  1016. '#9A60B4',
  1017. '#ef6567',
  1018. '#f9c956',
  1019. '#3BA272',
  1020. ]
  1021. return colorList[params.dataIndex]
  1022. },
  1023. },
  1024. },
  1025. },
  1026. ],
  1027. }
  1028. myChart.setOption(option)
  1029. this.chart3 = myChart
  1030. })
  1031. .finally(() => {
  1032. this.isshowLoading3 = false
  1033. })
  1034. },
  1035. initModelField() {
  1036. this.isshowLoading4 = true
  1037. AuditRiskbraryService.modelDomainStatistics(this.getSearchData('bar'))
  1038. .then((res) => {
  1039. const list = res.data.data
  1040. // 总数
  1041. const sum = list.reduce((prev, cur) => {
  1042. return prev + cur.count
  1043. }, 0)
  1044. // 计算占比
  1045. const data = list.map((item) => {
  1046. return {
  1047. value: ((item.count / sum) * 100).toFixed(2),
  1048. name: item.domainName,
  1049. }
  1050. })
  1051. if (data.length === 0) {
  1052. this.barishow2 = false
  1053. }
  1054. const myChart = echarts.init(document.getElementById('modelField'))
  1055. const option = {
  1056. tooltip: {
  1057. trigger: 'item',
  1058. formatter: '{a} <br/>{b} : {d}%',
  1059. },
  1060. series: [
  1061. {
  1062. name: '模型领域统计',
  1063. type: 'pie',
  1064. radius: '55%',
  1065. center: ['50%', '45%'],
  1066. data: data,
  1067. itemStyle: {
  1068. normal: {
  1069. color: function(params) {
  1070. // 自定义颜色
  1071. var colorList = [
  1072. '#fc8251',
  1073. '#5470c6',
  1074. '#9A60B4',
  1075. '#ef6567',
  1076. '#f9c956',
  1077. '#3BA272',
  1078. ]
  1079. return colorList[params.dataIndex]
  1080. },
  1081. },
  1082. },
  1083. label: {
  1084. formatter: `{b}
  1085. {d}%`,
  1086. },
  1087. },
  1088. ],
  1089. }
  1090. myChart.setOption(option)
  1091. this.chart4 = myChart
  1092. })
  1093. .finally(() => {
  1094. this.isshowLoading4 = false
  1095. })
  1096. },
  1097. // 饼图导出
  1098. exportPie1(type) {
  1099. this.isshowLoading5 = true
  1100. const params = this.getSearchData('bar')
  1101. AuditRiskbraryService.exportBoardBar(params)
  1102. .then((res) => {
  1103. if (res.status === 200) {
  1104. const url = URL.createObjectURL(res.data)
  1105. const filename = res.headers['content-disposition']
  1106. const fname = filename.substring(filename.indexOf('filename=') + 9, filename.length)
  1107. download(url, decodeURI(fname))
  1108. // 导出成功
  1109. message.success('导出成功')
  1110. } else {
  1111. Modal.warning({
  1112. title: '提示',
  1113. content: '导出报错,请联系管理员!',
  1114. })
  1115. return false
  1116. }
  1117. })
  1118. .finally(() => {
  1119. this.isshowLoading5 = false
  1120. })
  1121. },
  1122. exportPie2(type) {
  1123. this.isshowLoading5 = true
  1124. const params = this.getSearchData('bar')
  1125. AuditRiskbraryService.exportBoardBarModel(params)
  1126. .then((res) => {
  1127. if (res.status === 200) {
  1128. const url = URL.createObjectURL(res.data)
  1129. const filename = res.headers['content-disposition']
  1130. const fname = filename.substring(filename.indexOf('filename=') + 9, filename.length)
  1131. download(url, decodeURI(fname))
  1132. // 导出成功
  1133. message.success('导出成功')
  1134. } else {
  1135. Modal.warning({
  1136. title: '提示',
  1137. content: '导出报错,请联系管理员!',
  1138. })
  1139. return false
  1140. }
  1141. })
  1142. .finally(() => {
  1143. this.isshowLoading5 = false
  1144. })
  1145. },
  1146. exportBar() {
  1147. this.isshowLoading5 = true
  1148. const params = this.getSearchData('line')
  1149. AuditRiskbraryService.reformRateExport(params)
  1150. .then((res) => {
  1151. if (res.status === 200) {
  1152. const url = URL.createObjectURL(res.data)
  1153. const filename = res.headers['content-disposition']
  1154. const fname = filename.substring(filename.indexOf('filename=') + 9, filename.length)
  1155. download(url, decodeURI(fname))
  1156. // 导出成功
  1157. message.success('导出成功')
  1158. } else {
  1159. Modal.warning({
  1160. title: '提示',
  1161. content: '导出报错,请联系管理员!',
  1162. })
  1163. return false
  1164. }
  1165. })
  1166. .finally(() => {
  1167. this.isshowLoading5 = false
  1168. })
  1169. },
  1170. },
  1171. }
  1172. </script>
  1173. <style module lang="scss">
  1174. @use '@/common/design' as *;
  1175. .title {
  1176. margin-bottom: 20px;
  1177. }
  1178. .linebar {
  1179. width: 100%;
  1180. height: 650px;
  1181. margin-top: 20px;
  1182. }
  1183. .rectify-rate {
  1184. width: 100%;
  1185. height: 600px;
  1186. margin-top: 20px;
  1187. }
  1188. .bingtu {
  1189. width: 100%;
  1190. height: 400px;
  1191. margin-top: 20px;
  1192. }
  1193. .time {
  1194. display: flex;
  1195. align-items: center;
  1196. justify-content: flex-start;
  1197. }
  1198. .line-bar-body {
  1199. }
  1200. .form-bar {
  1201. :global {
  1202. .ant-form-item-control-wrapper {
  1203. width: 200px;
  1204. }
  1205. }
  1206. }
  1207. .search-btn {
  1208. margin-bottom: 20px;
  1209. }
  1210. .export-body {
  1211. position: relative;
  1212. .export-icon {
  1213. position: absolute;
  1214. top: 25px;
  1215. right: 10%;
  1216. z-index: 1;
  1217. cursor: pointer;
  1218. }
  1219. }
  1220. .riskviewtitle {
  1221. // 左侧有竖线
  1222. position: relative;
  1223. padding-left: 15px;
  1224. margin-bottom: 15px;
  1225. font-weight: 800px;
  1226. }
  1227. .riskviewtitle::before {
  1228. position: absolute;
  1229. top: 50%; /* 竖线垂直居中 */
  1230. left: 0;
  1231. width: 4px; /* 竖线宽度 */
  1232. height: 80%; /* 高度与容器高度相同 */
  1233. content: ''; /* 伪元素需要content属性 */
  1234. background: #1890ff; /* 竖线颜色 */
  1235. border-radius: 10px;
  1236. transform: translateY(-50%); /* 对齐调整 */
  1237. }
  1238. </style>