技巧
- 解决回车和失去焦点重复触发函数的问题
- 差异可以通过组件传参区分
@keyup.enter.native="$event.target.blur"
2.通过style绑定样式
:style="{ background: card.backColor, borderColor: card.bordColor }"
防抖
// fn 需要处理的函数
// t 时间
function debounce(fn, t=500){
let timer = null;
return function(){
timer&&clearTimeout(timer);
timer = setTimeout(fn, t)
}
}
打印
print({ printable: 'print_body', type: 'html', targetStyles: ['*'] })
上传二进制文件
excel导入
// 导入excel表格
beforeFileUpload (files) {
let fileFormData = new FormData()
fileFormData.append('file', files, this.files.name)
console.log(fileFormData)
//已经在importByExcel设置 headers: { 'Content-Type': 'multipart/form-data'},
importByExcel(fileFormData).then(res => {
this.$message.success('导入成功')
})
// this.getList() // 刷新列表
// return false
},
excel导出
本质就是利用a标签下载
// 导出excel表格
exportExcel () {
exportByExcel().then(res => {
const blob = new Blob([res])
const fileName = '模板.xlsx'
const elink = document.createElement('a')
elink.download = fileName
elink.style.display = 'none'
elink.href = URL.createObjectURL(blob)
document.body.appendChild(elink)
elink.click()
URL.revokeObjectURL(elink.href) // 释放URL 对象
document.body.removeChild(elink)
})
}
mixin
mixin能够有效的减少多余的代码
数据冲突:合并处理、组件优先级更高
钩子冲突:都被调用,先调用mixin钩子,再调用组件钩子
全局参数
//路由跳转前设置
sessionStorage.setItem("menberid", val.id);
//跳转后获取全局参数
this.menberid = sessionStorage.getItem("menberid");
路由跳转
getDetailInfo () {
this.$router.push({
name: 'income_detail',
params: {
drugType: 0
}
})
}
双向绑定
操作dom,并将数据绑定到实例上
样式修改
document.getElementById('test-id').style.cssText="display:none !important;"
随机字符串
//生成一个36位的随机数
let randomStr = [...'0123456789qwertyuiopasdfghjklzxcvbnm'].reduce((p,c,i,arr)=> p+arr[Math.ceil(Math.random()*35)],'')
sync修饰符
子组件:this.$emit('update:name',paulod)
父组件:<child :name.sync="name"></child>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>.sync</title>
</head>
<body>
<div id="app">
父组件传递
<child :name.sync="name"></child>
<button type="button" @click="changePropsInFather">
在父组件中将props值改变为'props'
</button>
</div>
<script src="https://unpkg.com/vue"></script>
<script>
// 定义一个子组件
Vue.component("Child", {
template: `<h1 @click="changePropsInChild">hello, {{name}}</h1>`,
props: {
name: String
},
methods: {
changePropsInChild() {
let said = 'hello this is child'
//子组件向父组件传递
this.$emit('update:name',said)
}
}
});
// 实例化一个Vue对象
const app = new Vue({
el: "#app",
data() {
return {
name: "world"
};
},
methods: {
changePropsInFather() {
this.name = "I am from father";
}
}
});
</script>
</body>
</html>
pc适配
1、安装插件
npm install postcss-px2rem px2rem-loader --save
2、在根目录src中新建util目录下新建rem.js等比适配文件
--------------------------
-js文件
// rem等比适配配置文件
// 基准大小
const baseSize = 16
// 设置 rem 函数
function setRem () {
// 当前页面宽度相对于 1920宽的缩放比例,可根据自己需要修改。
const scale = document.documentElement.clientWidth / 1920
// 设置页面根节点字体大小(“Math.min(scale, 2)” 指最高放大比例为2,可根据实际业务需求调整)
document.documentElement.style.fontSize = baseSize * Math.min(scale, 2) + 'px'
}
// 初始化
setRem()
// 改变窗口大小时重新设置 rem
window.onresize = function () {
setRem()
}
----------------------------
3、mian.js中引入
import './util/rem'
4、到vue.config.js中配置插件
----------------------
-js文件
// 引入等比适配插件
const px2rem = require('postcss-px2rem')
// 配置基本大小
const postcss = px2rem({
// 基准大小 baseSize,需要和rem.js中相同
remUnit: 16
})
// 使用等比适配插件
module.exports = {
lintOnSave: true,
css: {
loaderOptions: {
postcss: {
plugins: [
postcss
]
}
}
}
}
----------------------
sse基本使用
//接口
import { addRegEvent } from '@api/doctor.inquiry'
//页面初始化时调用接口
created () {
this.init()
//建立sse连接
let eventSource = new EventSource(addRegEvent + `?token=${util.cookies.get('token')}`)
//监听状态改变
eventSource.addEventListener('change',event => (this.init()),false)
}
BUG
//打印出来的数据与后台传递的数据不一样
//原因:在vue组件传值时,传递的参数由于是引用类型,然后又在父组件中修改了传递参数(引用类型)的数据,所以才导致了这个问题
vue无法热加载问题:css中一个属性
时间
setDate:设置时间
const event = new Date('August 19, 1975 23:15:30');
event.setDate(24);
console.log(event.getDate()); //24
虚拟dom刷新不实时,但是又想操作dom时。可以将id绑定值为id获取元素
插件
dayjs
npm install dayjs --save
import dayjs from 'dayjs'
//获取当前时间
dayjs() 下表为当前日期
let date = new Date()
dayjs(date).format('YYYY-MM-DD dddd HH:mm:ss.SSS A') 格式化时间
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oys6WdYm-1604547262046)(C:\Users\tayy\AppData\Roaming\Typora\typora-user-images\1587863869848.png)]
v-charts
extend的使用
<template>
<ve-histogram
:data="chartData"
:extend="chartExtend">
</ve-histogram>
</template>
<script>
export default {
data () {
this.chartExtend = {
series (v) {
v.forEach(i => {
i.barWidth = 10
})
return v
},
tooltip (v) {
v.trigger = 'none'
return v
}
}
/* 等同于
this.chartExtend = {
series: {
barWidth: 10
},
tooltip: {
trigger: 'none'
}
}
等同于
this.chartExtend = {
'series.0.barWidth': 10,
'series.1.barWidth': 10,
'tooltip.trigger': 'none'
}
*/
return {
chartData: {
columns: ['日期', '成本', '利润'],
rows: [
{ '日期': '1月1日', '成本': 15, '利润': 12 },
{ '日期': '1月2日', '成本': 12, '利润': 25 },
{ '日期': '1月3日', '成本': 21, '利润': 10 },
{ '日期': '1月4日', '成本': 41, '利润': 32 },
{ '日期': '1月5日', '成本': 31, '利润': 30 },
{ '日期': '1月6日', '成本': 71, '利润': 55 }
]
}
}
}
}
</script>
crypto-js
在request拦截器中使用HMAC-SHA1加密
// 请求拦截器
zyService.interceptors.request.use(config => {
// 授权码
const assessKey = 'sCm0fN3P9lw42eTStzC3qA=='
// 密匙
const secretKey = '179d5ddb-5a8f-4322-b23a-267f62625a36'
// 获取时间戳
let timestamp = new Date().getTime()
// 获取请求方式,请求接口url
let { method, url } = config
let StringToSign = method + '|' + url + '|' + timestamp
// SHA1加密
const SHA1Arr = hmacSHA1(StringToSign, secretKey)
// Base64再次加密
const Sign = SHA1Arr.toString(crypto.enc.Base64)
// 请求头授权码
const Authorization = 'HM ' + assessKey + ':' + Sign + 'lr12'
console.log(Authorization)
// get 参数序列化 空格 数组
if (config.method === 'get') {
config.paramsSerializer = function (params) {
return stringify(params, { indices: false })
}
}
config.headers['Content-Type'] = 'application/json; charset=UTF-8'
config.headers['X-HM-Timestamp'] = timestamp
config.headers['Authorization'] = Authorization
// 携带token
if (config.url.indexOf('/login') === -1) {
const token = util.cookies.get('token')
config.headers['token'] = token
}
console.log(config.headers)
// 返回中间件
return config
},
?.
-
安装依赖 npm install @babel/plugin-proposal-optional-chaining --save
-
在babel.config.js中 的 plugins中添加 @babel/plugin-proposal-optional-chaining
module.exports = {
presets: ['@vue/app'],
plugins: ['@babel/plugin-proposal-optional-chaining']
}
异常打印
打印异常
function errorCreate (msg) {
//创建一个异常实例
const err = new Error(msg)
//添加到日志
this.$store.state.dispatch('errorAdd', {type: 'error',err,info: '数据请求异常'})
//开发环境打印
if (process.env.NODE_ENV === 'development') console.log(err)
//界面抛出提示
this.$message.error(err)
//控制台抛出移除,【打印err的发生位置】
throw err
}
errorCreate(123)
编码规范
注释
页面
<!--
创建人:E00n
页面标题:医生问诊
开发者:E00n
-->
函数
/**
* 医生科室列表
* @param null
*/
export function docDeps () {
return request({
url: '/v1/doctor/department'
})
}
/**
* 确认就诊
* @param registerId 挂号id
*/
export function confirmVisit (registerId) {
return request({
url: '/v1/doctor/confirmVisit',
method: 'put',
data: registerId
})
}
字符串
静态字符串使用’'单引号,动态使用``反引号
const a = 'foo'
const b = `bar${a}`
对象
对象尽量静态化,不要随意添加属性,添加的话最好使用Object. assign
动态属性尽量在创建时定义好
//bad
const a = {}
a.x = 1
a[getKey('enabled')] = true;
//good
const a = {}
Object.assign(a,{x:1,[getKey('enabled')]:true})
//best
const a ={x:null,[getKey('enabled')]: true}
a.x = 1
vue
目录
api:接口
assets:静态资源
components:常用插件(小型插件)
layout:骨架
libs:架包|引入的工具包
menu:导航菜单
plugin:插件
router:路由
views:试图,页面
App.vue
main.js
vuex
modules:当设置namespaced属性为true时,会开启模块空间
namespaced: true
//state
this.$store.state.模块名.模块属性
//mutations
this.$store.commit('模块名/模块方法','参数')
computed: {
...mapState('taadmin/user', ['info']),
...mapState(['clinicInfo'])w'f
},
element-ui
<el-table
:data="tempList"
stripe
style="width: 100%"
height="500"
@expand-change="toggle"
:row-key="getRowKeys"
:expand-row-keys="expands"
>
<el-table-column
type="expand"
label="展开"
width="60"
>
123
</el-table-column>
</el-table>
//expend只展开一行
//data-表格
tempList: [],
getRowKeys (row) { // 行数据的Key
return row.id
},
expands: [] // 通过该属性设置Table目前的展开行,需要设置row-key属性才能使用,该属性为展开行的keys数组
//methods- 切换行
toggle (row, expandedRows) {
this.expands = []
if (expandedRows.length > 0 && row) {
this.expands.push(row.id)
}
},
中台项目
js-cookie
import Cookie from "js-cookie";
const cookie = {};
//存储cookie
cookie.set = function(name = "default", value = "", setting = {}) {
var realSetting = {
expires: 1
};
Object.assign(realSetting, setting);
return Cookie.set(`ta-${name}`, value, realSetting);
};
//拿到cookie
cookie.get = function(name = "default") {
return Cookie.get(`ta-${name}`);
};
//全部的cookie值
cookie.getAll = function() {
return Cookie.get();
};
//移除cookie
cookie.remove = function(name = "default") {
return Cookie.remove(`ta-${name}`);
};
export default cookie;
axios
import axios from "axios";
import { Message } from "element-ui";
import cookie from "./cookie";
import { stringify } from "qs";
// 开发环境显示错误信息
function errorLog(err) {
if (process.env.NODE_ENV === "development") {
console.log(err);
Message({ message: err.message, type: "error", duration: 5 * 1000 });
}
}
function createService(baseURL = "127.0.0.1") {
// 创建一个 axios 实例
const service = axios.create({
baseURL,
timeout: 30000 // 请求超时时间
});
// 请求拦截器
service.interceptors.request.use(
config => {
// get 参数序列化 空格 数组
if (config.method === "get") {
config.paramsSerializer = function(params) {
return stringify(params, { indices: false });
};
}
config.headers["Content-Type"] = "application/json; charset=UTF-8";
// 在请求发送之前做一些处理
if (config.url.indexOf("/login") === -1) {
config.headers["token"] = cookie.get("token");
}
return config;
},
error => {
errorLog(error);
}
);
// 响应拦截器
service.interceptors.response.use(
response => {
// dataAxios 是 axios 返回数据中的 data
const dataAxios = response.data;
// 这个状态码是和后端约定的
const { code } = dataAxios;
// 根据 code 进行判断
if (code === undefined || response.config.isOut) {
return dataAxios;
} else {
// 有 code 代表这是一个后端接口 可以进行进一步的判断
switch (code) {
case 200:
return dataAxios.data;
case 201:
console.log(dataAxios.message);
break;
default:
console.log(dataAxios.message);
break;
}
}
},
error => {
if (error && error.response) {
switch (error.response.status) {
case 400:
error.message = "请求错误";
break;
case 401:
error.message = "未授权,请登录";
cookie.remove("token");
window.location = "/#/login";
break;
case 403:
error.message = "拒绝访问";
break;
case 404:
error.message = "请求地址出错";
break;
case 408:
error.message = "请求超时";
break;
case 500:
error.message = "服务器内部错误";
break;
case 501:
error.message = "服务未实现";
break;
case 502:
error.message = "网关错误";
break;
case 503:
error.message = "服务不可用";
break;
case 504:
error.message = "网关超时";
break;
case 505:
error.message = "HTTP版本不受支持";
break;
default:
break;
}
}
errorLog(error);
return Promise.reject(error);
}
);
return service;
}
export default createService;
vue.config.js
const path = require("path");
const TerserPlugin = require("terser-webpack-plugin");
const px2rem = require("postcss-px2rem");
const postcss = px2rem({
// 基准大小 baseSize,需要和rem.js中相同
remUnit: 16
});
module.exports = {
publicPath: "./", // 基本路径
outputDir: "dist", // 输出文件目录
lintOnSave: false, // eslint-loader 是否在保存的时候检查
// webpack配置
configureWebpack: config => {
if (process.env.NODE_ENV === "production") {
// 为生产环境修改配置...
config.mode = "production";
// 将每个依赖包打包成单独的js文件
let optimization = {
runtimeChunk: "single",
splitChunks: {
chunks: "all",
maxInitialRequests: Infinity,
minSize: 20000,
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name(module) {
// get the name. E.g. node_modules/packageName/not/this/part.js
// or node_modules/packageName
const packageName = module.context.match(
/[\\/]node_modules[\\/](.*?)([\\/]|$)/
)[1];
// npm package names are URL-safe, but some servers don't like @ symbols
return `npm.${packageName.replace("@", "")}`;
}
}
}
},
minimizer: [
new TerserPlugin({
cache: true,
parallel: true,
sourceMap: true,
terserOptions: {
compress: {
warnings: false,
drop_console: true, // console
drop_debugger: false,
pure_funcs: ["console.log"] // 移除console
}
}
})
]
};
Object.assign(config, {
optimization
});
} else {
// 为开发环境修改配置...
config.mode = "development";
}
Object.assign(config, {
// 开发生产共同配置
resolve: {
alias: {
"@": path.resolve(__dirname, "./src"),
"@c": path.resolve(__dirname, "./src/components"),
"@api": path.resolve(__dirname, "./src/api")
} // 别名配置
}
});
},
productionSourceMap: true, // 生产环境是否生成 sourceMap 文件
// css相关配置
css: {
sourceMap: false,
loaderOptions: {
css: {},
scss: {},
postcss: {
plugins: [postcss]
}
},
requireModuleExtension: false
},
parallel: require("os").cpus().length > 1, // 是否为 Babel 或 TypeScript 使用 thread-loader。该选项在系统的 CPU 有多于一个内核时自动启用,仅作用于生产构建。
pwa: {}, // PWA 插件相关配置
// webpack-dev-server 相关配置
devServer: {
open: process.platform === "darwin",
host: "0.0.0.0", // 允许外部ip访问
port: 8022, // 端口
https: false, // 启用https
overlay: {
warnings: true,
errors: true
}, // 错误、警告在页面弹出
proxy: {
"/api": {
target: "http://www.baidu.com/api",
changeOrigin: true, // 允许websockets跨域
ws: true, //ws跨域
pathRewrite: {
"^/api": ""
}
}
} // 代理转发配置,用于调试环境
},
// 第三方插件配置
pluginOptions: {}
};
px2rem
// 基准大小
const baseSize = 16;
// 设置 rem 函数
function setRem() {
// 当前页面宽度相对于 1920宽的缩放比例,可根据自己需要修改。
const scale = document.documentElement.clientWidth / 1920;
// 设置页面根节点字体大小(“Math.min(scale, 2)” 指最高放大比例为2,可根据实际业务需求调整)
document.documentElement.style.fontSize =
baseSize * Math.min(scale, 2) + "px";
}
// 初始化
setRem();
// 改变窗口大小时重新设置 rem
window.onresize = function() {
setRem();
};