我们选择免费的,功能比较多的 Echart,当然了你也可以选择 AntV,也有 highChart
<template>
<div ref="chart" style="width: 600px;height:400px;"></div>
</template>
<script>
import echarts from 'echarts'
export default {
name: 'Chart',
mounted() {
var myChart = echarts.init(this.$refs.chart)
// 指定图表的配置项和数据
var option = {
title: {
text: 'ECharts 入门示例'
},
tooltip: {},
legend: {
data: ['销量']
},
xAxis: {
data: ['衬衫', '羊毛衫', '雪纺衫', '裤子', '高跟鞋', '袜子']
},
yAxis: {},
series: [
{
name: '销量',
type: 'bar',
data: [5, 20, 36, 10, 10, 20]
}
]
}
// 使用刚指定的配置项和数据显示图表。
myChart.setOption(option)
}
}
</script>
<style lang="less" scoped></style>
<template>
<div ref="chart" style="height:400px;">div>
template>
import echarts from 'echarts'
import { addListener, removeListener } from 'resize-detector'
export default {
name: 'Chart',
mounted() {
this.chart = echarts.init(this.$refs.chart)
// 指定图表的配置项和数据
var option = {
title: {
text: 'ECharts 入门示例'
},
tooltip: {},
legend: {
data: ['销量']
},
xAxis: {
data: ['衬衫', '羊毛衫', '雪纺衫', '裤子', '高跟鞋', '袜子']
},
yAxis: {},
series: [
{
name: '销量',
type: 'bar',
data: [5, 20, 36, 10, 10, 20]
}
]
}
// 使用刚指定的配置项和数据显示图表。
this.chart.setOption(option)
// 监听数据dom变化
addListener(this.$refs.chart, this.resize)
},
methods: {
resize() {
console.log('变化了')
this.chart.resize()
},
removeChart() {
console.log('卸载')
}
},
beforeDestroy() {
// 卸载时移除监听事件
removeListener(this.$refs.chart, this.removeChart)
// 始放图表组件,防止内存泄漏
this.chart.dispose()
this.chart = null
}
}
</script>
import { debounce } from 'lodash'
// 在created中添加一个debounce防抖函数
created() {
this.resize = debounce(this.resize, 200)
}
<script>
import echarts from 'echarts'
import { addListener, removeListener } from 'resize-detector'
import { debounce } from 'lodash'
export default {
props: {// 关于图表的类型,咱们通过组件调用传参过来即可
option: {
type: Object,
default: () => {}
}
},
mounted() {
this.renderChar()
// 监听数据dom变化
addListener(this.$refs.chart, this.resize)
},
methods: {
// 纯粹的自定义组件
renderChar() {
// 基于准备好的dom初始化chart示例
this.chart = echarts.init(this.$refs.chart)
this.chart.setOption(this.option)
},
resize() {
console.log('变化了')
this.chart.resize()
}
},
watch: {
option(val) {
// 这样有一个问题:option没有变化,但是option中的data数组如果变了是监视不到的,怎么办呢?用深度监听?
this.chart.setOption(val)
}
// option: {
// // 深度监听的写法:但是依旧很耗性能,怎么办呢?那我们还是采取第一种监听方式
// handler(val) {
// this.chart.setOption(val)
// },
// deep: true //
// }
},
beforeDestroy() {
removeListener(this.$refs.chart, this.resize)
// 始放图表组件,防止内存泄漏
this.chart.dispose()
this.chart = null
},
created() {
this.resize = debounce(this.resize, 200)
}
}
</script>
<div><Chart :option="opitons" style="height:400px" />div>
<script>
// 引入公共的图表组件
import Chart from '@/components/chart/Chart'
// 使用随机数
import { random } from 'lodash'
export default {
data() {
return {
// 指定图表的配置s项和数据
fuck: 'FUCK',
opitons: {
title: {
text: 'ECharts 入门示例'
},
tooltip: {},
legend: {
data: ['销量']
},
xAxis: {
data: ['衬衫', '羊毛衫', '雪纺衫', '裤子', '高跟鞋', '袜子']
},
yAxis: {},
series: [
{
name: '销量',
type: 'bar',
data: [5, 20, 36, 10, 10, 20]
}
]
}
}
},
mounted() {
setInterval(() => {
this.opitons.series[0].data = this.opitons.series[0].data.map(() =>
random(100)
)
// 重新赋值,是要数据发生变化就更新数据
this.opitons = { ...this.opitons }
}, 800)
},
components: {
Chart
}
}
</script>
安装 axios->cnpm i axios
新建 service 文件夹->mock->index.js
Analysis.vue
// 引入axios
import axios from 'axios'
mounted() {
// 调用mock接口
this.getCharData()
setInterval(() => {
this.getCharData()
// this.opitons.series[0].data = this.opitons.series[0].data.map(() =>
// random(100)
// )
// // 重新赋值,是要数据发生变化就更新数据
// this.opitons = { ...this.opitons }
}, 800)
},
methods: {
// 模拟mock数据
getCharData() {
axios
.get('/service/mock/chartData', { params: { ID: 12346 } })
.then(res => {
this.opitons = {
title: {
text: 'ECharts 入门示例'
},
tooltip: {},
legend: {
data: ['销量']
},
xAxis: {
data: ['衬衫', '羊毛衫', '雪纺衫', '裤子', '高跟鞋', '袜子']
},
yAxis: {},
series: [
{
name: '销量',
type: 'bar',
data: res.data
}
]
}
})
}
},
service->mock->index
function chartData(method) {
let res = null
switch (method) {
case 'GET':
res = [200, 40, 44, 12, 34, 200]
break
default:
res = null
}
return res
}
module.exports = { chartData }
配置 webpack->vue.config.js
devServer: {
proxy: {
'/service': {
target: 'http://localhost:3000',
bypass: function(req, res) {
if (req.headers.accept.indexOf('html') !== -1) {
console.log('Skipping proxy for browser request.')
return '/index.html'
} else {
const name = req.path.split('/')[3]
const mock = require(`./service/mock/index`)[name]
const result = mock(req.method)
delete require.cache[require.resolve(`./service/mock/index`)] //清除缓存这样,每次你只要一修改mock数据页面及时刷新
return res.send(result)
}
}
}
}
}
delete require.cache[require.r
esolve(`./service/mock/index`)] //清除缓存这样,每次你只要一修改mock数据页面及时刷新
"scripts": {
"serve": "vue-cli-service serve",
// 新增serve:mock命令此时就会将MOCK设置成环境变量cross-env设置跨平台环境变量设置
"serve:mock": "cross-env MOCK=true vue-cli-service serve",
"build": "vue-cli-service build",
"test:unit": "vue-cli-service test:unit",
"lint": "vue-cli-service lint"
},
devServer: {
proxy: {
"/service": {
target: "http://localhost:3000",
bypass: function(req, res) {
if (req.headers.accept.indexOf("html") !== -1) {
console.log("Skipping proxy for browser request.");
return "/index.html";
} else if(process.env.MOCK==='true') { // 通过环境变量来执行下面mock代理
const name = req.path.split("/")[3];
const mock = require(`./service/mock/index`)[name];
const result = mock(req.method);
delete require.cache[require.resolve(`./service/mock/index`)]; //清除缓存这样,每次你只要一修改mock数据页面及时刷新
return res.send(result);
}
}
}
}
}
import axios from 'axios'
import { Notification } from 'ant-design-vue'
function request(options) {
return axios(options)
.then(res => {
return res
})
.catch(error => {
const {
response: { status, statusText }
} = error
// 请求失败提醒
Notification.error({
message: status,
description: statusText
})
// 返回reject的好处就是你在使用的时候,直接通过catch去捕捉,不会在进入then里面让你处理相关逻辑
return Promise.reject(error)
})
}
export default request
// 引入封装好的方法
import request from '@/utils/request'
methods: {
// 模拟mock数据
getCharData() {
// 使用该方法
request({
url: '/service/mock/chartData',
method: 'get',
params: { ID: 12346 }
}).then(res => {
this.opitons = {
title: {
text: 'ECharts 入门示例'
},
tooltip: {},
legend: {
data: ['销量']
},
xAxis: {
data: ['衬衫', '羊毛衫', '雪纺衫', '裤子', '高跟鞋', '袜子']
},
yAxis: {},
series: [
{
name: '销量',
type: 'bar',
data: res.data
}
]
}
})
}
},
怎么用呢? 看这:https://github.com/vuejs/jsx
npm install @vue/babel-preset-jsx @vue/babel-helper-vue-jsx-merge-props
babel.config.js 添加配置
module.exports = {
presets: ['@vue/cli-plugin-babel/preset', '@vue/babel-preset-jsx'] // 添加jsx配置
}
import axios from 'axios'
import { Notification } from 'ant-design-vue'
function request(options) {
return axios(options)
.then(res => {
return res
})
.catch(error => {
const {
response: { status, statusText }
} = error
// 请求失败提醒
Notification.error({
// 注意了:下面的这句注释,是用来告诉eslint不用校验了,否则h没使用过就会报错
//eslint-disable-next-line no-unused-vars
message: h => (
// 注意看这里:咱们就可以使用jsx语法定义想要的样式了
<div>
请求错误:<span style="color:red">{status}</span>
<br />
{options.url}
</div>
),
description: statusText
})
// 返回reject的好处就是你在使用的时候,直接通过catch去捕捉,不会在进入then里面让你处理相关逻辑
return Promise.reject(error)
})
}
export default request
<template>
<a-form :layout="formLayout">
<a-form-item
label="Form Layout"
:label-col="formItemLayout.labelCol"
:wrapper-col="formItemLayout.wrapperCol"
>
<a-radio-group
default-value="horizontal"
@change="handleFormLayoutChange"
>
<a-radio-button value="horizontal">
Horizontal
a-radio-button>
<a-radio-button value="vertical">
Vertical
a-radio-button>
<a-radio-button value="inline">
Inline
a-radio-button>
a-radio-group>
a-form-item>
<a-form-item
label="Field A"
:label-col="formItemLayout.labelCol"
:wrapper-col="formItemLayout.wrapperCol"
>
<a-input placeholder="input placeholder" />
a-form-item>
<a-form-item
label="Field B"
:label-col="formItemLayout.labelCol"
:wrapper-col="formItemLayout.wrapperCol"
>
<a-input placeholder="input placeholder" />
a-form-item>
<a-form-item :wrapper-col="buttonItemLayout.wrapperCol">
<a-button type="primary">
Submit
a-button>
a-form-item>
a-form>
template>
<script>
export default {
data() {
return {
formLayout: 'horizontal'
}
},
computed: {
formItemLayout() {
const { formLayout } = this
return formLayout === 'horizontal'
? {
labelCol: { span: 4 },
wrapperCol: { span: 14 }
}
: {}
},
buttonItemLayout() {
const { formLayout } = this
return formLayout === 'horizontal'
? {
wrapperCol: { span: 14, offset: 4 }
}
: {}
}
},
methods: {
handleFormLayoutChange(e) {
this.formLayout = e.target.value
}
}
}
</script>
// 你看官方提供了这么写属性供咱们使用
validateStatus: 校验状态,可选 ‘success’, ‘warning’, ‘error’, ‘validating’。
hasFeedback:用于给输入框添加反馈图标。
help:设置校验文案
注意了:我们根据官方提供的这些属性改造一下表单校验
<template>
<a-form :layout="formLayout">
<a-form-item
label="Form Layout"
:label-col="formItemLayout.labelCol"
:wrapper-col="formItemLayout.wrapperCol"
>
<a-radio-group
default-value="horizontal"
@change="handleFormLayoutChange"
>
<a-radio-button value="horizontal">
Horizontal
a-radio-button>
<a-radio-button value="vertical">
Vertical
a-radio-button>
<a-radio-button value="inline">
Inline
a-radio-button>
a-radio-group>
a-form-item>
<a-form-item
label="姓名"
:label-col="formItemLayout.labelCol"
:wrapper-col="formItemLayout.wrapperCol"
:validateStatus="userErrorStatus"
:help="userHelpText"
>
<a-input placeholder="请输入用户名称" v-model="userName" />
a-form-item>
<a-form-item
label="手机"
:label-col="formItemLayout.labelCol"
:wrapper-col="formItemLayout.wrapperCol"
:validateStatus="phoneErrorStatus"
:help="phoneHelpText"
>
<a-input type="number" placeholder="请输入手机号码" v-model="phone" />
a-form-item>
<a-form-item :wrapper-col="buttonItemLayout.wrapperCol">
<a-button type="primary" @click="submitHandle">
Submit
a-button>
a-form-item>
a-form>
template>
<script>
export default {
data() {
return {
userErrorStatus: '',
userHelpText: '',
phoneErrorStatus: '',
phoneHelpText: '',
userName: '',
phone: '',
formLayout: 'horizontal'
}
},
watch: {
// 监听校验
userName(val) {
if (val.length < 2) {
;(this.userErrorStatus = 'error'),
(this.userHelpText = '昵称长度不得少于两位')
} else {
;(this.userErrorStatus = ''), (this.userHelpText = '')
}
},
phone(val) {
if (val.length < 11) {
;(this.phoneErrorStatus = 'error'),
(this.phoneHelpText = '手机不得少于11位')
} else {
;(this.phoneErrorStatus = ''), (this.phoneHelpText = '')
}
}
},
computed: {
formItemLayout() {
const { formLayout } = this
return formLayout === 'horizontal'
? {
labelCol: { span: 4 },
wrapperCol: { span: 14 }
}
: {}
},
buttonItemLayout() {
const { formLayout } = this
return formLayout === 'horizontal'
? {
wrapperCol: { span: 14, offset: 4 }
}
: {}
}
},
methods: {
// 提交校验
submitHandle() {
if (this.userName.length < 2) {
;(this.userErrorStatus = 'error'),
(this.userHelpText = '昵称长度不得少于两位')
return
}
if (this.phone.length < 11) {
;(this.phoneErrorStatus = 'error'),
(this.phoneHelpText = '手机不得少于11位')
return
}
},
handleFormLayoutChange(e) {
this.formLayout = e.target.value
}
}
}
</script>
已阿里 icon 库为例:https://www.iconfont.cn/
这是本地化操作
这是使用 cdn 的方式
// 使用Icon
import { Icon } from 'ant-design-vue'
// 将cdn地址换成阿里图标我们的地址
const IconFont = Icon.createFromIconfontCN({
scriptUrl: '//at.alicdn.com/t/font_1729142_92zhgmdrlj8.js'
})
// 全局注册
Vue.component('IconFont', IconFont)
然后你可以选择在任何地方使用这个图片,这个 type 就是你图标库中的图标的名称
注意:你可以改图标库中的名称等信息,但是你改完之后,会重新生成一个地址,你只要把那个地址重新覆盖到我们本地项目中就行了
我使用了 404 的图标放在 404 页面,你可以去看
<IconFont type="iconicon-404">IconFont>
chainWebpack: config => {
const svgRule = config.module.rule('svg')
// 清除已有的所有 loader。
// 如果你不这样做,接下来的 loader 会附加在该规则现有的 loader 之后。
svgRule.uses.clear()
// 添加要替换的 loader
svgRule.use('vue-svg-loader').loader('vue-svg-loader')
}
<template>
<div style=" text-align:center">
<Man />
div>
template>
import Man from '@/assets/man.svg' // 注意哦,此时你引入的是一个组件哦,所以需要干啥子?没错就是要注册
export default {
components: {
// 注册组件
Man
}
}
欢迎加微信一起学习:13671593005
未完待续…