1.根据对象的某个属性排序
① sort()会改变原数组。
② sort()在不传入参数时默认升序排列。
③ 若想自定义排序,如降序排列,按照对象某个属性排序等,就必须往sort()中传入一个回调函数作为参数。
var arr = [
{name:'zopp',age:0},
{name:'gpp',age:18},
{name:'yjj',age:8}
];
//定义一个比较器
function compare(prop){
// 默认传入两个参数,即为数组中要比较的两项
return function(a,b){
var value1 = a[prop];
var value2 = b[prop];
// 通过返回值的正负来排序,返回值必须是数字类型
return value1 - value2;
}
}
arr.sort(compare('age'))
console.log(arr)
2.render函数渲染DOM结构
示例:
https://blog.csdn.net/qq_36711388/article/details/88980908
在vue-easytable组件中,调用renderBodyCell函数,自定义表格内容。生成两个span标签:
renderBodyCell: ({ row, column, rowIndex }, h) => {
return h(
'span', [h('span', {
domProps: {
innerHTML: '编辑'
},
style: {
color: 'blue',
marginRight: '4px',
cursor: 'pointer'
},
on: {
click: () => {
this.$message('编辑')
}
}
}), h('span', {
domProps: {
innerHTML: '作废'
},
style: {
color: 'blue',
cursor: 'pointer'
},
on: {
click: () => {
this.$message('作废')
}
}
})]
);
},
3. 富文本编辑器
(1)vue2.0
https://www.cnblogs.com/wjlbk/p/12884661.html
(2)vue3.0 quill富文本编辑器
vue-admin-plus: https://chu1204505056.gitee.io/admin-plus/#/vab/richTextEditor
Quill文档:https://www.kancloud.cn/liuwave/quill/1409423
注意:获取富文本编辑器实例时,要用 var quill = this.$refs.quillRef2.editor.__quill
// 41.富文本上传图片成功
quillSucess(res) {
var quill = this.$refs.quillRef1.editor.__quill
let length = quill.getSelection(true).index
// 插入图片
quill.insertEmbed(length, 'image', res.data.pic_cover)
// 调整光标到最后
quill.setSelection(length + 1)
},
(3)tinymce富文本编辑器
参考来源:tinymce
仅在工具栏添加自定义按钮:
先在工具栏配置项中添加 按钮名,然后在 init 函数中配置下面的方法。
setup: (editor) => {
editor.ui.registry.addButton(‘mybutton’, {
// 按钮名
text: '图片',
// icon: false,
//点击事件
onAction: () => {
console.log('点击')
}
})
}
封装的组件:
数据双向绑定问题和光标问题:https://blog.csdn.net/tonywu1992/article/details/82953577
<template>
<div class="tinymceDiv">
<textarea :id="id" v-model="content"></textarea>
<el-button @click="click">测试</el-button>
<!-- 富文本弹框 -->
<vab-upload
ref="vabUpload"
:limit="1"
name="picture"
:size="2"
url="/upload"
@quillSucess="quillSucess($event)"
/>
</div>
</template>
<script>
import VabUpload from '@/extra/VabUpload'
import tinymce from 'tinymce/tinymce'
//这下面是tinymce的插件
//5.0+版本会页面显示not Found,引入图标可以解决这个问题 2021.11.11添加
import 'tinymce/icons/default/icons.js' // 引入图标包 icons.js
import 'tinymce/themes/silver/theme'
import 'tinymce/plugins/image' // 插入上传图片插件
import 'tinymce/plugins/media' // 插入视频插件
import 'tinymce/plugins/table' // 插入表格插件
import 'tinymce/plugins/lists' // 列表插件
import 'tinymce/plugins/wordcount' // 字数统计插件
import 'tinymce/plugins/code' //显示源代码插件
import 'tinymce/plugins/advlist' // 这几条引入是因为我的导入不了,不知道为啥
import 'tinymce/plugins/link'
import 'tinymce/plugins/textcolor'
import 'tinymce/plugins/paste'
import 'tinymce/plugins/colorpicker'
import 'tinymce/plugins/contextmenu'
import axios from 'axios'
import { getPicList } from '@/api/goodsGroupList'
//这里写你自己存放语言包的路径
import '@/assets/lang/zh_CN.js'
export default {
name: '',
components: { VabUpload },
props: {
id: String,
tinyVal: String, //内容绑定
},
data() {
return {
flag: true,
content: '子组件内容',
uploadHeader: {
'X-Auth-Token': this.$store.state.user.token,
'Access-Shop-Id': this.$store.state.user.shopId,
},
album_id: '',
init: {
selector: '#' + this.id,
language: 'zh_CN',
// skin_url: 'tinymce/skins/ui/oxide',
//插件-实现插入图片等功能
plugins:
'link lists image code table colorpicker textcolor wordcount contextmenu',
//工具栏-根据自己需要增减功能
toolbar:
'bold italic underline strikethrough | fontsizeselect | forecolor backcolor | alignleft aligncenter alignright alignjustify | bullist numlist | outdent indent blockquote | undo redo | link unlink mybutton code | removeformat',
branding: false,
menubar: true, //顶部菜单栏显示
min_height: 500, //高度
statusbar: false,
setup: (editor) => {
// 1.定义按钮,
editor.ui.registry.addButton('mybutton', {
// 按钮名
text: '图片',
// icon: false,
onAction: () => {
this.$refs['vabUpload'].handleShow()
},
})
// 2.监听富文本的变化
editor.on('input undo redo execCommand', async () => {
this.flag = false
// 获取富文本最新内容
const content = window.tinymce.activeEditor.getContent()
await this.$emit('update:tinyVal', content)
})
},
},
}
},
watch: {
tinyVal: {
handler: function (val) {
if (this.flag) {
tinymce.get(this.id).setContent(val) //动态设置内容
}
this.flag = true
},
},
},
created() {
this.getPicList()
},
mounted() {
//配置的初始化
tinymce.init(this.init)
},
beforeUnmount() {
//销毁
tinymce.get(this.id).destroy()
},
methods: {
click() {
this.$nextTick(() => {
console.log(this.content)
})
},
/**外部调用该方法,可以拿到绑定数据*/
release() {
//content 是文本内容带标签
let content = tinymce.get(this.id).getContent()
// getContent( { 'format' : 'text'} );//这是获取里面的文本文件,不带标签
return content
},
/**外部调用该方法,可以修改绑定数据*/
setData(data) {
tinymce.get(this.id).getContent(data)
}, //数据回填
// 33.获取相册列表
async getPicList() {
const { data } = await getPicList()
const item = data.data.find((item) => {
return item.album_name == '默认相册'
})
this.album_id = item.album_id
},
// 43.富文本上传图片成功
quillSucess(res) {
window.tinymce
.get(this.id)
.insertContent(`<img src="${res.data.pic_cover}" >`)
const content = window.tinymce.activeEditor.getContent()
this.$emit('update:tinyVal', content)
},
},
}
</script>
<style>
.tinymceDiv {
width: 100%;
height: 100%;
}
</style>
4.$refs找不到元素
dialog弹出框中的元素,用$refs获取不到,即使加了 nextTick()方法也不行。
此时可以在dailog的 open 方法的回调函数中获取。
showDialog() {
this.$nextTick(() => {
console.log(this.$refs.tableRef)
})
},
5.element UI表格嵌套上传组件
<el-table-column align="center" label="sku图片">
<template #default="{ row }">
<el-upload
ref="specPicRef"
action="https://muyang.api.zhidingtong.com/admin/album/picture/upload"
:auto-upload="true"
:before-upload="beforeUpload"
class="specUpload"
:data="{ album_id: album_id }"
:headers="uploadHeader"
list-type="picture-card"
name="picture"
//重点在于可以通过这种方式拿到表格当前行的数据row,和上传的内容res
:on-success="(res) => specPicSuccess(res, row)"
:show-file-list="false"
>
<template #default>
<i class="el-icon-plus"></i>
</template>
</el-upload>
</template>
</el-table-column>
6. sku列表-生成所有排列组合
let arr = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
[10, 11, 12],
];
function cartesianProductOf() {
return Array.prototype.reduce.call(arguments, function(a, b) {
var ret = [];
a.forEach(function(a) {
b.forEach(function(b) {
ret.push(a.concat([b]));
});
});
return ret;
}, [[]]);
}
let allArr =cartesianProductOf(...arr )
console.log(allArr) // 得到的结果就是所有的排列组合组成的数组
7. 编辑页面离开时销毁对应标签
this.$store.dispatch('tabs/delVisitedRoute', 'marketing/updatediscount')
8. 本地服务启动
1.打开phpStudy,启动mysql
2.打开Navicat Premium,创建数据库,运行sql文件生成表。
3.在sql文件所在目录,打开cmd,运行npm start启动本地服务。
9.参数传递
在用v-for渲染数据时,给循环单项绑定事件时,用 $$event 传递 事件对象形参,用$index传递当前索引形参。
10.在使用element-ui的无限滚动组件时,必须给滚动的盒子高度(宽度)。
11.页面刷新时computed中内部依赖的data数据为underfine
https://blog.csdn.net/weixin_49696014/article/details/112316036
12. 颜色选择器样式
elementUI的下拉选择器、颜色选择器等组件的 下拉框 ,不是直接插入到vue组件的html结构中的。
对于颜色选择器,可以添加 popper-class 属性,并将css样式写到 "App.vue"页面中。
13.拖拽
14.nextTick(()=>{}) 也无法获取到最新DOM元素时,可以尝试在内部加上setTimeout()定时器。
nextTick(() => {
setTimeout(() => {
xxxxxxx
}, 0);
});
15.循环渲染的input表单,在输入时总是自动失焦,可能跟key值有关。
16. :deep() ,v-deep,::deep
17. vue.cli的webpack反向代理
(1)为什么存在跨域?
当下,最流行的就是前后分离项目,也就是前端项目和后端接口并不在一个域名之下,那么前端项目访问后端接口必然存在跨域的行为。
(2)跨域分几种情况?
跨域分为 开发环境的跨域 和 生产环境的跨域。前端人员在本地开发,与后端接口存在跨域;上线后,项目在腾讯、百度的服务器上,与后端接口也存在跨域。
(3)如何解决开发环境的跨域?
开发环境的跨域,也就是在vue-cli脚手架环境下开发启动服务时,我们访问接口所遇到的跨域问题,vue-cli为我们在本地开启了一个服务,可以通过这个服务帮我们代理请求,解决跨域问题。(本机请求远程服务器时会因为跨域问题请求失败,改为本机请求webpack启动的本地后端服务,再由本地后端服务请求远程服务器)
(4)具体配置。
module.exports = {
devServer: {
// 代理配置
proxy: {
// 1.代码中所有包含"/api"的接口都会被匹配到
'/api': {
target: "http://192.168.4.164:8080", // 2.远程服务器地址
changeOrigin: true, // 3.是否跨域 需要设置此值为true 才可以让本地服务代理我们发出请求
pathRewrite: {
'^/api': '' // 4.固定写法,如果真正请求的服务器接口url中不包含"/api",此配置可以去除url中的"/api"。
}
},
}
}
}
① 在vue.config.js配置文件中,添加反向代理的配置内容----> proxy对象。
② api:代码中的所有接口都应该包含"/api",包含"/api"的接口才会被匹配到。
③ target:真正的服务器接口url,需包含协议、域名、端口号。
④ changeOrigin:是否跨域。值为true时,才能让本地服务的后端代替我们发出请求。
⑤ pathRewrite:固定写法。如果真正的服务器接口url中不包含"/api",此配置可以去除url中的"/api"。
⑥ 代码中的接口都以 "/api" 开头,再把axios中的 baseUrl属性 设置为空字符串;
或者代码中的接口只包含此接口的特有部分,然后将axios的 baseUrl属性 设置为 "/api",这样可以为所有接口加上统一的前缀 "/api"。如下:
const service = axios.create({
baseURL:"/api", // 1.为所有接口添加api前缀
timeout: 10000
})
export function getDeviceInfo(params) {
return request({
url: '/myExample', // 2.接口url中不需要再以/api开头
method: 'get',
params
})
}
⑦ 如上接口,代码中的接口url是 "/api/myExample",我们会请求本地服务 "http://localhost/api/myExample",本地服务会代理到真实的服务器"http://192.168.4.164:8080/myExample"
注意:修改配置后需重启项目才生效。
https://segmentfault.com/a/1190000043775780
- 组件库中,组件的 属性、事件、方法。v-bind 、v-on 、this.$refs
18. 项目引入已配置好的mock。
1.创建自己的项目,重点在于填写baseurl。
http://172.18.255.8:9999/
2.代码中,在vue.config.js中,反向代理处,添加mock的反向代理配置。
proxy: {
'/mock': {
target: 'http://172.18.255.8:38880/mock',
changeOrigin: true,
rewrite: (paths) => paths.replace(/^\/mock/, ''),
},
},
3.每个接口,传参时传入mock参数,如果mock为true,则在接口地址"/api"前面加上"/mock"。这样,代理就会走mock的反向代理。
4.mock的一些api:http://mockjs.com/examples.html#String
19.select框不显示placehoder提示内容
绑定的value值设置为undefined即可。
20.contains方法
用于判断Dom元素father是否包含Dom元素child,返回 布尔值。
const father= document.querySelector('.father');
const child= document.querySelector('.child');
const flag = father.contains(child)
应用:点击toolTip框之外的部分,隐藏toolTip框;点击toolTip框本身则保持为显示状态。
// 页面文档添加点击事件的监听
document.addEventListener('click', (e) => {
const toolTipDom = document.querySelector('.toolTip');// 获取toolTip框Dom元素
if(toolTipDomDom && !toolTipDom.contains(e.target)){
//关闭toolTip框
}
});
21. router.push()方法更新当前页面路由,无法实现页面更新
问题:
1.进入详情页时,地址上带了query参数。
2.处理完当前页的内容后,想要通过router.push()更新当前页的query参数。
3.发现页面路由地址虽然更新了,页面数据不更新。
解决:
1.先从详情页router.replace()到一个空页面。
2.在空页面中再次router.replace()到详情页即可。
3.router.replace()方法,不会再浏览器中留下记录,即不能后退到空白页。
22.svg 图标
<svg class="icon" aria-hidden="true">
<use xlink:href="#icon-fuzhi" />
</svg>
23.不想让滚动条挤到元素
overflow-y: overlay;
24.下载代码中的文件
1.在public目录下新建static文件夹,把文件放到里面
// 下载模板
const downloadTemplate = () => {
const a = document.createElement('a');
a.href = '/static/批量绑定邮箱模板.xlsx';
a.download = '批量绑定邮箱模板.xlsx';
a.style.display = 'none';
document.body.appendChild(a);
a.click();
a.remove();
};
25.样式混入
如果要定义一个公共样式,其他元素都能复用:scss语法可以用@mixin + @include方式;less语法可以用.aaa(){} 方法定义公共样式,用.bbb{.aaa()}进行调用。
26.对象、函数和类
- 在 对象中,一般都是要使用 键值对 的形式,但也可以 省略function 关键字,直接写入一个函数。
- 中间用 冒号 连接,结尾用 逗号 分隔。
(1)vue2.0中的 export default{} 一个配置对象。
export default {
data(){}, // 常见写法
data: function() {}, // 也可以这样
}
- 在 函数体内 、 <script></script>标签内,要使用const、let、function来声明变量与方法(普通函数用function声明,匿名函数用const声明常量接收)。
- 中间用 等号 连接,结尾用 分号 分隔。
function a(){};
const b = ()=>{};
- 在 class类中 不能使用const、let关键字。
- 中间用 等号 连接结尾用 分号 分隔。
class A {
a(){}; // 常见写法:该方法被放到类的原型对象上
b = function(){}; // 函数表达式形式:该方法被直接被放到类上,而不是类的原型对象上。
c = 1; // 类中可以直接写 赋值语句,就是往类的 实例对象 上直接追加一个属性。
}
27.axios请求头headers
Content-Type值为application/x-www-form-urlencoded;charset=UTF-8时,参数需要用JSON.stringify()或qs.stringify()转为JSON格式。Content-Type值为 application/json 时,参数不要再做处理。
28.Content-Type
1.文件上传时,接口报错500,但是用ponstman可以上传成功。
2.普通接口报错415,提示Metia Type Error。
解决方案:新建一个axios实例对象,不设置Content-Type。
// 文件导入时,不设置默认的Content-Type
export let fileHttp = axios.create({
baseURL: baseUrl,
timeout: 10000,
withCredentials: true,
})
fileHttp.interceptors.request.use(()=>{},()=>{})
fileHttp.interceptors.response.use(()=>{},()=>{})
29.post上传raw格式的参数
// request是axios的实例对象
export function saveNetworkSetting(data) {
return request.post('/network/save', data, {
headers: {
'Content-Type': 'application/json'
}
})
saveInfo(type) {
const params = {...this.form};
const arr = [params]; // raw参数一般是个数组
saveNetworkSetting(arr).then(res => {
})
}