毕设记录(一)(重制版)——医疗知识图谱实现智能问答与分析服务(前端) ...

诗林  金牌会员 | 2025-3-11 17:37:00 | 来自手机 | 显示全部楼层 | 阅读模式
打印 上一主题 下一主题

主题 679|帖子 679|积分 2037

前言

        本篇毕设是基于b站up主——每天都要机器学习的开源项目,以该项目的深度学习部分为毕设的核心部分,再自己搭建起了前端页面以及与后端部分交互的接口逻辑而完成的毕设成果。各位同道们如果对我的前后端构建想要有一个更加深入的了解,可以参照本文讲解进一步理顺逻辑,一共分为三篇。第一篇为前端界面的构建,第二篇为后端接口的逻辑实现,第三部分为前后端交互功能的实现。末了,开源作者在b站上在深度学习部分有对模型的练习及其代码逻辑的具体讲解,这一部分就可以去看作者的视频啦。
        由于已是半年再度回顾毕设,且第一次写下长篇的博客,博客展示的都是一个小部分的代码切片,大概有些地方比力跳跃大概难明,各位可以结合一下整体源码,在全局中理解一下,再返来看看,是否能加深理解。同时博客中也有许多可能紧张的地方没有被提及,而写下的内容也会存在讲解不清以及错误的地方,如有疏漏,若仍有疑惑,还请各位不吝见教,提出题目,我都会悉数改进。
up主开源项目地点:GitHub - wangle1218/KBQA-for-Diagnosis: Knowledge Graph,Question Answering System,基于知识图谱和向量检索的医疗诊断问答体系
up主b站视频讲解:
基于知识图谱的智能问答项目实战_哔哩哔哩_bilibili
我的毕设演示视频:
毕设演示|基于知识图谱实现简易医疗问答体系_哔哩哔哩_bilibili
我的毕设开源源码:(望各位同道们能够在参考的基础上开辟出一些新的功能哦)
wjh-1211/medical_system
一、情况搭建

        开源项目的深度学习情况是tensorflow1.14.0、keras2.2.5、cuda(找到和自己电脑相匹配的版本安装),版本一定要对应好,否则会有很多很多报错,具体的安装我参考的是如下博客Anaconda 安装后情况变量配置 超详微小白版_anaconda情况变量配置-CSDN博客
blog.csdn.net/Ps_hello/article/details/131696828
Anaconda3、TensorFlow和keras简单安装方法(较具体)_keras安装-CSDN博客
      按照步调验证安装成功后,我是在vscode中安装解释器,完成情况的配置。
      运行开源项目中文件夹build_kg下的build_kg_utils.py完成知识图谱的创建,构建时间较长,必要耐心等待。
二、前端界面构建

利用vue完成整个界面的构建,包罗(element-ui、axios、vuex、route、neo4j-driver、neovis.js等。末了两个包用来实现图谱的可视化功能)
项目目次结构

        Login为登陆界面、ChatBot为导航栏中的对话体系界面、NeoVisual对应导航栏中的图谱可视化界面、BackPlat对应导航栏中的后台管理界面、MainBoard为导航栏的展示界面。
项目整体界面

        前端部分实现的无非是与后端的联动,前端利用axios对后端提供的相应接口api举行请求,而后端将请求所必要的数据返回。我们在控制台中查看返回的结果,按照相应的层级结构将变量赋予给在vue的data中界说好的变量即可举行渲染展示。涉及到数据的请求都是按照以上的步调来操纵。
导航栏界面

