遇到的坑
Vue
-
坑点:当我在layout布局页面中为块级元素使用
class=header
的标签后,所有匹配逻辑会失效,查元素检查器发现该layout被直接应用在页面最外层(Vue甚至没有挂载到App)解决方法:换个自定义class名
* 解决方法 * 学习
Vue数组变动的渲染
深入Vue响应式原理 :出于性能的考虑,Vue不会对
数组[index]= newVal
数组.length=newLength
进行响应,需要使用1
2vue.$set(array,index,newVal)
vm.items.splice(newLength)等写法触发状态更新。
在实际应用中,我们可以在数据改变时利用
@change
等钩子函数同步一下即可。如下所示1
2
3
4handleAnswerChange(index: number): void {
// 解决Vue响应式原理中数组更新视图不渲染问题
this.$set(this.array, index, this.array[index])
}自己编写的可拖拽组件:查看代码
利用ElementUI的照片墙样式,定制自己的图片上传组件:根据限制数量限制图片上传按钮的出现,自定义
http-request
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42<template>
<div class="upload-pic">
<el-upload
action=""
list-type="picture-card"
:file-list="imgFileList"
:limit="limit"
:class="{ hide: hideUpload }"
ref="upload"
name="files"
:on-preview="handlePreview"
:before-upload="handleBeforeUpload"
:http-request="handleUpload"
>
<i slot="default" class="el-icon-plus"></i>
<div slot="file" slot-scope="{ file }">
<img class="el-upload-list__item-thumbnail" :src="file.url" />
<span class="el-upload-list__item-actions">
<span
class="el-upload-list__item-preview"
@click="handlePreview(file)"
>
<i class="el-icon-zoom-in"></i>
</span>
<span
v-if="!disabled"
class="el-upload-list__item-delete"
@click="handleRemove(file)"
>
<i class="el-icon-delete"></i>
</span>
</span>
</div>
<div v-if="slotText" slot="tip" class="el-upload__tip">
{{ slotText }}
</div>
</el-upload>
<el-dialog :visible.sync="dialogVisible">
<img width="100%" :src="dialogImageUrl" />
</el-dialog>
</div>
</template>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94<script lang="ts">
/**
* @file 带缩略图的单张照片墙组件
* @todo 如果需要扩展为多张照片墙,需修改imgUrl的类型为Array<string>
*/
import { IObject } from "@/@types/common"
import { uploadFileUser } from "@/api/api"
import { Component, Vue, Prop, Watch } from "vue-property-decorator"
import { InnerFile } from "@/@types/common"
@Component
export default class UploadPic extends Vue {
@Prop()
imgType!: string
@Prop()
imgUrl!: string
@Prop({ default: 1, required: false })
limit!: number
@Prop({ default: "该值", required: false })
validateText!: string
@Prop({ default: "", required: false })
slotText!: string
dialogImageUrl = ""
dialogVisible = false
hideUpload = false
disabled = false //是否显示删除按钮
imgFileList: InnerFile[] = []
mounted(): void {
this.handleimgUrl()
}
handleimgUrl(): void {
if (this.imgUrl && this.imgUrl != "defaultimg") {
const fileName = this.imgUrl.match("[^/]+(?!.*/)")
const fileType = fileName ? fileName[0] : ""
const name = this.$store.getters.userInfo.orgId
this.imgFileList.push({
name: this.imgType + "_" + name + "_" + fileType,
url: this.imgUrl
})
this.hideUpload = true
}
}
handleUpload(config: IObject): void {
// 上传时不显示多余的上传框
this.hideUpload = true
let fd = new FormData()
fd.append("businessName", "organization")
fd.append("file", config.file)
fd.append("type", this.imgType)
uploadFileUser(fd).then(res => {
if (res.code === 200) {
this.$message.success("上传成功")
this.$emit("update:imgUrl", res.data)
} else {
this.$message.error(res.msg)
this.hideUpload = false
}
})
}
handlePreview(file: InnerFile): void {
this.dialogImageUrl = file.url ? file.url : ""
this.dialogVisible = true
}
handleRemove(file: InnerFile): void {
const el = this.$refs.upload as IObject
el.handleRemove(file)
this.$emit("update:imgUrl", "")
// HACK 删除后才加载添加框 safari加载520,chrome加载1000
// setTimeout(() => {
this.hideUpload = false
// }, 520)
}
handleBeforeUpload(file: File): boolean {
const isPic = file.type === "image/png" || "image/jpeg"
const isLt2M = file.size / 1024 / 1024 < 10
if (!isPic) {
this.$message.error("上传图片只能是 JPG 或 PNG 格式!")
}
if (!isLt2M) {
this.$message.error("上传头像图片大小不能超过 10MB!")
}
return isPic && isLt2M
}
}
</script>1
2
3
4
5
6
7<style lang="scss">
.upload-pic {
.hide .el-upload--picture-card {
display: none;
}
}
</style>
JavaScript
可拖动组件
坑点:鼠标拖拽绑定到元素会有卡顿,触屏拖拽不会
解决方法:绑定到窗口
窗口自适应变化
1
2
3
4
5
6
7
8
9<template>
<div
class="drag__wrapper"
ref="dragRef"
:style="{ top: pos.y + 'px', left: pos.x + 'px' }"
>
<slot name="float" />
</div>
</template>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130<script lang="ts">
import { env } from "echarts"
import { Component, Vue, Prop } from "vue-property-decorator"
@Component
export default class FloatCounter extends Vue {
@Prop({
required: false
})
position!: {
top: number
left: number
}
@Prop({
required: true
})
dragId!: string
pos = {
x: 0,
y: 0
}
// 可拖拽边界值
maxX = 0
maxY = 0
isDown = false
mounted(): void {
// 组件大小
const el = this.$refs.dragRef as Element
const rect = el.getBoundingClientRect()
// 组件父元素位置
const elParent = el.parentElement as HTMLElement
// 组件能移动的位置
this.maxX = document.body.clientWidth - rect.width
this.maxY = document.body.clientHeight - rect.height
if (!this.position) {
this.handleSlide(
elParent.offsetLeft + elParent.clientWidth - rect.width,
elParent.offsetTop + elParent.clientHeight - rect.height
)
} else {
this.handleSlide(this.position.left, this.position.top)
}
const drag = document.getElementById(this.dragId) as HTMLElement
let isMove = false
let mouseX = 0
let mouseY = 0
// 鼠标拖拽 绑定到document,否则容易出现延迟
drag.onmousedown = e => {
isMove = true
const event = e || window.event
const rect = el.getBoundingClientRect()
this.maxX = document.body.clientWidth - rect.width
this.maxY = document.body.clientHeight - rect.height
// 获取鼠标的位置,兼容多浏览器
mouseX = event.pageX ? event.pageX : event.clientX
mouseY = event.pageY ? event.pageY : event.clientY
// 获取当前元素位置
mouseX -= drag.getBoundingClientRect().left
mouseY -= drag.getBoundingClientRect().top
document.onmousemove = e => {
if (isMove) {
const event = e || window.event
const ox = event.pageX ? event.pageX : event.clientX
const oy = event.pageY ? event.pageY : event.clientY
this.handleSlide(
Math.max(0, Math.min(ox - mouseX, this.maxX)),
Math.max(0, Math.min(oy - mouseY, this.maxY))
)
}
}
}
document.onmouseup = () => {
isMove = false
document.onmousemove = null
}
// 触屏拖拽 绑定到对象本身
drag.addEventListener("touchstart", e => {
isMove = true
const rect = el.getBoundingClientRect()
this.maxX = document.body.clientWidth - rect.width
this.maxY = document.body.clientHeight - rect.height
// 获取触点的位置
const event = e || window.event
const touch = event.touches[0]
mouseX = touch.clientX - drag.getBoundingClientRect().left
mouseY = touch.clientY - drag.getBoundingClientRect().top
})
drag.addEventListener("touchmove", e => {
if (isMove) {
const event = e || window.event
const touch = event.touches[0]
const ox = touch.pageX ? touch.pageX : touch.clientX
const oy = touch.pageY ? touch.pageY : touch.clientY
this.handleSlide(
Math.max(0, Math.min(ox - mouseX, this.maxX)),
Math.max(0, Math.min(oy - mouseY, this.maxY))
)
e.preventDefault()
}
})
drag.addEventListener("touchend", () => {
isMove = false
})
// 自适应窗口变化
window.onresize = () => {
return (() => {
const rect = el.getBoundingClientRect()
this.maxX = document.body.clientWidth - rect.width
this.maxY = document.body.clientHeight - rect.height
if (!this.position) {
const elParent = el.parentElement as HTMLElement
this.handleSlide(
elParent.offsetLeft + elParent.clientWidth - rect.width,
elParent.offsetTop + elParent.clientHeight - rect.height
)
}
})()
}
}
// 移动组件位置
handleSlide(desX: number, desY: number): void {
this.pos.x = desX
this.pos.y = desY
}
}
</script>
ElementUI
选择框
el-radio:选项超出宽度自动换行,换行后自动缩进和顶端对齐:点击跳转到样式部分查看代码
el-checkbox:存在bug,使用时需绑定
checked
选中状态才能实时响应1
2
3
4
5
6
7
8
9
10<el-checkbox
v-for="answer in question.answer"
:key="answer.option"
:label="answer.option"
class="horizon"
:checked="checked"
@change="checked = !checked"
>
{{ answer.content }}
</el-checkbox>
El-Image:相对路径引用
1
2
3
4<el-image
:src="require('@/assets/questionair/top.png')"
fit="fill"
></el-image>
样式
块元素的垂直居中:老生常谈
网络上大量“7种垂直居中的方法”“14种垂直居中的方法”,实际运用中大量绝对定位的方法都可以忽略不计。常用的几种方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20// span块
.span{
display: inline-block; // 转为块元素
text-align: center; // 水平居中
line-height:值为所需对齐的div块高度; // 垂直居中
}
// flex布局
.divParent{
display: flex;
justify-content: center; // 水平
align-items: center; // 垂直
}
// 定位居中
.name{
background:#eee;
position:absolute;
left:50%;
top:50%;
transform: translate(-50%,-50%);
}其他还有:利用table的方法
文本溢出自动换行,应用
white-space
属性1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16.el-radio__label {
font-size: 14px;
color: #5e6166;
line-height: 22px;
// 解决选项溢出问题
width: 100%;
text-overflow: ellipsis;
white-space: normal;
// 解决溢出行缩进
display: inline-block;
vertical-align: top;
}
.el-radio__input {
// 对其label文字
margin-top: 2px;
}-
字体衬线导致即使设置了字体的
line-height
与块元素height
相同,使用verticle-align:middle
后两者也不能再同一水平线上。可以使用顶端对齐块元素向下偏移2px的方法
组件
排序拖拽组件:sortable draggable
markdown组件