Vue+Element+Echarts+Springboot+微信小程序开发(肿瘤医学项目)
一、软件结构
MedicalProject
1.小程序账号 + 微信开发者工具 + 前端代码 + 后端数据 = 微信小程序
2.腾讯云服务器 + Tomcat + 后端接口定义 = 可访问的后端服务接口
3.Idea + Java + SpringBoot = PC后端接口定义
4.mysql+mongodb+neo4j = PC数据库
小程序端开发
小程序端开发其实就是在腾讯的开发工具里,使用js语言,遵循腾讯小程序的开发文档规范进行代码编写。开发过程中可以编译、预览、真机调试等,可以使用各种插件,可以调用一些公共的api或者自己定义的后端接口,也可以使用腾讯提供的云函数。
PC端开发
PC端主要是提供给小程序数据接口,让小程序各个页面都能有数据展示,这里提供的是http接口,返回的Json格式的字符串。使用的语言是Java,使用的框架是SpringBoot,程序将最终部署在web服务器Tomcat中,Tomcat就自动的将通过url过来的请求分发到程序代码的处理逻辑中,处理完请求拿到相应的数据则以Json字符串的格式返回。
环境:
开发语言:Java 8
开发集成环境:IntelliJ IDEA, VScode, 微信开发者工具
框架:Springboot, Vue.js,Element UI, ECharts
数据库:Mysql,MongoDB, Neo4j
构建工具:Maven
代码管理:华为云
分支管理:
Master: 主分支,存储软件发布版本。
develop: 存储软件稳定版本;各自分支代码开发完毕后合并到develop分支。
CDN服务
CDN的全称是Content Delivery Network,即内容分发网络。CDN是构建在现有网络基础之上的智能虚拟网络,依靠部署在各地的边缘服务器,通过中心平台的负载均衡、内容分发、调度等功能模块,使用户就近获取所需内容,降低网络拥塞,提高用户访问响应速度和命中率。CDN的关键技术主要有内容存储和分发技术。
CDN通过广泛的网络节点分布,提供快速、稳定、安全、可编程的全球内容分发加速服务,支持将网站、音视频、下载等内容分发至接近用户的节点,使用户可就近取得所需内容,提高用户访问的响应速度和成功率。
作者:阿里巴巴淘系技术
链接:https://www.zhihu.com/question/36514327/answer/1604554133
来源:知乎
CDN工作原理
内容分发网络(Content Delivery Network,简称CDN)是建立并覆盖在承载网之上,由分布在不同区域的边缘节点服务器群组成的分布式网络。
CDN应用广泛,支持多种行业、多种场景内容加速,例如:图片小文件、大文件下载、视音频点播、直播流媒体、全站加速、安全加速。
![](https://img2020.cnblogs.com/blog/1948390/202108/1948390-20210809201558222-896258795.jpg)
借用阿里云官网的例子,来简单介绍CDN的工作原理。
假设通过CDN加速的域名为www.a.com
,接入CDN网络,开始使用加速服务后,当终端用户(北京)发起HTTP请求时,处理流程如下:
- 当终端用户(北京)向
www.a.com
下的指定资源发起请求时,首先向LDNS(本地DNS)发起域名解析请求。 - LDNS检查缓存中是否有
www.a.com
的IP地址记录。如果有,则直接返回给终端用户;如果没有,则向授权DNS查询。 - 当授权DNS解析
www.a.com
时,返回域名CNAMEwww.a.tbcdn.com
对应IP地址。 - 域名解析请求发送至阿里云DNS调度系统,并为请求分配最佳节点IP地址。
- LDNS获取DNS返回的解析IP地址。
- 用户获取解析IP地址。
- 用户向获取的IP地址发起对该资源的访问请求。
- 如果该IP地址对应的节点已缓存该资源,则会将数据直接返回给用户,例如,图中步骤7和8,请求结束。
- 如果该IP地址对应的节点未缓存该资源,则节点向源站发起对该资源的请求。获取资源后,结合用户自定义配置的缓存策略,将资源缓存至节点,例如,图中的北京节点,并返回给用户,请求结束。
从这个例子可以了解到:
(1)CDN的加速资源是跟域名绑定的。
(2)通过域名访问资源,首先是通过DNS分查找离用户最近的CDN节点(边缘服务器)的IP
(3)通过IP访问实际资源时,如果CDN上并没有缓存资源,则会到源站请求资源,并缓存到CDN节点上,这样,用户下一次访问时,该CDN节点就会有对应资源的缓存了。
链接:https://www.cnblogs.com/yanggb/p/10822420.html
来源:博客园
npm与cnpm的区别
NPM(Node Package Manager,节点包管理器)是NodeJS的包管理器,用于节点插件的管理(包括安装,卸载和管理依赖等)。NPM是随同新版的NodeJS一起安装的包管理工具,所以我们需要安装NodeJS。
NPM的常见使用场景
1.允许用户从NPM服务器上下载别人编写的第三方包到本地使用。
2.允许用户从NPM服务器上下载并安装别人编写的命令行程序到本地使用。
3.允许用户将自己编写的包或命令行程序上传到NPM服务器上供别人使用。
为什么要用CNPM
NMP安装插件是从NPM官网下载对应的插件包,该网站的服务器在国外,经常会出现下载缓慢或出现异常,这时便需要找到另外的方法提供稳定的下载,这个方法就是CNPM。阿里巴巴的淘宝团队把NMP官网的插件都同步到了在中国的服务器,提供给我们从这个服务器上稳定下载资源。
CNMP同样是NMP的一个插件,要安装的话需要在CMD命令行控制台执行以下命令:
npm install cnpm -g --registry=https://registry.npm.taobao.org
安装完成后可以使用cnpm -v命令查看版本号,要使用cnmp命令的话最好在安装后重新打开CMD命令行控制台。
cnpm的用法和npm的用法一致,只是在执行命令的时候将npm改为cnpm。
前端工程化
Vue-cli
Vue脚手架指的是Vue-cli,它是一个专门为单页面应用快速搭建繁杂程序的脚手架,它可以轻松地创建新的应用程序而且可用于自动生成Vue和Webpack的项目模板。
利用Vue-cli脚手架构建Vue项目需要先安装Node.js和NPM环境。
Node.js
Node. js是一个基于Chrome V8引擎的JavaScript运行环境。N ode是一个让JavaScript运行在服务端的开发平台,它让JavaScript成为与PHP、 Python、Perl、Ruby等服务端语言平起平坐的脚本语言。
Node是一个基于Chrome JavaScript 运行时建立的平台,用于方便地搭建响应速度快、易于扩展的网络应用。Node使用事件驱动,采用非阻塞1I/O模型而得以轻量和高效,非常适合在分布式设备上运行数据密集型的实时应用。
NPM
NPM代表npmjs.org这个网站, 这个站点存储了很多Node.js的第三方功能包,NPM的全称是Node Package Manager,它是一个Node. js包管理和分发工具,已经成为为非官方的发布Node模块(包)的标准。它可以让JavaScript开发者能够更加轻松地共享代码和共用代码片段,并且通过NPM管理需要分享的代码也很方便、快捷和简单。
NPM是随同Node.js一起安装的包管理工具,能解决Node.js代码部署上的很多问题,常见的使用场景有以下几种:
(1)允许用户从NPM服务器下载别人编写的第三方包到本地使用。
(2)允许用户从NPM服务器下载并安装别人编写的命令行程序到本地使用。
(3)允许用户将自己编写的包或命令行程序上传到NPM服务器供别人使用。
基本使用
搭建完整的Vue_cli脚手架构建的项目
Vue-cli是用Node编写的命令行工具,需要进行全局安装。命令行终端输入:
npm install -g vue - cli
使用Vue-cli快速生成基于Webpack模板构建的项目。
配置完成后
进入项目目录,使用npm install安装依赖
依赖安装后,项目的目录结构
src目录
index.html文件
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>vue_medicalproject</title>
</head>
<body>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>
main.js文件// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import App from './App'
import router from './router'
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
components: { App },
template: '<App/>'
})
App.vue文件
<!--页面模板-->
<template>
<div id="app">
<img src="./assets/logo.png">
<router-view/>
</div>
</template>
<!--页面脚本-->
<script>
export default {
name: 'App'
}
</script>
<!--页面样式-->
<style>
#app {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
命令行npm run dev 启动项目
浏览器输入http://localhost:8080
项目打包与发布
控制台npm run build对Vue项目进行打包
生成dist打包文件
Element-ui
Vue的核心思想是组件和数据驱动,但是每一个组件都需要自己编写模板、样式、添加事件、数据等,这些工作是比较烦琐的,所以饿了公推出了基于Vue2.0的组件库,它的名称为Element-ui,此组件库提供了丰富的PC端组件。
ElementUI官网为http://element-cn. eleme. io/ # /zh-CN。
Vue项目中引入Element-ui组件库有两种方法:
1.CDN
在线方式直接在页面上引入Element-ui的JS和CSS文件即可开始使用
<!--引入样式-->
<link rel = "stylesheet" href = "https://unpkg.com/element-ui/lib/theme-chalk/index.css">
<!--引入组件库-->
<script src = "https://unpkg.com/element-ui/lib/index.js"></script>
2.NPM安装
使用NPM方式安装,可以更好地和webpack打包工具配合使用
npm install element - ui - S
NMP安装插件是从NPM官网下载对应的插件包,该网站的服务器在国外,经常会出现下载缓慢或出现异常,这时便需要找到另外的方法提供稳定的下载,这个方法就是CNPM。
cnpm i element-ui -S
Vue项目中集成Element-ui
在main.js中完整引入Element-ui组件库
main.js中引入Element-ui组件库
从Element-ui官网复制代码到First.vue文件
参考自:https://blog.csdn.net/weixin_43236610/article/details/82866518
vue项目新建页面
- 示例项目启动成功后,若新增页面,需新增.vue文件并配置路由
- 第一步:新增.vue文件
笔者在components目录下新增First.vue文件,内容如下
- 第一步:新增.vue文件
第二步:配置路由
Vue提供了一个属性mode:"history",可以去掉地址栏中的#,代码如下:
插入url地址与组件地址映射语句
{
path: '/first',
name: 'First',
component: First
}
第三步:打开浏览器,输入http://localhost:8080/first
3. 分析调用关系
- 上一节页面新建时,界面中有个意外的图标,引发思考
- 既然First.vue代码中只编写了 "welcome"相关代码,那么图标从何而来呢?
- 解决这个问题,需要分析vue文件中的调用关系
- 使用到vue项目的文件包括一个.html,两个.js,两个.vue文件,关系如上图所示
- 由图可见,文件关键处在于main.js,管理着所有需要的资源,其中new Vue的参数,解释如下:
el:官方解释为实例提供挂载的元素。此处为index.html中的<div id="app"><div>。
router:为router:router,的简写,指向引入文件中的routes:[]
components:注册哪些组件,需在顶部引入文件。
template:替换挂载元素的模板组件,而挂载元素的内容都将被忽略。即用template替换index.html里面的<div id="app"></div>
此时,可知main.js文件调用关系分为三步,如图中序号
1.确定将被挂载(替换)的元素,此处为index.html中的<div id="app"><div>。
2.注册组件(此处只有组件App),选择其中用于替换挂载元素(第一步中的元素)的模板组件(<App/>),即用App.vue替换index.html中的<div id="app"><div>。
3.注入路由器router:
1.模板组件(App.vue)中有<router-view/>,将在其中渲染路由匹配到的组件
2.注入(import)路由时指定的是router文件夹,即文件夹下所有routes
3.router文件夹下此时只有index.js文件,其中routes:[]规定了文件地址及其url地址映射
4.根据文件地址,载入组件(First.vue),组件被渲染在<router-view/>中,显示在index.html中
然而追本溯源,调用关系中仍有两个问题:
index.html为何默认显示?
其实执行npm run dev时,控制台将执行如下语句:
webpack-dev-server --inline --progress --config build/webpack.dev.conf.js
由此可见,运行时启动文件webpack.dev.conf.js,而文件中包含如下语句,规定了起始页面:
main.js为何默认加载?
因为使用的脚手架工具vue-cli里用webpack来打包项目文件,webpack.dev.conf文件里还定义了webpack基础配置文件webpack.base.conf.js,定义语句如下:
const baseWebpackConfig = require('./webpack.base.conf')
而文件webpack.base.conf.js中,包含如下语句,指定了入口:
文件调用关系完毕
Echarts可视化库
ECharts 是一个使用 JavaScript 实现的开源可视化库,涵盖各行业图表,满足各种需求。
ECharts 遵循 Apache-2.0 开源协议,免费商用。
ECharts 兼容当前绝大部分浏览器(IE8/9/10/11,Chrome,Firefox,Safari等)及兼容多种设备,可随时随地任性展示。
NPM 方法
由于 npm 安装速度慢,本教程使用了淘宝的镜像及其命令 cnpm,安装使用介绍参照:使用淘宝 NPM 镜像。
npm 版本需要大于 3.0,如果低于此版本需要升级它:
# 查看版本
$ npm -v
2.3.0
#升级 npm
cnpm install npm -g
# 升级或安装 cnpm
npm install cnpm -g
通过 cnpm 获取 echarts:
# 最新稳定版
$ cnpm install echarts --save
安装完成后 ECharts 和 zrender 会放在 node_modules 目录下,我们可以直接在项目代码中使用 require('echarts') 来使用。
- 引用echarts(两种方式,这里使用的是全局引用)
import echarts from 'echarts'
- 注册echarts组件
Vue.prototype.$echarts = echarts
引入echars5.0报错“export ‘default‘ (imported as ‘echarts‘) was not found in ‘echarts‘
原文链接:https://blog.csdn.net/weixin_43972437/article/details/111475106
问题
引入 echars 5.0 遇到报错 "export ‘default’ (imported as ‘echarts’) was not found in ‘echarts’
解决
引入方式改为
import * as echarts from 'echarts';
// 或
const echarts = require('echarts');
成功解决Component template should contain exactly one root element
原文链接:https://blog.csdn.net/qq_26230421/article/details/96426823
前言
报错信息:
VUE小白会碰到这样的错误,刚上手,对vue还不太熟悉,所以对里面的构造方式不太清楚。
对于这个bug,我们很容易翻译为:
组件模板应该只包含一个根元素
查看代码
如图所示,template下面包含了两个<div>根节点,这是报错的根本原因。
解决
在最外层写一个div,把所有的前端代码放进去就好了:
WebStorm开发Vue自定义标签提示是未知标签解决办法
1.file -> settings -> editor -> inspections -> html -> unkown html tag, unkown html tag attribute
2.打开 File -> Settings -> File Types 在右侧的窗口中找到Vue.js Template 并选中,在下面的窗口中添加 *.vue 即可解决问题。
实现简单的数据可视化页面
First.vue
<template> <el-container style="height: 550px; border: 1px solid #eee"> <el-header style="text-align: right; font-size: 12px"> <el-dropdown> <i class="el-icon-setting" style="margin-right: 15px"></i> <el-dropdown-menu slot="dropdown"> <el-dropdown-item>查看</el-dropdown-item> <el-dropdown-item>新增</el-dropdown-item> <el-dropdown-item>删除</el-dropdown-item> </el-dropdown-menu> </el-dropdown> <span>@MedicalProject</span> </el-header> <el-container> <el-aside width="150px" style="background-color: rgb(238, 241, 246)"> <el-menu :default-openeds="[]"> <el-submenu index="1"> <template slot="title"><i class="el-icon-message"></i>导航一</template> <el-menu-item-group> <template slot="title">分组一</template> <el-menu-item index="1-1">选项1</el-menu-item> <el-menu-item index="1-2">选项2</el-menu-item> </el-menu-item-group> <el-menu-item-group title="分组2"> <el-menu-item index="1-3">选项3</el-menu-item> </el-menu-item-group> <el-submenu index="1-4"> <template slot="title">选项4</template> <el-menu-item index="1-4-1">选项4-1</el-menu-item> </el-submenu> </el-submenu> <el-submenu index="2"> <template slot="title"><i class="el-icon-menu"></i>导航二</template> <el-menu-item-group> <template slot="title">分组一</template> <el-menu-item index="2-1">选项1</el-menu-item> <el-menu-item index="2-2">选项2</el-menu-item> </el-menu-item-group> <el-menu-item-group title="分组2"> <el-menu-item index="2-3">选项3</el-menu-item> </el-menu-item-group> <el-submenu index="2-4"> <template slot="title">选项4</template> <el-menu-item index="2-4-1">选项4-1</el-menu-item> </el-submenu> </el-submenu> <el-submenu index="3"> <template slot="title"><i class="el-icon-setting"></i>导航三</template> <el-menu-item-group> <template slot="title">分组一</template> <el-menu-item index="3-1">选项1</el-menu-item> <el-menu-item index="3-2">选项2</el-menu-item> </el-menu-item-group> <el-menu-item-group title="分组2"> <el-menu-item index="3-3">选项3</el-menu-item> </el-menu-item-group> <el-submenu index="3-4"> <template slot="title">选项4</template> <el-menu-item index="3-4-1">选项4-1</el-menu-item> </el-submenu> </el-submenu> </el-menu> </el-aside> <el-main> <!-- 为ECharts准备一个具备大小(宽高)的Dom--> <div> <div style="display: flex; margin-bottom: 10px" > <div style="background-color: #0F1033; width: 580px;height: 300px; display: flex;"> <div id="bar1" style="width: 280px;height:300px; background-color:#1F2056; align-content: center"></div> <div id="bar2" style="width: 280px;height:300px; background-color:#1F2056; align-content: center"></div> </div> <div style="background-color: #0F1033; width: 580px;height: 300px; display: flex;"> <div id="pie1" style="width: 580px;height:300px; background-color:#1F2056;display: flex;" ></div> </div> </div> <div style="display: flex; margin-top: 10px" > <div style="background-color: #0F1033; width: 580px;height: 300px; display: flex;"> <div id="set1" style="width: 280px;height:300px; background-color:#1F2056; align-content: center"></div> <div id="set2" style="width: 280px;height:300px; background-color:#1F2056; align-content: center"></div> </div> <div style="background-color: #0F1033; width: 580px;height: 300px; display: flex;"> <div id="set3" style="width: 580px;height:300px; background-color:#1F2056;display: flex;" ></div> </div> </div> </div> </el-main> </el-container> <el-footer>Footer</el-footer> </el-container> </template> <style scoped> .el-header, .el-footer { background-color: #B3C0D1; color: #333; text-align: center; line-height: 60px; } .el-main { background-color: #E9EEF3; color: #333; height: 600px; width: 1000px; text-align: center; line-height: 110px; } .el-aside { color: #333; } </style> <script> let echarts = require('echarts/lib/echarts'); export default { name: 'First.vue', data() { const item = { // date: '2016-05-02', // name: '@', // address: '上海市普陀区金沙江路 1518 弄' }; return { // tableData: Array(10).fill(item) } }, mounted() { this.drawbar1(); this.drawbar2(); this.drawpie1(); this.drawdataset1(); this.drawdataset2(); this.drawdataset3(); }, methods: { drawbar1() { // 基于准备好的dom,初始化echarts实例 let myChart1 = echarts.init(document.getElementById('bar1')); // 指定图表的配置项和数据 let option = { title: { text: '第一个 ECharts 实例', textStyle: { fontSize: 15, color: "#FBFBFD" } }, tooltip: {}, legend: { data: [ { name: '销量', textStyle:{ color: "rgba(252, 252, 252, 0.99)" } } ], bottom: "83%", itemWidth: 20, itemHeight: 20, itemStyle:{ } }, xAxis: { data: ["衬衫", "羊毛衫", "雪纺衫", "裤子", "高跟鞋", "袜子"] }, yAxis: {}, series: [{ name: '销量', type: 'bar', data: [5, 20, 36, 10, 10, 20] }] }; myChart1.setOption(option); window.addEventListener("resize",function(){ myChart1.resize(); //myChart2指自己定义的echartsDom对象 }) }, drawbar2() { // 基于准备好的dom,初始化echarts实例 let myChart2 = echarts.init(document.getElementById('bar2')); // 指定图表的配置项和数据 let option = { title: { text: '第一个 ECharts 实例', textStyle: { fontSize: 15, color: "#FBFBFD" } }, tooltip: {}, legend: { data: [ { name: '销量', textStyle:{ color: "rgba(252, 252, 252, 0.99)" } } ], bottom: "83%", itemWidth: 20, itemHeight: 20, }, xAxis: { data: ["衬衫", "羊毛衫", "雪纺衫", "裤子", "高跟鞋", "袜子"] }, yAxis: {}, series: [{ name: '销量', type: 'bar', data: [5, 20, 36, 10, 10, 20] }] }; myChart2.setOption(option); window.addEventListener("resize",function(){ myChart2.resize(); //myChart2指自己定义的echartsDom对象 }) }, drawpie1() { let myChart3 = echarts.init(document.getElementById('pie1')); let option = { title: { text: '南丁格尔玫瑰图', subtext: '纯属虚构', left: 'center' }, tooltip: { trigger: 'item', formatter: '{a} <br/>{b} : {c} ({d}%)' }, legend: { left: 'center', top: 'bottom', data: ['rose1', 'rose2', 'rose3', 'rose4', 'rose5', 'rose6', 'rose7', 'rose8'] }, toolbox: { show: true, feature: { mark: {show: true}, dataView: {show: true, readOnly: false}, restore: {show: true}, saveAsImage: {show: true} } }, series: [ { name: '面积模式', type: 'pie', radius: [20, 90], center: ['25%', '50%'], roseType: 'area', Legend: { width: "10%", height: "10%", }, itemStyle: { borderRadius: 5 }, data: [ {value: 30, name: 'rose 1'}, {value: 28, name: 'rose 2'}, {value: 26, name: 'rose 3'}, {value: 24, name: 'rose 4'}, {value: 22, name: 'rose 5'}, {value: 20, name: 'rose 6'}, {value: 18, name: 'rose 7'}, {value: 16, name: 'rose 8'} ] } ] }; option && myChart3.setOption(option); window.addEventListener("resize",function(){ myChart3.resize(); //myChart2指自己定义的echartsDom对象 }) }, drawdataset1(){ let myChart4 = echarts.init(document.getElementById('set1')); let option = { dataset: { source: [ ['score', 'amount', 'product'], [89.3, 58212, 'Matcha Latte'], [57.1, 78254, 'Milk Tea'], [74.4, 41032, 'Cheese Cocoa'], [50.1, 12755, 'Cheese Brownie'], [89.7, 20145, 'Matcha Cocoa'], [68.1, 79146, 'Tea'], [19.6, 91852, 'Orange Juice'], [10.6, 101852, 'Lemon Juice'], [32.7, 20112, 'Walnut Brownie'] ] }, grid: {containLabel: true}, xAxis: {name: 'amount'}, yAxis: {type: 'category'}, visualMap: { orient: 'horizontal', left: 'center', min: 10, max: 100, text: ['High Score', 'Low Score'], // Map the score column to color dimension: 0, inRange: { color: ['#65B581', '#FFCE34', '#FD665F'] } }, series: [ { type: 'bar', encode: { // Map the "amount" column to X axis. x: 'amount', // Map the "product" column to Y axis y: 'product' } } ] }; option && myChart4.setOption(option); window.addEventListener("resize",function(){ myChart4.resize(); //myChart2指自己定义的echartsDom对象 }) }, drawdataset2(){ let myChart5 = echarts.init(document.getElementById('set2')); let option = { xAxis: { type: 'category', boundaryGap: false, data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] }, yAxis: { type: 'value' }, series: [{ data: [820, 932, 901, 934, 1290, 1330, 1320], type: 'line', areaStyle: {} }] }; option && myChart5.setOption(option); window.addEventListener("resize",function(){ myChart5.resize(); //myChart2指自己定义的echartsDom对象 }) }, drawdataset3(){ let myChart6 = echarts.init(document.getElementById('set3')); let option = { title: { text: '某站点用户访问来源', subtext: '纯属虚构', left: 'center' }, tooltip: { trigger: 'item' }, legend: { orient: 'vertical', left: 'left', }, series: [ { name: '访问来源', type: 'pie', radius: '50%', data: [ {value: 1048, name: '搜索引擎'}, {value: 735, name: '直接访问'}, {value: 580, name: '邮件营销'}, {value: 484, name: '联盟广告'}, {value: 300, name: '视频广告'} ], emphasis: { itemStyle: { shadowBlur: 10, shadowOffsetX: 0, shadowColor: 'rgba(0, 0, 0, 0.5)' } } } ] }; option && myChart6.setOption(option); window.addEventListener("resize",function(){ myChart6.resize(); //myChart2指自己定义的echartsDom对象 }) }, } } </script>
Vue中mounted的简单理解
原文链接:https://blog.csdn.net/sinat_41124751/article/details/97927232
mounted是vue中的一个钩子函数,一般在初始化页面完成后,再对dom节点进行相关操作。官方文档的解释如下,钩子函数的官方链接为 https://cn.vuejs.org/v2/api/#mounted ,生命周期函数图示链接为 https://cn.vuejs.org/v2/guide/instance.html#生命周期图示
自理解,通常是为 methods 函数提前定义( 类似提前声明变量 进入页面内容全部渲染完成后自动引函数),即
mounted() {
this.submit()
},
methods: {
submit() {
this.select.List = [];
for (var j = 0; j < this.select.label.length; j++) {
var Name = this.ToLabel(this.select.label[j])
this.select.List.push(Name);
}
}
vue实现切换左侧导航栏,对应切换右侧页面内容
1、右侧显示区域使用:<router-view></router-view>
2、左侧栏目菜单使用:<router-link to=""></router-link>
指定页面进行跳转
Navigator.vue
<template> <el-container style="height: 550px; border: 1px solid #eee"> <el-header style="text-align: right; font-size: 12px"> <el-dropdown> <i class="el-icon-setting" style="margin-right: 15px"></i> <el-dropdown-menu slot="dropdown"> <el-dropdown-item>查看</el-dropdown-item> <el-dropdown-item>新增</el-dropdown-item> <el-dropdown-item>删除</el-dropdown-item> </el-dropdown-menu> </el-dropdown> <span>@MedicalProject</span> </el-header> <el-container> <el-aside width="150px" style="background-color: rgb(238, 241, 246)"> <el-menu :default-openeds="[]"> <el-submenu index="1"> <template slot="title"><i class="el-icon-message"></i>问卷调查</template> <el-menu-item-group> <template slot="title">分组一</template> <el-menu-item index="1-1">选项1</el-menu-item> <el-menu-item index="1-2">选项2</el-menu-item> </el-menu-item-group> <el-menu-item-group title="分组2"> <el-menu-item index="1-3">选项3</el-menu-item> <el-menu-item index="1-4">选项4</el-menu-item> </el-menu-item-group> </el-submenu> <el-submenu index="2"> <template slot="title"><i class="el-icon-menu"></i>风险评估</template> <el-menu-item-group> <template slot="title">分组一</template> <el-menu-item index="2-1">模型分析</el-menu-item> <el-menu-item index="2-2">编程分析</el-menu-item> </el-menu-item-group> <el-menu-item-group title="分组2"> <el-menu-item index="2-3"><router-link to="/first">数据可视化</router-link></el-menu-item> <el-menu-item index="2-4">数据查询</el-menu-item> </el-menu-item-group> </el-submenu> <el-submenu index="3"> <template slot="title"><i class="el-icon-setting"></i>知识图谱</template> <el-menu-item-group> <template slot="title">分组一</template> <el-menu-item index="3-1">选项1</el-menu-item> <el-menu-item index="3-2">选项2</el-menu-item> </el-menu-item-group> <el-menu-item-group title="分组2"> <el-menu-item index="3-3">选项3</el-menu-item> </el-menu-item-group> <el-submenu index="3-4"> <template slot="title">选项4</template> <el-menu-item index="3-4-1">选项4-1</el-menu-item> </el-submenu> </el-submenu> <el-submenu index="4"> <template slot="title"><i class="el-icon-setting"></i>系统管理</template> <el-menu-item-group> <template slot="title">分组一</template> <el-menu-item index="3-1">选项1</el-menu-item> <el-menu-item index="3-2">选项2</el-menu-item> </el-menu-item-group> <el-menu-item-group title="分组2"> <el-menu-item index="3-3">选项3</el-menu-item> </el-menu-item-group> <el-submenu index="3-4"> <template slot="title">选项4</template> <el-menu-item index="3-4-1">选项4-1</el-menu-item> </el-submenu> </el-submenu> </el-menu> </el-aside> <el-main> <router-view></router-view> </el-main> </el-container> <el-footer>Footer</el-footer> </el-container> </template> <style scoped> .el-header, .el-footer { background-color: #B3C0D1; color: #333; text-align: center; line-height: 60px; } .el-main { background-color: #E9EEF3; color: #333; height: 600px; width: 1000px; text-align: center; line-height: 110px; } .el-aside { color: #333; } a { text-decoration: none; } .router-link-active { text-decoration: none; } </style> <script> let echarts = require('echarts/lib/echarts'); export default { name: 'First.vue', data() { const item = { // date: '123', // name: '@', // address: '123' }; return { // tableData: Array(10).fill(item) } }, mounted() { }, methods: {} } </script>
Data_Visualization.vue
<!-- 为ECharts准备一个具备大小(宽高)的Dom--> <template> <div> <div style="display: flex; margin-bottom: 10px" > <div style="background-color: #0F1033; width: 580px;height: 300px; display: flex;"> <div id="bar1" style="width: 280px;height:300px; background-color:#1F2056; align-content: center"></div> <div id="bar2" style="width: 280px;height:300px; background-color:#1F2056; align-content: center"></div> </div> <div style="background-color: #0F1033; width: 580px;height: 300px; display: flex;"> <div id="pie1" style="width: 580px;height:300px; background-color:#1F2056;display: flex;" ></div> </div> </div> <div style="display: flex; margin-top: 10px" > <div style="background-color: #0F1033; width: 580px;height: 300px; display: flex;"> <div id="set1" style="width: 280px;height:300px; background-color:#1F2056; align-content: center"></div> <div id="set2" style="width: 280px;height:300px; background-color:#1F2056; align-content: center"></div> </div> <div style="background-color: #0F1033; width: 580px;height: 300px; display: flex;"> <div id="set3" style="width: 580px;height:300px; background-color:#1F2056;display: flex;" ></div> </div> </div> </div> </template> <script> let echarts = require('echarts/lib/echarts'); export default { name: 'First.vue', data() { const item = { // date: '2016-05-02', // name: '王小虎', // address: '上海市普陀区金沙江路 1518 弄' }; return { // tableData: Array(10).fill(item) } }, mounted() { this.drawbar1(); this.drawbar2(); this.drawpie1(); this.drawdataset1(); this.drawdataset2(); this.drawdataset3(); }, methods: { drawbar1() { // 基于准备好的dom,初始化echarts实例 let myChart1 = echarts.init(document.getElementById('bar1')); // 指定图表的配置项和数据 let option = { title: { text: '第一个 ECharts 实例', textStyle: { fontSize: 15, color: "#FBFBFD" } }, tooltip: {}, legend: { data: [ { name: '销量', textStyle:{ color: "rgba(252, 252, 252, 0.99)" } } ], bottom: "83%", itemWidth: 20, itemHeight: 20, itemStyle:{ } }, xAxis: { data: ["衬衫", "羊毛衫", "雪纺衫", "裤子", "高跟鞋", "袜子"] }, yAxis: {}, series: [{ name: '销量', type: 'bar', data: [5, 20, 36, 10, 10, 20] }] }; myChart1.setOption(option); window.addEventListener("resize",function(){ myChart1.resize(); //myChart2指自己定义的echartsDom对象 }) }, drawbar2() { // 基于准备好的dom,初始化echarts实例 let myChart2 = echarts.init(document.getElementById('bar2')); // 指定图表的配置项和数据 let option = { title: { text: '第一个 ECharts 实例', textStyle: { fontSize: 15, color: "#FBFBFD" } }, tooltip: {}, legend: { data: [ { name: '销量', textStyle:{ color: "rgba(252, 252, 252, 0.99)" } } ], bottom: "83%", itemWidth: 20, itemHeight: 20, }, xAxis: { data: ["衬衫", "羊毛衫", "雪纺衫", "裤子", "高跟鞋", "袜子"] }, yAxis: {}, series: [{ name: '销量', type: 'bar', data: [5, 20, 36, 10, 10, 20] }] }; myChart2.setOption(option); window.addEventListener("resize",function(){ myChart2.resize(); //myChart2指自己定义的echartsDom对象 }) }, drawpie1() { let myChart3 = echarts.init(document.getElementById('pie1')); let option = { title: { text: '南丁格尔玫瑰图', subtext: '纯属虚构', left: 'center' }, tooltip: { trigger: 'item', formatter: '{a} <br/>{b} : {c} ({d}%)' }, legend: { left: 'center', top: 'bottom', data: ['rose1', 'rose2', 'rose3', 'rose4', 'rose5', 'rose6', 'rose7', 'rose8'] }, toolbox: { show: true, feature: { mark: {show: true}, dataView: {show: true, readOnly: false}, restore: {show: true}, saveAsImage: {show: true} } }, series: [ { name: '面积模式', type: 'pie', radius: [20, 90], center: ['25%', '50%'], roseType: 'area', Legend: { width: "10%", height: "10%", }, itemStyle: { borderRadius: 5 }, data: [ {value: 30, name: 'rose 1'}, {value: 28, name: 'rose 2'}, {value: 26, name: 'rose 3'}, {value: 24, name: 'rose 4'}, {value: 22, name: 'rose 5'}, {value: 20, name: 'rose 6'}, {value: 18, name: 'rose 7'}, {value: 16, name: 'rose 8'} ] } ] }; option && myChart3.setOption(option); window.addEventListener("resize",function(){ myChart3.resize(); //myChart2指自己定义的echartsDom对象 }) }, drawdataset1(){ let myChart4 = echarts.init(document.getElementById('set1')); let option = { dataset: { source: [ ['score', 'amount', 'product'], [89.3, 58212, 'Matcha Latte'], [57.1, 78254, 'Milk Tea'], [74.4, 41032, 'Cheese Cocoa'], [50.1, 12755, 'Cheese Brownie'], [89.7, 20145, 'Matcha Cocoa'], [68.1, 79146, 'Tea'], [19.6, 91852, 'Orange Juice'], [10.6, 101852, 'Lemon Juice'], [32.7, 20112, 'Walnut Brownie'] ] }, grid: {containLabel: true}, xAxis: {name: 'amount'}, yAxis: {type: 'category'}, visualMap: { orient: 'horizontal', left: 'center', min: 10, max: 100, text: ['High Score', 'Low Score'], // Map the score column to color dimension: 0, inRange: { color: ['#65B581', '#FFCE34', '#FD665F'] } }, series: [ { type: 'bar', encode: { // Map the "amount" column to X axis. x: 'amount', // Map the "product" column to Y axis y: 'product' } } ] }; option && myChart4.setOption(option); window.addEventListener("resize",function(){ myChart4.resize(); //myChart2指自己定义的echartsDom对象 }) }, drawdataset2(){ let myChart5 = echarts.init(document.getElementById('set2')); let option = { xAxis: { type: 'category', boundaryGap: false, data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] }, yAxis: { type: 'value' }, series: [{ data: [820, 932, 901, 934, 1290, 1330, 1320], type: 'line', areaStyle: {} }] }; option && myChart5.setOption(option); window.addEventListener("resize",function(){ myChart5.resize(); //myChart2指自己定义的echartsDom对象 }) }, drawdataset3(){ let myChart6 = echarts.init(document.getElementById('set3')); let option = { title: { text: '某站点用户访问来源', subtext: '纯属虚构', left: 'center' }, tooltip: { trigger: 'item' }, legend: { orient: 'vertical', left: 'left', }, series: [ { name: '访问来源', type: 'pie', radius: '50%', data: [ {value: 1048, name: '搜索引擎'}, {value: 735, name: '直接访问'}, {value: 580, name: '邮件营销'}, {value: 484, name: '联盟广告'}, {value: 300, name: '视频广告'} ], emphasis: { itemStyle: { shadowBlur: 10, shadowOffsetX: 0, shadowColor: 'rgba(0, 0, 0, 0.5)' } } } ] }; option && myChart6.setOption(option); window.addEventListener("resize",function(){ myChart6.resize(); //myChart2指自己定义的echartsDom对象 }) }, } } </script>
App.vue
<template> <!-- <div id="app">--> <!-- <img src="./assets/logo.png">--> <div id="app"> <Nav></Nav> </div> <!-- </div>--> </template> <script> import Nav from '@/components/Navigator' export default { name: 'App', components: { Nav } } </script> <style> /*#app {*/ /* font-family: 'Avenir', Helvetica, Arial, sans-serif;*/ /* -webkit-font-smoothing: antialiased;*/ /* -moz-osx-font-smoothing: grayscale;*/ /* text-align: center;*/ /* color: #2c3e50;*/ /* margin-top: 60px;*/ /*}*/ </style>
index.js
import Vue from 'vue'
import Router from 'vue-router'
import Data_Visualization from '@/components/Data_Visualization'
Vue.use(Router)
export default new Router({
mode:'history',//去掉路由地址栏中的#
routes: [
{
path: '/Data_V',
name: 'Data_Visualization',
component: Data_Visualization
}
]
})
消除router-link 的下划线问题
增加样式代码即可
a {
text-decoration: none;
}
.router-link-active {
text-decoration: none;
}
vue中vuedraggable 拖拽列表的使用
原文链接:https://blog.csdn.net/weixin_47180815/article/details/109841448
【想看官网的这里来】
vue中draggable拖拽列表的使用
https://github.com/SortableJS/Vue.Draggable
vuedraggable中文文档
https://www.itxst.com/vue-draggable/tutorial.html
官方示例:
https://sortablejs.github.io/Vue.Draggable/#/transition-example-2
https://david-desmaisons.github.io/draggable-example/
【以下是我自己的用法】
安装引入,已经ok的可以直接跳过这个部分
- 安装
npm install vuedraggable
或者使用镜像安装
cnpm install vuedraggable
- 使用
首先在使用的组件中引入
import draggable from 'vuedraggable'
Axios
Axios是-个基于Promise 的HTTP库,它是一个简洁、易用且高效的代码封装库。
通俗地讲,它是当前比较流行的一种Ajax框架,可以使用它发起HTTP请求接口功能,
是基于Promise的,相比较Ajax的回调函数能够更好地管理异步操作。
Axios的特点:
(1)从浏览器中创建XMLHttpRequests
(2)从Node.js创建HTTP请求。
(3)支持Promise API
(4)拦截请求和响应
(5)转换请求数据和响应数据
(6)取消请求
(7)自动转换JSON数据
(8)客户端支持防御XSRF
使用方法:
使用NPM安装Axios的依赖:
npm install axios
1.全局使用Axios需要在main.js中设置,然后在组件中通过this来调用,代码如下:
import axios from 'axios'
Vue.prototype.$axios= axios; //加载到原型上
2.vue中Axios的封装
api/index.js文件,封装配置Axios
import axios from 'axios'
let http = axios.create({
baseURI: 'http://localhost:18071/', //设置默认前缀地址
withCredentials: true, //配置跨域,需要
headers: {
'content-Type': 'application/x-www-form-urlencoded; charset=utf-8'
},
transformRequest: [function (data) {
let newData = '';
for (let k in data){
if(data.hasOwnProperty(k) === true){
newData += encodeURIComponent(k) + '=' + encodeURIComponent(data[k]) + '&';
}
}
return newData;
}]
});
function apiAxios(method, url, params, response){
http({
method: method,
url: url,
data: method === 'POST'|| method === 'PUT' ? params : null,
params: method === 'GET'|| method === 'DELETE' ? params : null,
}).then(function (res) {
response(res);
}).catch(function (err) {
response(err);
})
}
export default {
get: function (url, params, response) {
return apiAxios('GET', url, params, response)
},
post: function (url, params, response) {
return apiAxios('POST', url, params, response)
},
put: function (url, params, response) {
return apiAxios('PUT', url, params, response)
},
delete: function (url, params, response) {
return apiAxios('DELETE', url, params, response)
}
}
使用:
在main.js中引入方法,代码如下:
//main.js 部分代码省略
import Api from './api/index.js';
Vue.prototype.$http= Api;
![](https://img2020.cnblogs.com/blog/1948390/202108/1948390-20210819231911089-534774404.png)
使用Axios请求后端的实时数据:
![](https://img2020.cnblogs.com/blog/1948390/202108/1948390-20210819232015071-1821321146.png)
版权声明:本文为CSDN博主「让我看看谁在学习」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_42545184/article/details/89311666
vue项目中某一页面不想引用公共组件app.vue解决办法
Vue单页面应用把公共组件放在 app.vue 但是我希望某个页面没有这些公共组件怎么办(比如登陆页面)每个页面都有导航栏但是我希望
登陆页面只有一个背景色和一个登陆框 没有导航栏 那应该怎样设置呢?
在根组件中:在导航栏使用v-show判断当前路由是否是不需要的组件来完成页面:
App.vue
index.vue
Vue--登录页面
https://www.cnblogs.com/zouzou-busy/p/11741407.html
使用elementUI
<template>
<div class="login-container">
<el-form ref="form" :model="form" label-width="80px" class="login-form">
<h2 class="login-title">肿瘤健康管理系统</h2>
<el-form-item label="用户名">
<el-input v-model="form.username"></el-input>
</el-form-item>
<el-form-item label="密码">
<el-input v-model="form.password"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="onSubmit">登录</el-button>
</el-form-item>
</el-form>
</div>
</template>
<script>
export default {
data() {
return {
form: {
username: "",
password: ""
}
};
},
methods: {
onSubmit() {
console.log("submit!");
}
}
};
</script>
<style acoped>
.login-form {
width: 350px;
margin: 160px auto; /* 上下间距160px,左右自动居中*/
background-color: rgb(255, 255, 255, 0.8); /* 透明背景色 */
padding: 30px;
border-radius: 20px; /* 圆角 */
}
/* 背景 */
.login-container {
position: absolute;
width: 100%;
height: 100%;
background: url("../../assets/login.png");
}
/* 标题 */
.login-title {
color: #303133;
text-align: center;
}
body{
font-family: "微软雅黑"; /* 设置字体 */
margin: 0px auto /* 去除上下的边距*/
}
</style>
![](https://img2020.cnblogs.com/blog/1948390/202108/1948390-20210823212845910-1890176319.png)
添加表单验证
上面我们只是实现了登录的form表单,但是没有验证数据输入的合法性,比如空,或者长度
elementui提供给了我们校验的方法
Form 组件提供了表单验证的功能,只需要通过 rules
属性传入约定的验证规则,并将 Form-Item 的 prop
属性设置为需校验的字段名即可
修改 login.vue里的代码
<template>
<div class="login-container">
<el-form ref="form" :rules="rules" :model="form" label-width="80px" class="login-form">
<h2 class="login-title">肿瘤健康管理系统</h2>
<el-form-item label="用户名" prop="username">
<el-input v-model="form.username"></el-input>
</el-form-item>
<el-form-item label="密码" prop="password">
<el-input v-model="form.password"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitForm('form')">登录</el-button>
</el-form-item>
</el-form>
</div>
</template>
<script>
export default {
data() {
return {
form: {
username: "",
password: ""
},
rules: {
username: [
{required: true, message: "用户名不能为空", trigger: 'blur'},
{min: 3, max: 10, message: "用户名3-5位", trigger: 'blur'}
],
password: [
{required: true, message: "密码不能为空", trigger: 'blur'},
{min: 3, max: 10, message: "密码3-5位", trigger: 'blur'}
]
}
};
},
methods: {
submitForm(formName) {
this.$refs[formName].validate(valid => {
// console.log(valid) 验证通过为true,有一个不通过就是false
if (valid) {
// 通过的逻辑
} else {
console.log('验证失败');
return false;
}
});
}
}
};
</script>
<style acoped>
.login-form {
width: 350px;
margin: 160px auto; /* 上下间距160px,左右自动居中*/
background-color: rgb(255, 255, 255); /* 透明背景色 */
padding: 30px;
border-radius: 20px; /* 圆角 */
}
/* 背景 */
.login-container {
position: absolute;
width: 100%;
height: 100%;
background: url("../assets/login.png");
}
/* 标题 */
.login-title {
color: #303133;
text-align: center;
}
body{
font-family: "微软雅黑"; /* 设置字体 */
margin: 0px auto /* 去除上下的边距*/
}
</style>
在第三行加上 :rules="rules" 第五行和第八行加上 prop="username" 后面的属性值自定义,更改39行到47行,第十三行改为 @click="submitForm('form')",submitForm就是method的方法名。后面的form是第三行的ref属性。
{required: true, message: "密码不能为空", trigger: 'blur'},表示当鼠标失去焦点后验证,必填,如果为空则提示message里的信息
{min: 3, max: 10, message: "密码3-5位", trigger: 'blur'}表示当鼠标失去焦点后验证,最小为3位,最大为10位,如果不满足则提示message里的信息
vue处理token+配置axios
版权声明:本文为CSDN博主「花眼熊」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_49668076/article/details/114338975
在项目中登录之后就要把token存起来,有权限的接口都要携带token访问。
本篇是通过vuex容器+本地存储结合在一起存储token,好处是获取方便、响应式、持久化。
什么是 Token?
Token 是用户登录成功之后服务端返回的一个身份令牌,在项目中的多个业务中需要使用到
访问需要授权的 API 接口
校验页面的访问权限
…
但是我们只有在第一次用户登录成功之后才能拿到 Token
所以为了能在其它模块中获取到 Token 数据,我们需要把它存储到一个公共的位置,方便随时取用
具体代码如下:1
.封装一个本地缓存的方法
在src下新建一个storage文件夹,文件夹里新建index.vue
Vue 页面权限控制和登陆验证
页面权限控制是什么意思呢?
就是一个网站有不同的角色,比如管理员和普通用户,要求不同的角色能访问的页面是不一样的。如果一个页面,有角色越权访问,这时就得做出限制了。
这是我写过的一篇文章,
Vue 动态添加路由及生成菜单通过动态添加路由和菜单来做控制,不能访问的页面不添加到路由表里,这是其中一种办法。
另一种办法就是所有的页面都在路由表里,只是在访问的时候要判断一下角色权限。如果有权限就让访问,没有权限就拒绝,跳转到 404 页面(或指定页面)。
思路:
在每一个路由的 meta
属性里,将能访问该路由的角色添加到 roles
里。用户每次登陆后,将用户的角色返回。然后在访问页面时,把路由的 meta
属性和用户的角色进行对比,如果用户的角色在路由的 roles
里,那就是能访问,如果不在就拒绝访问。
代码示例:
路由信息
routes: [
{
path: '/login',
name: 'login',
meta: {
roles: ['admin', 'user']
},
component: () => import('../components/Login.vue')
},
{
path: 'home',
name: 'home',
meta: {
roles: ['admin']
},
component: () => import('../views/Home.vue')
},
]
页面控制
// 假设角色有两种:admin 和 user
// 这里是从后台获取的用户角色
const role = 'user'
// 在进入一个页面前会触发 router.beforeEach 事件
router.beforeEach((to, from, next) => {
if (to.meta.roles.includes(role)) {
next()
} else {
next({path: '/404'})
}
})