1.由于我将导航栏设置在全局,也即点击相应的导航栏并不会改变导航栏的所在位置,因此必要将MainBoard界面的路由设置在最表面,内里嵌套三个子界面的路由,并在keep-alive标签中设置router-view路由。
  1.     <div>
  2.         <el-breadcrumb separator="|" class="bread">
  3.             <el-breadcrumb-item :to="{ path: '/' }" class="font" style="font-size:20px">
  4.                 <span>医疗知识图谱智能问答系统</span>
  5.             </el-breadcrumb-item>
  6.             <el-breadcrumb-item class="font">
  7.                 <router-link to="/main/chat">
  8.                     <span @click="change(1)" :class="{'highlight':index===1}">对话系统</span>
  9.                 </router-link>
  10.             </el-breadcrumb-item>
  11.             <el-breadcrumb-item class="font">
  12.                 <router-link to="/main/neovisual">
  13.                     <span @click="change(2)" :class="{'highlight':index===2}">图谱可视化</span>
  14.                 </router-link>
  15.             </el-breadcrumb-item>
  16.             <el-breadcrumb-item class="font" >
  17.                 <router-link to="/main/backplat">
  18.                     <span @click="change(3)" :class="{'highlight':index===3}">后台管理</span>
  19.                 </router-link>
  20.             </el-breadcrumb-item>
  21.             <span class="name">你好,{{username}}</span>
  22.         </el-breadcrumb>
  23.         <div v-if="isshow" class="welcome">{{ welcomeMessage }}  </div>
  24.         <keep-alive>
  25.             <router-view></router-view>
  26.         </keep-alive>
  27.     </div>
复制代码
2.是在登录界面跳转后,制作了“接待利用问答体系”字样,js逻辑如下。
  1.     computed: {  
  2.         welcomeMessage() {  
  3.             return this.welcomeChars.join(''); // join方法用于将数组(或一个类数组对象)的所有元素连接到一个字符串中,中间不是空格符
  4.         },  
  5.     },
  6.     mounted() {  
  7.         const message = "欢迎使用智能问答系统!";  
  8.         let index = 0;  
  9.         const interval = setInterval(() => {  
  10.         if (index < message.length) {  
  11.             this.welcomeChars.push(message[index]);  
  12.             index++;  
  13.         } else {  
  14.             clearInterval(interval); // 当所有字符都添加完后,清除定时器  ,一定要删除,否则会消耗内存
  15.         }  
  16.         }, 150);  
  17.     },  
复制代码
聊天主界面

1.要实现动态的将对话放置到聊天界面中,那么就要界说一个数组messages,内里界说两个变量,一个为text,用于存储文本,另一个为sender,该属性用于实现机器人和用户的样式转换,若当前值为robot,则样式class动态绑定robot;若为man,则绑定用户样式。每输入一个题目时,就会被压入messages.text中,v-for循环中每次都会重新渲染dom,因此每次都能将新获得的消息传入展示渲染到聊天界面上。
  1.     <div class="outer">
  2.         <div class="header">
  3.             <div class="header-title">
  4.                 <i class="el-icon-caret-left" title="返回" v-show="isshow" @click="backend"></i>
  5.                 <i class="el-icon-search" title="实体识别" @click="multishow=!multishow" v-show="!isshow"></i>
  6.                 <p class="title">医疗智能问答系统</p>
  7.                 <i class="el-icon-message" @click="shownews" title="消息">
  8.                     <div class="newscircle" v-show="newsnum">
  9.                         <span>{{newsnum}}</span>
  10.                     </div>
  11.                 </i>
  12.             </div>
  13.         </div>
  14.         <div class="show1" v-show="!isshow">
  15.             <div class="main" ref="dialogueContainer">
  16.                 <div class="screen-inner">
  17.                     <!--v-for每次都会渲染一次dom中的内容-->
  18.                     <div v-for="(message,index) in messages" :key="index" :class="message.sender==='robot'?'robot-dialogue':'man-dialogue'">
  19.                         <!-- 若动态进行绑定src,则需要将引入的图片放到data中,然后再引用变量 -->
  20.                         <img :src="message.sender==='robot' ? robotImg : manImg"
  21.                             :class="message.sender==='robot'?'robotinfo':'userinfo'">
  22.                         <div :class="message.sender==='robot'?'dialogue-text':'dialogue-input'">{{message.text}}</div>
  23.                         <i class="el-icon-chat-dot-square" title="评价" v-if="message.sender==='robot'" @click="comment(message)"></i>
  24.                     </div>
  25.                 </div>
  26.             </div>
  27.         </div>
  28.             <div class="submit">
  29.                 <textarea
  30.                     id="dialogue-input"
  31.                     @keydown.enter="submit"
  32.                     @keydown.enter.prevent
  33.                     v-model="notedata"
  34.                     placeholder="请输入您的问题,按Enter键提交">
  35.                 </textarea>
  36.             </div>
  37.     </div>
复制代码
  1.     data(){
  2.         return{
  3.             messages:[
  4.                 {text:'你好,欢迎使用医疗自助问答服务系统,你可以对疾病从定义、病因、预防、临床表现、相关病症、治疗方法、所属科室、治愈率、禁忌、治疗时间等方面向我提问,祝您身体健康!',
  5.                 sender:'robot'}
  6.             ],
  7.         }
  8.     },
  9.     submit(){
  10.       const text = this.notedata.replace(/\n/g, "")//将最后的回车空格去掉
  11.       const requestData = {sent:text}
  12.       this.$axios.get('http://127.0.0.1:5000/index',{
  13.           params:requestData
  14.       })
  15.       .then((res)=>{
  16.           console.log(res);
  17.           this.messages.push({text:this.notedata,sender:'man'})
  18.           //这个if是用来控制右上角动画的展示,讲述下一个功能将会用到
  19.           if(!(res.data.diseasename instanceof Array)){
  20.               this.diseasename = res.data.diseasename
  21.               this.updatenum(this.diseasename)
  22.               // diseasename用来控制实体识别结果动画的消失,将该效果持续三秒
  23.               setTimeout(()=>{
  24.                   this.diseasename = ''
  25.               },3000)
  26.           }
  27.           //间隔1s后再将后端返回的答案加入到messages中
  28.           setTimeout(()=>{
  29.               this.messages.push({text:res.data.reply,sender:'robot'})
  30.           },1000)
  31.          
  32.           this.notedata=''
  33.          
  34.       })
  35.     }
复制代码
  robot和man的样式一致,只是将位置置于了相反的方向,此处不再枚举展开。
  1. .show1{
  2.      .main{
  3.          width: 800px;
  4.          height: 410px;
  5.          max-height: 410px;
  6.          overflow-y:auto;
  7.          .screen-inner{
  8.              margin: 15px;
  9.              .robot-dialogue{
  10.                  width: 100%;//宽度一定要设置成100%,确保其拥有父元素的整个宽度
  11.                  // 添加 overflow: hidden; 主要是为了清除浮动(clear float)。在这种情况下,由于子元素使用了浮动,父元素不会自动扩展以包含浮动元素,可能导致布局问题。通过为父元素设置 overflow: hidden;,可以强制父元素包含其浮动子元素。
  12.                  // 它的工作原理是,设置 overflow: hidden; 的元素会创建一个 BFC(块级格式化上下文),BFC 会包含浮动元素并防止其溢出到父元素之外,从而解决了浮动元素导致的布局问题。
  13.                  overflow:hidden;
  14.                  margin-top: 15px;
  15.                  // 图片圆框样式
  16.                  .robotinfo{
  17.                      width: 35px;
  18.                      height: 35px;
  19.                      float: left;
  20.                      margin-right: 10px;
  21.                      border-radius: 15px;
  22.                  }
  23.                  .dialogue-text{
  24.                      max-width: 665px;//需要设置出最大长度,以避免文本太长将文本框撑开导致文本框样式不一致
  25.                      background-color: #fff;
  26.                      float: left;
  27.                      padding:10px;
  28.                      font-size: 14px;
  29.                      position:relative;
  30.                  }
  31.                  // 每个聊天框后面的小图标
  32.                  .el-icon-chat-dot-square{
  33.                      cursor: pointer;
  34.                      margin-left:5px;
  35.                      line-height: 35px;
  36.                  }
  37.                  // 在每个文本框前面添加一个小三角形样式
  38.                  .dialogue-text::before{
  39.                      position:absolute;
  40.                      left: -8px;
  41.                      content: '';
  42.                      border-right: 10px solid #FFF;
  43.                      border-top: 8px solid transparent;
  44.                      border-bottom: 8px solid transparent;
  45.                  }
  46.              }
  47. }
复制代码
2.右上角每次随着用户信息的输入而弹出的实体辨认结果对话框的效果是利用了vue中的动画,这里界说了diseasename变量,它不仅是展示的内容,同时也是控制动画效果进出的一个关键变量。对diseasename修改的方法仍然是在submit中,一旦提交,就获取相应的res变量举行相应的修改。

  1.     <!--每次实体识别后右上角会有一个同步的展示效果,这里用的是动画-->
  2.     <transition name="rec-on-right">
  3.         <!--根据v-if来控制动画框是否展示-->
  4.         <div class="information" v-if="diseasename">
  5.             <div class="firstline">
  6.                 <i class="el-icon-info"></i>
  7.                 <h4>实体识别结果</h4>            
  8.             </div>
  9.             <span>{{diseasename}}</span>
  10.         </div>      
  11.     </transition>
复制代码
注意,我们必要在transition中界说好动画的名字,然后在css样式中在这个名字后面加上enter-active和leave-active,在其中的animation引入keyframes关键帧。
  1. <style>
  2.   .show2{
  3.       width: 800px;
  4.       height: 545px;
  5.       background-color: white;
  6.       max-width: 800px;
  7.       max-height: 545px;
  8.       overflow-y:auto;  
  9.       .outer-box-card{
  10.           border-bottom: 5px solid;
  11.           .box-card:hover{
  12.               background-color: rgb(184, 182, 182);
  13.           }
  14.       }
  15.   }
  16. @keyframes slideInFromRight {
  17.     from {
  18.         transform: translateX(100%);
  19.     }
  20.     to {
  21.         transform: translateX(0);
  22.     }
  23. }
  24. @keyframes slideOutToLeft {
  25.     from {
  26.         transform: translateX(0);
  27.     }
  28.     to {
  29.         transform: translateX(100%);
  30.     }
  31. }
  32. .rec-on-right-enter-active {
  33.     animation: slideInFromRight 1s ease;
  34. }
  35. .rec-on-right-leave-active {
  36.     animation: slideOutToLeft 1s ease;
  37. }
  38. </style>
复制代码
3.左边的实体辨认和意图辨认功能界面代码如下,其出入效果也由动画实现,并且必要调用后端接口获取相应的辨认数据,其在js中界说的方法为getword(),在毕设记录(三)中会具体讲解。

部分样式在标签中给出,部分样式在style中给出,整个框的滑入滑出是通过动画实现的。 
  1.     <transition name="rec-in-left">
  2.         <div class="recognize" v-show="multishow">
  3.             <div class="title">识别检测功能</div>
  4.             <div class="searchbox">
  5.                 <div style="margin-top:10px;font-weight:bold">请在下面的文本框输入问句</div>
  6.                 <textarea cols="40" rows="10" style="resize:none;" v-model="multiword"></textarea>
  7.                 <el-button type="primary" @click="getword(0)">实体识别</el-button>
  8.                 <el-button type="primary" @click="getword(1)">意图识别</el-button>
  9.             </div>
  10.             <div class="answer">
  11.                 <div style="font-weight:bold">识别结果</div>
  12.                 <div class="realword" ref="showanswer" style="width:300px;height:150px;border:2px solid black;margin-left:15px;"></div>
  13.                 <el-button type="danger" style="margin-top:5px;" @click="multishow=!multishow">退出</el-button>
  14.             </div>
  15.         </div>            
  16.     </transition>
复制代码
  1. .recognize{
  2.     width: 340px;
  3.     height: 600px;
  4.     float: left;
  5.     border: 2px solid black;
  6.     .title{
  7.         border: 2px solid black;
  8.         text-align: center;
  9.         font-weight: bold;
  10.         height: 50px;
  11.         line-height: 50px;
  12.         margin-bottom:15px 0;
  13.         background-color: #d6d6d6;
  14.     }
  15.     .searchbox{
  16.         margin-top: 20px;
  17.         height: 240px;
  18.         text-align: center;
  19.         border: 2px solid black;
  20.     }
  21.     .answer{
  22.         margin-top: 40px;
  23.         text-align: center;
  24.         height: 240px;
  25.         border: 2px solid black;
  26.     }
  27. }
  28. @keyframes slideOutFromLeft{
  29.     from {
  30.         transform: translateX(-100%)
  31.     }
  32.     to{
  33.         transform: translateX(0)
  34.     }
  35. }
  36. @keyframes slideOutToRight{
  37.     from {
  38.         transform: translateX(0)
  39.     }
  40.     to{
  41.         transform:translateX(-100%)
  42.     }
  43. }
  44. .rec-in-left-enter-active{
  45.     animation:slideOutFromLeft 0.5s ease
  46. }
  47. .rec-in-left-leave-active{
  48.     animation:slideOutToRight 0.5s ease
  49. }
复制代码
图谱可视化界面

        获取数据实际上是利用输入的数据,将其组建成cypher语句。cypher语句的写法有很多,看个人必要实现哪类数据查询,可以去学习一下其它范例的cypher 查询语句,本项目只是用了最简单的语句举行展示。
        左边为图谱直接可视化,右图为图数据库转换成关系型数据库可视化,上方有相应的下拉选项框,其背后的逻辑是为了传递相应的值实现cypher语句的查询。

value、value1和value2各对应选项框中的一个具体内容的一个映射,可以查看源代码看到具体的标识。没有想到更好的写法,因此写成了三个if。每个if内里的p、r、q中箭头所指方向是不同的,以此来匹配neo4j数据库中的查询。各人可以研究一下neo4j中的不同图谱对应的查询语句,然后可以在前端中展示更过细的图谱查询结果哦。
  1.    <div class="header">
  2.        <el-form :model="formInline" >
  3.            <el-row class="demo-form-inline">
  4.                <el-form-item>
  5.                    <el-input v-model="formInline.input" placeholder="请输入疾病名称" style="width:240px;"></el-input>
  6.                    <el-select v-model="value2" placeholder="请选择实体类别" style="margin-left:5px;">
  7.                        <el-option
  8.                            v-for="(item,index) in options3"
  9.                            :key="index"
  10.                            :label="item.label"
  11.                            :value="item.value2">
  12.                        </el-option>
  13.                    </el-select>
  14.                    <el-select v-model="value" placeholder="请选择查询关系" style="margin-left:5px">
  15.                        <el-option
  16.                            v-for="(item,index) in options1"
  17.                            :key="index"
  18.                            :label="item.label"
  19.                            :value="item.value">
  20.                        </el-option>
  21.                    </el-select>
  22.                    <el-select v-model="value1" placeholder="请选择关联关系" style="margin-left:5px">
  23.                        <el-option
  24.                            v-for="(item,index) in options2"
  25.                            :key="index"
  26.                            :label="item.label"
  27.                            :value="item.value1">
  28.                        </el-option>
  29.                    </el-select>
  30.                </el-form-item>
  31.                <el-form-item class="btn">
  32.                    <el-button :disabled="isClicked" type="primary" icon="el-icon-search" @click="submit">搜索</el-button>
  33.                </el-form-item>
  34.            </el-row>
  35.        </el-form>                  
  36.    </div>
复制代码
  1. methods:{
  2.         submit () {
  3.             if(this.value===0){
  4.                 if(this.value1===0){
  5.                     // this.cypher = `MATCH(p:疾病)-[r]-(q) WHERE p.name='${this.formInline.input}' RETURN p,r,q`
  6.                     this.cypher = `MATCH(p:${this.value2})-[r]-(q) WHERE p.name='${this.formInline.input}' RETURN p,r,q`
  7.                 }
  8.                 else if(this.value1===1){
  9.                     this.cypher = `MATCH(p:${this.value2})-[r]->(q) WHERE p.name='${this.formInline.input}' RETURN p,r,q`
  10.                 }
  11.                 else if(this.value1===2){
  12.                     this.cypher = `MATCH(p:${this.value2})<-[r]-(q) WHERE p.name='${this.formInline.input}' RETURN p,r,q`
  13.                 }
  14.             }
  15.             else {
  16.                 if(this.value1===0){
  17.                     this.cypher = `MATCH(p:${this.value2})-[r:${this.value}]-(q) WHERE p.name='${this.formInline.input}' RETURN p,r,q`
  18.                 }
  19.                 else if(this.value1===1){
  20.                     this.cypher = `MATCH(p:${this.value2})-[r:${this.value}]->(q) WHERE p.name='${this.formInline.input}' RETURN p,r,q`
  21.                 }
  22.                 else if(this.value1===2){
  23.                     this.cypher = `MATCH(p:${this.value2})<-[r:${this.value}]-(q) WHERE p.name='${this.formInline.input}' RETURN p,r,q`
  24.                 }
  25.             }
  26.             
  27.             this.search()
  28.             .then((length) => {
  29.                 if(length){
  30.                     this.viz.renderWithCypher(this.cypher);
  31.                 }
  32.                 else {
  33.                     this.$message.error("当前疾病不存在该关系,请重新选择!");
  34.                     this.tableData=[]
  35.                 }
  36.             })
  37.         },
  38. }
复制代码
图谱直接可视化配置
        这里紧张展示js部分的配置,网上的参考资料较少,踩了一点坑,因为当时我参考的博客已经和现在neo4j的版本对应不上了,导致图谱中无法显现出正确的文字,干系的配置得按照我现在这样来写,才气正确的显示图谱中的文字。
        在html部分,必要自行界说一个div容器:
  1.     <div class="myDiv">
  2.         <div id="viz" ref="viz"></div>
  3.     </div>
复制代码
        要注意在script中引入两个包,末了举行如下配置
  1. <script>
  2. import NeoVis from 'neovis.js';//图谱可视化的包引入
  3. import neo4j from 'neo4j-driver'//图谱转换为关系型数据库包引入
  4. data(){
  5.     return{
  6.         viz:{}//定义一个viz对象
  7.     }
  8. },
  9. methods:{
  10.     draw(){
  11.             var config ={
  12.                 containerId: 'viz',
  13.                 neo4j: {
  14.                     serverUrl: 'bolt://localhost:7687',
  15.                     serverUser: 'neo4j',
  16.                     serverPassword: '自己的neo4j密码'
  17.                 },
  18.                 labels: {
  19.                     科室:{
  20.                         label: 'name',  // 节点显示的文字对应内容key
  21.                     },
  22.                     检查:{label: 'name'},
  23.                     疾病:{label: 'name'},
  24.                     症状:{label: 'name'},
  25.                     药企:{label: 'name'},
  26.                     药品:{label: 'name'},
  27.                     菜谱:{label: 'name'},
  28.                     食物:{label: 'name'},
  29.                 },
  30.                 relationships: {
  31.                     belongs_to: {label: "name"},
  32.                     acompany_with: {label: "name"},
  33.                     cure_department:{label: "name"},
  34.                     do_eat:{label: "name"},
  35.                     has_common_drug:{label: "name"},
  36.                     has_symptom:{label: "name"},
  37.                     need_check:{label: "name"},
  38.                     not_eat:{label: "name"},
  39.                     production:{label: "name"},
  40.                     recommand_drug:{label: "name"},
  41.                     recommand_recipes:{label: "name"},
  42.                 },
  43.                 visConfig: {
  44.                     edges: {
  45.                         arrows: {
  46.                             to: {enabled: true}
  47.                         }
  48.                     }
  49.                 },
  50.                 initialCypher: "MATCH (p)-[r]->(m) RETURN p, r, m limit 50"
  51.             }
  52.             this.viz = new NeoVis(config)
  53.             // console.log(this.viz);
  54.             this.viz.render()
  55.             // 点击完搜索全图之后 才能开启搜索功能,因为需要先渲染一下
  56.             this.isClicked = false            
  57.         },
  58.         //在点进这个页面之前,首先会加载一次,使其初始化显示出来
  59.         mounted(){
  60.             this.draw()
  61.         }   
  62. </script>
复制代码
图谱转换为关系型数据展示js配置
  1. methods:{
  2.   search(){
  3.        return new Promise((resolve,reject)=>{
  4.                 this.tableData = []
  5.                 const driver = neo4j.driver("bolt://localhost:7687",neo4j.auth.basic("neo4j","自己的密码"))
  6.                 const session = driver.session()
  7.                 session.run(this.cypher)
  8.                 .then((res)=>{
  9.                     console.log(res);
  10.                     this.tableData = []
  11.                     if(this.value1===0 || this.value1===1){
  12.                         for(let i=0;i<res.records.length;i++){
  13.                             this.tableData.push
  14.                             ({
  15.                                 //此处的层级结构在控制台中查看返回数据的层级结构可以获得
  16.                                 diseasename:res.records[i]._fields[0].properties.name,
  17.                                 relationship:this.englishToChinese[res.records[i]._fields[1].properties.name],
  18.                                 node:res.records[i]._fields[2].properties.name
  19.                             })
  20.                         }                        
  21.                     }
  22.                     // 成功时将长度值返回,使用resolve即可返回
  23.                     resolve(this.tableData.length)
  24.                 })
  25.             })
  26.         },
  27.         handleCurrent(val){
  28.             this.currentPage = val
  29.         }
  30. }      
复制代码
后台管理界面

这一部分做了三个小界面,分别是处理用户评价界面、疾病实体可视化统计界面以及用户管理界面。这里仅展示数据可视化界面,剩余两个界面可查看源码,写法比力固定。
该可视化界面制作了饼图、柱状图和词云,效果如下:



首先是利用el-table展示用户提问疾病的数据,在页面渲染阶段利用dom将数据记载出来即可。接着利用makeform和makeciyun分别来天生两种统计表。传递进去的参数为index,因两种范例的统计图共用一个页面,因此要用index来区分。
  1. <el-tab-pane label="疾病提问统计" name="second" class="second">
  2.      <el-table :data="diseasedata" style="width:300px" class="tablelist">
  3.          <el-table-column prop="id" label="序号" width="100" align="center"></el-table-column>
  4.          <el-table-column prop="diseasename" label="疾病名称" width="100" align="center"></el-table-column>
  5.          <el-table-column prop="num" label="提问次数" width="100" align="center"></el-table-column>
  6.      </el-table>
  7.      <el-button type="primary" @click="makeform(1)">生成统计图表</el-button>
  8.      <el-button type="primary" @click="makeciyun(2)">生成词云</el-button>
  9.      <div class="outer">
  10.          <div class="chart" v-show="index===1">
  11.              <div class="echart" id="echart"></div>
  12.              <div class="circle" id="circle"></div>                    
  13.          </div>
  14.          <div class="cloud" id="cloud" v-show="index===2"></div>
  15.      </div>
  16. </el-tab-pane>
复制代码
注意在loadnum中要向后端的数据库中请求提问的数据,分别填充到柱状图必要的数据——xData和yData以及pieData
  1. <script>
  2. import * as echarts from "echarts";
  3. import 'echarts-wordcloud'
  4. data(){
  5.     return{
  6.         xData:[],
  7.         yData:[],
  8.         pieData:[],
  9.     }
  10. },
  11. methods:{
  12. // 加载数据库中的第二个表到第二个table中
  13. loadnum(){
  14.      this.$axios.get('http://127.0.0.1:5002/getnum')
  15.      .then((res)=>{
  16.          for(let i=0;i<res.data.length;i++){
  17.              this.diseasedata.push(
  18.                  {
  19.                      id:res.data[i].id,
  20.                      diseasename:res.data[i].diseasename,
  21.                      num:res.data[i].num
  22.                  }
  23.              )
  24.              this.xData.push(res.data[i].diseasename)
  25.              this.yData.push(res.data[i].num)
  26.              this.pieData.push(
  27.                  {
  28.                      value:res.data[i].num,
  29.                      name:res.data[i].diseasename
  30.                  }
  31.              )
  32.          }
  33.      })
  34. },
  35. mounted(){
  36.         this.loadnum()
  37. }
  38. }
  39. </script>
复制代码
        饼图和柱状图的配置,具体的参数配置可见别的文档,大体意思可以看英文名,这里就不再赘述啦。
  1.   initEcharts(){
  2.       const option ={
  3.           xAxis:{
  4.               type:'category',
  5.               data:this.xData,
  6.               axisLabel:{
  7.                   show:true,
  8.                   formatter:function(value){
  9.                       return value.split("").join("\n");
  10.                   }
  11.               }
  12.           },
  13.           yAxis:{
  14.               type:'value'
  15.           },
  16.           series:[
  17.               {
  18.                   type:"bar",
  19.                   data:this.yData,
  20.                   label:{
  21.                       show:true,
  22.                       position:'top'
  23.                   },
  24.                   barWidth: 20,
  25.                   // barGap:'80%',
  26.                   // barCategoryGap:'50%',
  27.                   showBackground: true,
  28.                   // 将背景完整的补充上
  29.                   backgroundStyle: {
  30.                       color: 'rgba(180, 180, 180, 0.2)'
  31.                   },
  32.                   itemStyle:{
  33.                       normal:{
  34.                           color:function(){return "#"+Math.floor(Math.random()*(256*256*256-1)).toString(16);}
  35.                       }
  36.                   }
  37.               }
  38.           ]
  39.       };
  40.       const myChart = echarts.init(document.getElementById("echart"))
  41.       myChart.setOption(option)
  42.   },
  43.   initpieEcharts(){
  44.       const option ={
  45.           legend:{
  46.               data:this.xData,
  47.               right:"10%",
  48.               // top:"30%",
  49.               // orient:"vertical"
  50.           },
  51.           series:[
  52.               {
  53.                   type:"pie",
  54.                   label:{
  55.                       show:true,
  56.                       formatter:"{b}:{c}({d}%)"
  57.                   },
  58.                   data:this.pieData
  59.               }
  60.           ]
  61.       }
  62.       const mycircle = echarts.init(document.getElementById("circle"))
  63.       mycircle.setOption(option)
  64.   },
复制代码
注意,配置词云的数据必须是一个数组对象中包罗着名称和该名称出现的频次,这里继续利用pieData,刚好也是词云要用到的数据,词云配置时会主动读取该数据对象中的两个数据。
  1.    initciyuncharts(){
  2.        const option={
  3.            series:[
  4.                {
  5.                    type:'wordCloud',
  6.            //配置数据,一定要是一个数组对象,否则将无法正确配置,一开始只是传递了一个对象
  7.                    data:this.pieData,
  8.                    gridSize: 20,//用来调整词之间的距离
  9.                    sizeRange: [14, 60],//用来调整字的大小范围
  10.                    rotationRange: [-90,90],//设置词的旋转角度
  11.                    fontSizeRange: [5, 40],//设置字体大小
  12.                    textStyle:{
  13.                        normal:{
  14.                            color: function() {
  15.                                return 'rgb(' + [
  16.                                    Math.round(Math.random() * 160),
  17.                                    Math.round(Math.random() * 160),
  18.                                    Math.round(Math.random() * 160)
  19.                                ].join(',') + ')';
  20.                            }
  21.                        }
  22.                    }
  23.                }
  24.            ]
  25.        };
  26.        const cloudmap = echarts.init(document.getElementById('cloud'))
  27.        cloudmap.setOption(option)
  28.    },
复制代码



免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

诗林

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表