<template>
|
<div>
|
<div class="remark">
|
<el-input
|
ref="remarksName"
|
v-model="delCursor.remarksName"
|
size="mini"
|
placeholder="图形备注"
|
@change="changeName"
|
></el-input>
|
</div>
|
|
<div class="body">
|
<div class="canvasArea">
|
<canvas
|
id="canvasDialog"
|
ref="canvasDialog"
|
width="1042"
|
height="586"
|
:style="`position:static;background:url(${snapshot_url}) 0% 0%/1042px 586px no-repeat;`"
|
></canvas>
|
<p
|
style="
|
position: absolute;
|
width: 350px;
|
left: 80px;
|
top: 90px;
|
color: white;
|
font-size: 1.5rem;
|
"
|
:style="disabled ? `display:block;` : `display:none;`"
|
>
|
批量配置方式不允许绘制区域,请选择摄像机进行区域绘制
|
</p>
|
</div>
|
|
<div class="control">
|
<el-tooltip
|
content="切换到此状态后可选中已画图形"
|
placement="left"
|
popper-class="atooltip"
|
>
|
<el-button
|
class="btn"
|
@click="changeType('0')"
|
:disabled="disableSelect"
|
:class="{ selectedBtn: type == '0' }"
|
>
|
<i class="iconfont"></i>
|
</el-button>
|
</el-tooltip>
|
<el-tooltip
|
content="直线"
|
placement="left"
|
popper-class="atooltip"
|
:class="{ selectedBtn: type == '1' }"
|
>
|
<el-button
|
class="btn"
|
@click="changeType('1')"
|
:disabled="disableLine"
|
>
|
<i class="iconfont"></i>
|
</el-button>
|
</el-tooltip>
|
<el-tooltip
|
content="矩形"
|
placement="left"
|
popper-class="atooltip"
|
:class="{ selectedBtn: type == '2' }"
|
>
|
<el-button
|
class="btn"
|
@click="changeType('2')"
|
:disabled="disableRect"
|
>
|
<i class="iconfont"></i>
|
</el-button>
|
</el-tooltip>
|
<el-tooltip
|
content="箭头"
|
placement="left"
|
popper-class="atooltip"
|
:class="{ selectedBtn: type == '4' }"
|
>
|
<el-button
|
class="btn"
|
@click="changeType('4')"
|
:disabled="disableArrow"
|
>
|
<i class="iconfont"></i>
|
</el-button>
|
</el-tooltip>
|
<el-tooltip
|
content="多边形:双击结束绘制"
|
placement="left"
|
popper-class="atooltip"
|
:class="{ selectedBtn: type == '5' }"
|
>
|
<el-button
|
@click="changeType('5')"
|
class="btn"
|
:disabled="disablePolygon"
|
>
|
<i class="iconfont"></i>
|
</el-button>
|
</el-tooltip>
|
<el-tooltip
|
content="选中图形再编辑重绘,重绘的图形保留之前的名称"
|
placement="left"
|
popper-class="atooltip"
|
>
|
<el-button class="btn" @click="edit()" :disabled="disableSelect">
|
<i class="iconfont"></i>
|
</el-button>
|
</el-tooltip>
|
<el-tooltip
|
content="先选中图形,再删除"
|
placement="left"
|
popper-class="atooltip"
|
>
|
<el-button class="btn" @click="del()" :disabled="disableSelect">
|
<i class="iconfont"></i>
|
</el-button>
|
</el-tooltip>
|
<el-tooltip
|
content="清空所有已画图形,此操作不可撤销"
|
placement="left"
|
popper-class="atooltip"
|
>
|
<el-button class="btn" @click="clear()" :disabled="disableSelect">
|
<i class="iconfont"></i>
|
</el-button>
|
</el-tooltip>
|
<el-tooltip
|
content="撤销上一步操作"
|
placement="left"
|
popper-class="atooltip"
|
>
|
<el-button
|
class="btn"
|
@click="del()"
|
:disabled="disableSelect"
|
style="border: 0px"
|
>
|
<i class="iconfont"></i>
|
</el-button>
|
<!-- <span
|
class="iconfont iconfanhui1"
|
@click="undo"
|
:disabled="disableSelect"
|
style="position:absolute;left:25px;top:400px;font-size:2rem;cursor:pointer"
|
></span>-->
|
</el-tooltip>
|
</div>
|
|
<!-- <el-button type="default" @click="undo()">撤销</el-button> -->
|
</div>
|
</div>
|
</template>
|
<script>
|
export default {
|
name: "canvasDialog",
|
data() {
|
return {
|
backImg: require("../../assets/img/baseimg.png"),
|
url: "", // canvas图片的二进制格式转为dataURL格式
|
type: "0", // 绘图状态 '0'为选中删除,'1'为画线,‘2’为画矩形,‘4’为画箭头,‘5’为画多边形
|
points: [], // 记录绘制多边形时的各点坐标,绘制多边形时由于不是一笔完成,以数组数据做始终闭合的图形,双击后录成快照,把数组内容转移到最终数据中,然后清空数组
|
pointsUndo: [], // 撤销多边形时保证实时变化,不需要move之后才能响应过来。其值为points连接了移动坐标后的数组
|
delCursor: { type: -1, index: -1, remarksName: "", id: "" }, // 删除或者修改游标,记录了点击选中的元素的类型和所在数组的索引以便删除
|
editObj: {},
|
lineIndex: 0, // 生成图形备注所用的索引,依次++
|
rectIndex: 0,
|
arrowIndex: 0,
|
polygonIndex: 0,
|
disableSelect: false,
|
disableLine: false,
|
disableRect: false,
|
disableArrow: false,
|
disablePolygon: false,
|
canvasData: {
|
// 最终传递给后台的数据
|
line: [],
|
rect: [], // {id:'uuid', name: '矩形1', location: [{ x: 20, y: 30 }, { x: 20, y: 60 }, { x: 100, y: 60 }, { x: 100, y: 30 }] }
|
arrow: [],
|
polygon: [],
|
},
|
// canvasData: this.canvasDataToChild, // 最终输出的画布坐标数据
|
originX: null, // 当前点击点x
|
originY: null, // 当前点击点y
|
canvasPic: new Image(), // 撤销用的图片
|
flag: false, // 是否开启绘制
|
canvasHistory: [], // 历史数据,以供撤销使用
|
step: -1, // 记录索引,以供撤销使用
|
c: null,
|
ctx: null,
|
inputWidth: 80,
|
oldName: "", // 用来暂存更改之前的remarksName,方便放进撤销队列里
|
};
|
},
|
mounted() {
|
this.init();
|
},
|
watch: {
|
delCursor: {
|
handler(newVal, oldVal) {
|
if (newVal.remarksName) {
|
this.inputWidth = newVal.remarksName.length * 20;
|
}
|
this.oldName = oldVal.remarksName;
|
},
|
deep: true,
|
},
|
snapshot_url: {
|
handler(newVal, oldVal) {
|
if (newVal !== oldVal) {
|
// console.log(newVal, 'canvasDialog')
|
}
|
},
|
},
|
canvasDataToChild: {
|
handler(newVal, oldVal) {
|
// console.log(newVal, '打开绘制后接收到的数据')
|
this.canvasHistory.length = 0;
|
this.step = -1;
|
this.canvasData = JSON.parse(JSON.stringify(this.canvasDataToChild));
|
this.clickSelect(this.canvasData);
|
this.indexInit();
|
// 先录个快照,不然一画线就没了
|
this.step++;
|
this.canvasHistory.push({
|
type: 0,
|
src: this.c.toDataURL("image/png"),
|
});
|
},
|
deep: true,
|
},
|
// canvasDataToChild: function(newVal, oldVal) {
|
// this.init()
|
// }
|
},
|
methods: {
|
// 初始化函数
|
init() {
|
//this.c = document.querySelector("#canvasDialog");
|
this.c = this.$refs["canvasDialog"];
|
this.ctx = this.c.getContext("2d");
|
this.drawCanvasInit();
|
this.canvasData = JSON.parse(JSON.stringify(this.canvasDataToChild));
|
this.clickSelect(this.canvasData);
|
this.indexInit();
|
// 先录个快照,不然一画线就没了
|
this.step++;
|
this.canvasHistory.push({
|
type: 0,
|
src: this.c.toDataURL("image/png"),
|
});
|
this.delCursor = { type: -1, index: -1, remarksName: "", id: "" };
|
|
console.log("画布初始化");
|
},
|
// 取消画布清除状态函数
|
cancel() {
|
this.changeType("0");
|
this.undisabled();
|
//this.delCursor = {}
|
this.delCursor = { type: -1, index: -1, remarksName: "", id: "" };
|
this.canvasHistory.length = 0;
|
this.step = -1;
|
this.canvasData = JSON.parse(JSON.stringify(this.canvasDataToChild));
|
this.clickSelect(this.canvasData);
|
this.indexInit();
|
// 先录个快照,不然一画线就没了
|
this.step++;
|
this.canvasHistory.push({
|
type: 0,
|
src: this.c.toDataURL("image/png"),
|
});
|
},
|
// 主监听流程
|
drawCanvasInit() {
|
window.addEventListener("keydown", (e) => {
|
let keyID = e.keyCode ? e.keyCode : e.which;
|
if (keyID === 82) {
|
// r键
|
this.undo();
|
}
|
});
|
this.c.addEventListener("mousedown", (e) => {
|
if (this.type !== "0") {
|
this.flag = true;
|
this.originX = e.offsetX; // 鼠标落下时的X
|
this.originY = e.offsetY; // 鼠标落下时的Y
|
if (this.type === "5") {
|
// 绘制多边形
|
this.points.push({
|
x: this.originX,
|
y: this.originY,
|
});
|
}
|
} else {
|
this.clickSelect(e);
|
}
|
});
|
this.c.addEventListener("mousemove", (e) => {
|
switch (this.type) {
|
case "1":
|
this.drawLine(e);
|
break;
|
case "2":
|
this.drawRect(e);
|
break;
|
case "4":
|
this.drawArrow(e);
|
break;
|
case "5":
|
this.drawPolygon(e);
|
break;
|
}
|
});
|
this.c.addEventListener("mouseup", (e) => {
|
switch (this.type) {
|
case "1":
|
// 直线
|
this.lineMouseUp(e);
|
break;
|
case "2":
|
// 矩形
|
this.rectMouseUp(e);
|
break;
|
case "4":
|
// 箭头
|
this.arrowMouseUp(e);
|
break;
|
}
|
});
|
this.c.addEventListener("dblclick", (e) => {
|
if (this.type === "5") {
|
// 绘制多边形
|
this.polygonDblclick(e);
|
}
|
});
|
},
|
// 清除画布函数
|
clear() {
|
// console.log("清除");
|
this.ctx.clearRect(0, 0, this.c.width, this.c.height);
|
// 清空保存的状态
|
this.url = "";
|
this.canvasData.line.length = 0;
|
this.canvasData.rect.length = 0;
|
this.canvasData.arrow.length = 0;
|
this.canvasData.polygon.length = 0;
|
this.canvasHistory.length = 0;
|
|
this.indexInit();
|
this.freedEdit();
|
this.delCursor = {};
|
this.step = -1;
|
},
|
// 修改图形名称
|
changeName() {
|
if (this.delCursor.remarksName.length <= 6) {
|
switch (this.delCursor.type) {
|
case "1":
|
this.oldName = this.canvasData.line[this.delCursor.index].name;
|
this.canvasData.line[this.delCursor.index].name =
|
this.delCursor.remarksName;
|
break;
|
case "2":
|
this.oldName = this.canvasData.rect[this.delCursor.index].name;
|
this.canvasData.rect[this.delCursor.index].name =
|
this.delCursor.remarksName;
|
break;
|
case "4":
|
this.oldName = this.canvasData.arrow[this.delCursor.index].name;
|
this.canvasData.arrow[this.delCursor.index].name =
|
this.delCursor.remarksName;
|
break;
|
case "5":
|
this.oldName = this.canvasData.polygon[this.delCursor.index].name;
|
this.canvasData.polygon[this.delCursor.index].name =
|
this.delCursor.remarksName;
|
break;
|
}
|
this.clickSelect();
|
this.step++;
|
this.canvasHistory.push({
|
type: this.delCursor.type,
|
src: this.c.toDataURL("image/png"),
|
index: this.delCursor.index,
|
name: this.oldName,
|
});
|
} else {
|
this.$notify({
|
type: "warning",
|
message: "命名长度不能超过6个字!",
|
});
|
switch (this.delCursor.type) {
|
case "1":
|
this.delCursor.remarksName =
|
this.canvasData.line[this.delCursor.index].name;
|
break;
|
case "2":
|
this.delCursor.remarksName =
|
this.canvasData.rect[this.delCursor.index].name;
|
break;
|
case "4":
|
this.delCursor.remarksName =
|
this.canvasData.arrow[this.delCursor.index].name;
|
break;
|
case "5":
|
this.delCursor.remarksName =
|
this.canvasData.polygon[this.delCursor.indexhhhhhhhhhhh].name;
|
break;
|
}
|
}
|
},
|
// 左上角输入框失去焦点后保存一张快照以作撤销之用
|
saveUrl() {
|
// console.log("保存一张快照");
|
let delEle = {};
|
this.clickSelect();
|
this.step++;
|
this.canvasHistory.push({
|
type: this.delCursor.type,
|
src: this.c.toDataURL("image/png"),
|
index: this.delCursor.index,
|
data: delEle,
|
});
|
},
|
// 删除元素
|
del() {
|
let delEle = {};
|
switch (this.delCursor.type) {
|
case "1":
|
delEle = this.canvasData.line[this.delCursor.index];
|
this.canvasData.line.splice(this.delCursor.index, 1);
|
break;
|
case "2":
|
delEle = this.canvasData.rect[this.delCursor.index];
|
this.canvasData.rect.splice(this.delCursor.index, 1);
|
break;
|
case "4":
|
delEle = this.canvasData.arrow[this.delCursor.index];
|
this.canvasData.arrow.splice(this.delCursor.index, 1);
|
break;
|
case "5":
|
delEle = this.canvasData.polygon[this.delCursor.index];
|
this.canvasData.polygon.splice(this.delCursor.index, 1);
|
break;
|
}
|
this.clickSelect();
|
this.step++;
|
this.canvasHistory.push({
|
type: this.delCursor.type,
|
src: this.c.toDataURL("image/png"),
|
index: this.delCursor.index,
|
deleteLength: 0, // 删除的话deleteLength为0
|
data: delEle,
|
});
|
// console.log(this.canvasHistory, "删除之后的撤销队列");
|
},
|
edit() {
|
let delEle = {};
|
switch (this.delCursor.type) {
|
case "1":
|
delEle = {
|
id: this.canvasData.line[this.delCursor.index].id,
|
name: this.canvasData.line[this.delCursor.index].name,
|
location: this.canvasData.line[this.delCursor.index].location,
|
};
|
// this.canvasData.line.splice(this.delCursor.index, 1);
|
this.canvasData.line[this.delCursor.index].location = [
|
{ x: delEle.location[0].x, y: delEle.location[0].y },
|
{ x: delEle.location[0].x, y: delEle.location[0].y },
|
];
|
break;
|
case "2":
|
delEle = {
|
id: this.canvasData.rect[this.delCursor.index].id,
|
name: this.canvasData.rect[this.delCursor.index].name,
|
location: this.canvasData.rect[this.delCursor.index].location,
|
};
|
// this.canvasData.rect.splice(this.delCursor.index, 1);
|
this.canvasData.rect[this.delCursor.index].location = [
|
{ x: delEle.location[0].x, y: delEle.location[0].y },
|
{ x: delEle.location[0].x, y: delEle.location[0].y },
|
{ x: delEle.location[0].x, y: delEle.location[0].y },
|
{ x: delEle.location[0].x, y: delEle.location[0].y },
|
];
|
break;
|
case "4":
|
delEle = {
|
id: this.canvasData.arrow[this.delCursor.index].id,
|
name: this.canvasData.arrow[this.delCursor.index].name,
|
location: this.canvasData.arrow[this.delCursor.index].location,
|
};
|
// this.canvasData.arrow.splice(this.delCursor.index, 1);
|
this.canvasData.arrow[this.delCursor.index].location = [
|
{ x: delEle.location[0].x, y: delEle.location[0].y },
|
{ x: delEle.location[0].x, y: delEle.location[0].y },
|
];
|
break;
|
case "5":
|
delEle = {
|
id: this.canvasData.polygon[this.delCursor.index].id,
|
name: this.canvasData.polygon[this.delCursor.index].name,
|
location: this.canvasData.polygon[this.delCursor.index].location,
|
};
|
// this.canvasData.polygon.splice(this.delCursor.index, 1);
|
this.canvasData.polygon[this.delCursor.index].location = [
|
{ x: delEle.location[0].x, y: delEle.location[0].y },
|
{ x: delEle.location[0].x, y: delEle.location[0].y },
|
{ x: delEle.location[0].x, y: delEle.location[0].y },
|
];
|
break;
|
}
|
this.clickSelect();
|
this.step++;
|
this.canvasHistory.push({
|
type: this.delCursor.type,
|
src: this.c.toDataURL("image/png"),
|
index: this.delCursor.index,
|
deleteLength: 1, // 编辑的话deleteLength为1
|
data: delEle,
|
});
|
this.editObj = {
|
id: this.delCursor.id,
|
type: this.delCursor.type,
|
index: this.delCursor.index,
|
remarksName: this.delCursor.remarksName,
|
};
|
this.disabledOthers(this.delCursor.type);
|
// 切换当前状态
|
this.changeType(this.delCursor.type);
|
this.$notify({
|
type: "warning",
|
message: "已擦除旧的区域,请直接绘制区域",
|
});
|
},
|
// 点击选中变色 将当前页面所有路径重绘判断当前鼠标的坐标在哪个图形内 如果不传坐标参数就是回显的方法
|
clickSelect(e) {
|
this.ctx.clearRect(0, 0, this.c.width, this.c.height);
|
// console.log("当前数据:",this.canvasData)
|
this.ctx.lineWidth = "2";
|
let _this = this; // 集合中遍历需要将this转存一下使用
|
_this.canvasData.line.forEach(function (v, i) {
|
_this.ctx.strokeStyle = "yellow";
|
_this.ctx.beginPath();
|
_this.ctx.moveTo(v.location[0].x, v.location[0].y);
|
_this.ctx.lineTo(v.location[1].x, v.location[1].y);
|
_this.ctx.stroke();
|
_this.showRemarks(v.location[0].x, v.location[0].y, v.name, false);
|
_this.c.style.cursor = "pointer";
|
if (e && _this.minDistance(e.offsetX, e.offsetY, v.location, 10)) {
|
// 如果传入了事件坐标,就用isPointInStroke判断一下
|
// 如果当前环境覆盖了该坐标,就将图形的index放到数组里
|
// 当鼠标移入之后将当前的模式切换为选中模式
|
_this.type = "0";
|
_this.delCursor.type = "1";
|
_this.delCursor.index = i;
|
_this.delCursor.remarksName = v.name;
|
_this.delCursor.id = v.id;
|
// 将当前元素标红
|
_this.ctx.strokeStyle = "red";
|
_this.ctx.beginPath();
|
_this.ctx.moveTo(v.location[0].x, v.location[0].y);
|
_this.ctx.lineTo(v.location[1].x, v.location[1].y);
|
_this.ctx.stroke();
|
_this.showRemarks(v.location[0].x, v.location[0].y, v.name, true);
|
_this.c.style.cursor = "pointer";
|
}
|
});
|
_this.canvasData.rect.forEach(function (v, i) {
|
_this.ctx.strokeStyle = "yellow";
|
_this.ctx.beginPath();
|
_this.ctx.moveTo(v.location[0].x, v.location[0].y);
|
_this.ctx.lineTo(v.location[1].x, v.location[1].y);
|
_this.ctx.lineTo(v.location[2].x, v.location[2].y);
|
_this.ctx.lineTo(v.location[3].x, v.location[3].y);
|
_this.ctx.lineTo(v.location[0].x, v.location[0].y);
|
_this.ctx.stroke();
|
_this.showRemarks(v.location[0].x, v.location[0].y, v.name, false);
|
_this.c.style.cursor = "pointer";
|
if (e && _this.minDistance(e.offsetX, e.offsetY, v.location, 10)) {
|
// 如果传入了事件坐标,就用isPointInStroke判断一下
|
// 当鼠标移入之后将当前的模式切换为选中模式
|
_this.type = "0";
|
_this.delCursor.type = "2";
|
_this.delCursor.index = i;
|
_this.delCursor.remarksName = v.name;
|
_this.delCursor.id = v.id;
|
// console.log("当前选中元素:",_this.delCursor)
|
// 将当前元素标红
|
_this.ctx.strokeStyle = "red";
|
_this.ctx.beginPath();
|
_this.ctx.moveTo(v.location[0].x, v.location[0].y);
|
_this.ctx.lineTo(v.location[1].x, v.location[1].y);
|
_this.ctx.lineTo(v.location[2].x, v.location[2].y);
|
_this.ctx.lineTo(v.location[3].x, v.location[3].y);
|
_this.ctx.lineTo(v.location[0].x, v.location[0].y);
|
_this.ctx.stroke();
|
_this.showRemarks(v.location[0].x, v.location[0].y, v.name, true);
|
_this.selectedId = v.id;
|
_this.c.style.cursor = "pointer";
|
}
|
});
|
_this.canvasData.arrow.forEach(function (v, i) {
|
_this.ctx.strokeStyle = "yellow";
|
// _this.ctx.beginPath()
|
// _this.ctx.moveTo(v.location[0].x, v.location[0].y)
|
// _this.ctx.lineTo(v.location[1].x, v.location[1].y)
|
// _this.ctx.stroke()
|
_this.drawArrowUtil(
|
_this.ctx,
|
v.location[0].x,
|
v.location[0].y,
|
v.location[1].x,
|
v.location[1].y,
|
20,
|
_this.twoPointDistance(
|
{ x: v.location[0].x, y: v.location[0].y },
|
{ x: v.location[1].x, y: v.location[1].y }
|
) * 0.1,
|
2,
|
"yellow"
|
); // 绘制方法
|
_this.showRemarks(v.location[0].x, v.location[0].y, v.name, false);
|
//_this.c.style.cursor = "pointer";
|
if (e && _this.minDistance(e.offsetX, e.offsetY, v.location, 10)) {
|
// 如果传入了事件坐标,就用isPointInStroke判断一下 && _this.ctx.isPointInPath(e.offsetX, e.offsetY)
|
// 如果当前环境覆盖了该坐标,就将图形的index放到数组里
|
// 当鼠标移入之后将当前的模式切换为选中模式
|
_this.type = "0";
|
_this.delCursor.type = "4";
|
_this.delCursor.index = i;
|
_this.delCursor.remarksName = v.name;
|
_this.delCursor.id = v.id;
|
// 将当前元素标红
|
_this.ctx.strokeStyle = "red";
|
// _this.ctx.beginPath()
|
// _this.ctx.moveTo(v.location[0].x, v.location[0].y)
|
// _this.ctx.lineTo(v.location[1].x, v.location[1].y)
|
// _this.ctx.stroke()
|
_this.drawArrowUtil(
|
_this.ctx,
|
v.location[0].x,
|
v.location[0].y,
|
v.location[1].x,
|
v.location[1].y,
|
20,
|
_this.twoPointDistance(
|
{ x: v.location[0].x, y: v.location[0].y },
|
{ x: v.location[1].x, y: v.location[1].y }
|
) * 0.1,
|
2,
|
"red"
|
); // 绘制方法
|
_this.showRemarks(v.location[0].x, v.location[0].y, v.name, true);
|
_this.c.style.cursor = "pointer";
|
}
|
});
|
_this.canvasData.polygon.forEach(function (v, i) {
|
if (v.location.length !== 0) {
|
_this.ctx.strokeStyle = "yellow";
|
_this.ctx.beginPath();
|
_this.ctx.moveTo(v.location[0].x, v.location[0].y);
|
for (let i = 1; i < v.location.length; i++) {
|
_this.ctx.lineTo(v.location[i].x, v.location[i].y);
|
}
|
_this.ctx.closePath();
|
_this.ctx.stroke();
|
_this.showRemarks(v.location[0].x, v.location[0].y, v.name, false);
|
_this.c.style.cursor = "pointer";
|
if (e && _this.minDistance(e.offsetX, e.offsetY, v.location, 10)) {
|
// 如果传入了事件坐标,就用isPointInStroke判断一下
|
// 如果当前环境覆盖了该坐标,就将图形的index放到数组里
|
// 当鼠标移入之后将当前的模式切换为选中模式
|
_this.type = "0";
|
_this.delCursor.type = "5";
|
_this.delCursor.index = i;
|
_this.delCursor.remarksName = v.name;
|
_this.delCursor.id = v.id;
|
// 将当前元素标红
|
_this.ctx.strokeStyle = "red";
|
_this.ctx.beginPath();
|
_this.ctx.moveTo(v.location[0].x, v.location[0].y);
|
for (let i = 1; i < v.location.length; i++) {
|
_this.ctx.lineTo(v.location[i].x, v.location[i].y);
|
}
|
_this.ctx.closePath();
|
_this.ctx.stroke();
|
_this.showRemarks(v.location[0].x, v.location[0].y, v.name, true);
|
_this.c.style.cursor = "pointer";
|
}
|
}
|
});
|
// console.log("刚选中的图形:",_this.delCursor)
|
},
|
// 撤销
|
undo() {
|
if (this.type === "5" && this.flag) {
|
// 正在画多边形,暂存数组里有坐标数据,可一步一步撤销
|
if (this.points.length > 0) {
|
if (this.points.length === 1) {
|
this.type = "0"; // 不切换成‘0’把多边形撤销之后无法继续撤销其他图形
|
this.flag = false;
|
this.originX = null;
|
this.originY = null;
|
}
|
this.points.pop();
|
this.ctx.clearRect(0, 0, this.c.width, this.c.height);
|
this.loadImage();
|
this.pointsUndo.splice(this.pointsUndo.length - 2, 1);
|
this.drawPolygonUtil(this.pointsUndo);
|
}
|
} else {
|
// 多边形已经完成或者是在画别的图形
|
if (this.step >= 0) {
|
if (this.canvasHistory[this.step].data !== undefined) {
|
// 编辑删除步骤的撤销 之前删除的数据原路塞回去
|
switch (this.canvasHistory[this.step].type) {
|
case "1":
|
this.canvasData.line.splice(
|
this.canvasHistory[this.step].index,
|
this.canvasHistory[this.step].deleteLength,
|
this.canvasHistory[this.step].data
|
);
|
break;
|
case "2":
|
this.canvasData.rect.splice(
|
this.canvasHistory[this.step].index,
|
this.canvasHistory[this.step].deleteLength,
|
this.canvasHistory[this.step].data
|
);
|
break;
|
case "4":
|
this.canvasData.arrow.splice(
|
this.canvasHistory[this.step].index,
|
this.canvasHistory[this.step].deleteLength,
|
this.canvasHistory[this.step].data
|
);
|
break;
|
case "5":
|
this.canvasData.polygon.splice(
|
this.canvasHistory[this.step].index,
|
this.canvasHistory[this.step].deleteLength,
|
this.canvasHistory[this.step].data
|
);
|
break;
|
}
|
this.ctx.clearRect(0, 0, this.c.width, this.c.height);
|
let canvasPic = new Image();
|
canvasPic.src = this.canvasHistory[this.step - 1].src;
|
let ctx = this.ctx;
|
canvasPic.addEventListener("load", function () {
|
ctx.drawImage(canvasPic, 0, 0);
|
});
|
this.step--;
|
} else if (this.canvasHistory[this.step].name !== undefined) {
|
// 修改图形备注的撤销
|
switch (this.canvasHistory[this.step].type) {
|
case "1":
|
this.canvasData.line[this.canvasHistory[this.step].index].name =
|
this.canvasHistory[this.step].name;
|
break;
|
case "2":
|
this.canvasData.rect[this.canvasHistory[this.step].index].name =
|
this.canvasHistory[this.step].name;
|
break;
|
case "4":
|
this.canvasData.arrow[
|
this.canvasHistory[this.step].index
|
].name = this.canvasHistory[this.step].name;
|
break;
|
case "5":
|
this.canvasData.polygon[
|
this.canvasHistory[this.step].index
|
].name = this.canvasHistory[this.step].name;
|
break;
|
}
|
this.ctx.clearRect(0, 0, this.c.width, this.c.height);
|
let canvasPic = new Image();
|
canvasPic.src = this.canvasHistory[this.step - 1].src;
|
let ctx = this.ctx;
|
canvasPic.addEventListener("load", function () {
|
ctx.drawImage(canvasPic, 0, 0);
|
});
|
this.step--;
|
} else {
|
// 正常的撤销
|
this.ctx.clearRect(0, 0, this.c.width, this.c.height);
|
let canvasPic = new Image();
|
if (this.step > 0) {
|
canvasPic.src = this.canvasHistory[this.step - 1].src;
|
}
|
// 不知为何直接传进去this.ctx会是undefind,所以在外面转存一下
|
let ctx = this.ctx;
|
canvasPic.addEventListener("load", function () {
|
ctx.drawImage(canvasPic, 0, 0);
|
});
|
// 撤销最终数据
|
switch (this.canvasHistory[this.step].type) {
|
case "1":
|
this.canvasData.line.pop();
|
break;
|
case 2:
|
this.canvasData.rect.pop();
|
break;
|
case 4:
|
this.canvasData.arrow.pop();
|
break;
|
case 5:
|
this.canvasData.polygon.pop();
|
this.points.length = 0;
|
break;
|
case 0:
|
// 将回显数据清空,相当于清空页面
|
// console.log("撤销原始数据");
|
this.clear();
|
break;
|
}
|
this.step--;
|
}
|
} else {
|
this.$notify({
|
type: "warning",
|
message: "不能再继续撤销了",
|
});
|
}
|
}
|
// console.log("撤销!",this.canvasData);
|
},
|
disabledOthers(type) {
|
console.log("当前type:", type);
|
switch (type) {
|
case "1":
|
this.disableLine = false;
|
this.disableRect = true;
|
this.disableArrow = true;
|
this.disablePolygon = true;
|
this.disableSelect = true;
|
break;
|
case "2":
|
this.disableLine = true;
|
this.disableRect = false;
|
this.disableArrow = true;
|
this.disablePolygon = true;
|
this.disableSelect = true;
|
break;
|
case "4":
|
this.disableLine = true;
|
this.disableRect = true;
|
this.disableArrow = false;
|
this.disablePolygon = true;
|
this.disableSelect = true;
|
break;
|
case "5":
|
this.disableLine = true;
|
this.disableRect = true;
|
this.disableArrow = true;
|
this.disablePolygon = false;
|
this.disableSelect = true;
|
break;
|
}
|
console.log("禁用直线:", this.disableLine);
|
console.log("禁用矩形:", this.disableRect);
|
console.log("禁用箭头:", this.disableArrow);
|
console.log("禁用多边形:", this.disablePolygon);
|
},
|
undisabled() {
|
this.disableLine = false;
|
this.disableRect = false;
|
this.disableArrow = false;
|
this.disablePolygon = false;
|
this.disableSelect = false;
|
},
|
// 刷新底图
|
refresh() {
|
this.$emit("refresh");
|
// this.$notify({
|
// type: 'success',
|
// message: '底图已刷新'
|
// })
|
},
|
// 箭头绘制函数
|
drawArrowUtil(ctx, fromX, fromY, toX, toY, theta, headlen, width, color) {
|
// ctx:Canvas绘图环境
|
// fromX, fromY:起点坐标(也可以换成p1,只不过它是一个数组)
|
// toX, toY:终点坐标 (也可以换成p2,只不过它是一个数组)
|
// theta:三角斜边一直线夹角
|
// headlen:三角斜边长度
|
// width:箭头线宽度
|
// color:箭头颜色
|
|
theta = typeof theta !== "undefined" ? theta : 30;
|
headlen = typeof theta !== "undefined" ? headlen : 10;
|
width = typeof width !== "undefined" ? width : 1;
|
// color = typeof color !== 'undefined' ? color : 'yellow'
|
// 计算各角度和对应的P2,P3坐标
|
|
let angle = (Math.atan2(fromY - toY, fromX - toX) * 180) / Math.PI;
|
let angle1 = ((angle + theta) * Math.PI) / 180;
|
let angle2 = ((angle - theta) * Math.PI) / 180;
|
let topX = headlen * Math.cos(angle1);
|
let topY = headlen * Math.sin(angle1);
|
let botX = headlen * Math.cos(angle2);
|
let botY = headlen * Math.sin(angle2);
|
|
ctx.save();
|
ctx.beginPath();
|
let arrowX = fromX - topX;
|
let arrowY = fromY - topY;
|
ctx.moveTo(arrowX, arrowY);
|
ctx.moveTo(fromX, fromY);
|
ctx.lineTo(toX, toY);
|
arrowX = toX + topX;
|
arrowY = toY + topY;
|
ctx.moveTo(arrowX, arrowY);
|
ctx.lineTo(toX, toY);
|
arrowX = toX + botX;
|
arrowY = toY + botY;
|
ctx.lineTo(arrowX, arrowY);
|
ctx.strokeStyle = color;
|
ctx.lineWidth = width;
|
ctx.stroke();
|
ctx.restore();
|
},
|
// 获取相对坐标(暂不用)
|
getLocation(x, y, c) {
|
let bbox = c.getBoundingClientRect();
|
return {
|
x: (x - bbox.left) * (c.width / bbox.width),
|
y: (y - bbox.top) * (c.height / bbox.height),
|
/*
|
* 此处不用下面两行是为了防止使用CSS和JS改变了canvas的高宽之后是表面积拉大而实际
|
* 显示像素不变而造成的坐标获取不准的情况
|
x: (x - bbox.left),
|
y: (y - bbox.top)
|
*/
|
};
|
},
|
// 生成图形备注
|
remarks(x, y, type) {
|
this.ctx.moveTo(x, y - 20);
|
this.ctx.fillStyle = "green"; // 设置填充颜色为绿色
|
this.ctx.font = '20px "微软雅黑"'; // 设置字体
|
this.ctx.textBaseline = "bottom"; // 设置字体底线对齐绘制基线
|
this.ctx.textAlign = "left"; // 设置字体对齐的方式
|
let name = "";
|
switch (type) {
|
case "1":
|
this.lineIndex++;
|
name = "直线" + this.lineIndex;
|
break;
|
case "2":
|
this.rectIndex++;
|
name = "矩形" + this.rectIndex;
|
break;
|
case "4":
|
this.arrowIndex++;
|
name = "箭头" + this.arrowIndex;
|
break;
|
case "5":
|
this.polygonIndex++;
|
name = "多边形" + this.polygonIndex;
|
break;
|
}
|
this.ctx.fillText(name, x, y - 20); // 填充文字
|
return name;
|
},
|
// 回显图形备注
|
showRemarks(x, y, remarks, isHightlight) {
|
this.ctx.moveTo(x, y - 20);
|
if (isHightlight) {
|
this.ctx.fillStyle = "#8ae22e"; // 设置填充颜色为绿色
|
} else {
|
this.ctx.fillStyle = "green"; // 设置填充颜色为绿色
|
}
|
this.ctx.font = '20px "微软雅黑"'; // 设置字体
|
this.ctx.textBaseline = "bottom"; // 设置字体底线对齐绘制基线
|
this.ctx.textAlign = "left"; // 设置字体对齐的方式
|
this.ctx.fillText(remarks, x, y - 20); // 填充文字
|
},
|
// 重现保存画面
|
loadImage() {
|
if (this.step > -1) {
|
let img = new Image();
|
img.src = this.canvasHistory[this.step].src;
|
this.ctx.drawImage(img, 0, 0, this.c.width, this.c.height);
|
}
|
},
|
// 切换画线类型
|
changeType(num) {
|
if (num === "0") {
|
this.c.style.cursor = "pointer";
|
} else {
|
this.c.style.cursor = "crosshair";
|
}
|
this.type = num;
|
},
|
// 绘制多边形方法
|
drawPolygonUtil(points) {
|
this.ctx.strokeStyle = "yellow";
|
this.ctx.lineWidth = 2;
|
this.ctx.beginPath();
|
this.ctx.moveTo(points[0].x, points[0].y);
|
for (let i = 1; i < points.length; i++) {
|
this.ctx.lineTo(points[i].x, points[i].y);
|
}
|
this.ctx.closePath();
|
this.ctx.stroke();
|
},
|
// 画直线移动函数
|
drawLine(e) {
|
if (this.flag) {
|
this.ctx.clearRect(0, 0, this.c.width, this.c.height);
|
this.loadImage();
|
this.ctx.beginPath();
|
this.ctx.strokeStyle = "yellow";
|
this.c.style.cursor = "default";
|
this.ctx.lineWidth = 2;
|
this.ctx.moveTo(this.originX, this.originY);
|
this.ctx.lineTo(e.offsetX, e.offsetY);
|
this.ctx.stroke(); // 绘制
|
}
|
},
|
// 画矩形移动函数
|
drawRect(e) {
|
if (this.flag) {
|
this.ctx.clearRect(0, 0, this.c.width, this.c.height);
|
this.loadImage();
|
this.ctx.beginPath();
|
this.ctx.strokeStyle = "yellow";
|
this.ctx.lineWidth = 2;
|
this.ctx.strokeRect(
|
this.originX,
|
this.originY,
|
e.offsetX - this.originX,
|
e.offsetY - this.originY
|
); // 绘制方法
|
}
|
},
|
// 画箭头移动函数
|
drawArrow(e) {
|
if (this.flag) {
|
this.ctx.clearRect(0, 0, this.c.width, this.c.height);
|
this.loadImage();
|
this.ctx.lineWidth = 2;
|
this.drawArrowUtil(
|
this.ctx,
|
this.originX,
|
this.originY,
|
e.offsetX,
|
e.offsetY,
|
20,
|
this.twoPointDistance(
|
{ x: this.originX, y: this.originY },
|
{ x: e.offsetX, y: e.offsetY }
|
) * 0.1,
|
2,
|
"yellow"
|
); // 绘制方法
|
}
|
},
|
twoPointDistance(p1, p2) {
|
let dep = Math.sqrt(Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2));
|
return dep;
|
},
|
// 画多边形移动函数
|
drawPolygon(e) {
|
if (this.flag) {
|
this.ctx.clearRect(0, 0, this.c.width, this.c.height);
|
this.loadImage();
|
this.pointsUndo = this.points.concat({
|
x: e.offsetX,
|
y: e.offsetY,
|
});
|
this.drawPolygonUtil(
|
this.points.concat({
|
// concat返回连接后的数组,原数组不变,相当于把移动的坐标作为一个虚假的坐标追加在后面,绝!
|
x: e.offsetX,
|
y: e.offsetY,
|
})
|
);
|
}
|
},
|
// 画直线抬起
|
lineMouseUp(e) {
|
if (
|
Math.abs(this.originX - e.offsetX) < 5 &&
|
Math.abs(this.originY - e.offsetY) < 5
|
) {
|
this.flag = false;
|
return;
|
}
|
this.flag = false;
|
let Id;
|
let fileName;
|
let coordinate = [];
|
coordinate.push({
|
x: this.originX,
|
y: this.originY,
|
});
|
coordinate.push({
|
x: e.offsetX,
|
y: e.offsetY,
|
});
|
// console.log("line的editObj:",this.editObj)
|
if (this.editObj.id == undefined) {
|
Id = this.getUuid();
|
fileName = this.remarks(this.originX, this.originY, "1");
|
this.canvasData.line.push({
|
id: Id,
|
name: fileName,
|
location: coordinate,
|
});
|
} else {
|
Id = this.editObj.id;
|
fileName = this.editObj.remarksName;
|
this.canvasData.line.splice(this.delCursor.index, 1, {
|
id: Id,
|
name: fileName,
|
location: coordinate,
|
});
|
}
|
this.clickSelect();
|
this.undisabled();
|
this.c.style.cursor = "crosshair";
|
// 将当前刚画完的图形加入删改游标,可以将刚画完的图形名称显示在左上角
|
this.delCursor = {
|
id: this.getUuid(),
|
remarksName: fileName,
|
type: "1",
|
index: this.lineIndex - 1,
|
};
|
this.url = this.c.toDataURL(); // 每次 mouseup 都保存一次画布状态
|
this.step++;
|
this.canvasHistory.length = this.step; // 截断数组
|
this.canvasHistory.push({ type: 1, src: this.c.toDataURL("image/png") }); // 将快照保存到历史记录中以供撤销之用
|
// this.changeType('0')
|
this.freedEdit();
|
},
|
// 画矩形抬起
|
rectMouseUp(e) {
|
if (
|
Math.abs(this.originX - e.offsetX) < 5 &&
|
Math.abs(this.originY - e.offsetY) < 5
|
) {
|
this.flag = false;
|
return;
|
}
|
this.flag = false;
|
let coordinate = [];
|
// 逆时针算出矩形四角坐标
|
coordinate.push({
|
x: this.originX,
|
y: this.originY,
|
});
|
coordinate.push({
|
x: this.originX,
|
y: e.offsetY,
|
});
|
coordinate.push({
|
x: e.offsetX,
|
y: e.offsetY,
|
});
|
coordinate.push({
|
x: e.offsetX,
|
y: this.originY,
|
});
|
let Id;
|
let fileName;
|
// console.log("rect的editObj:",this.editObj)
|
if (this.editObj.id == undefined) {
|
Id = this.getUuid();
|
fileName = this.remarks(this.originX, this.originY, "2");
|
this.canvasData.rect.push({
|
id: Id,
|
name: fileName,
|
location: coordinate,
|
});
|
} else {
|
Id = this.editObj.id;
|
fileName = this.editObj.remarksName;
|
|
this.canvasData.rect.splice(this.delCursor.index, 1, {
|
id: Id,
|
name: fileName,
|
location: coordinate,
|
});
|
}
|
this.clickSelect();
|
this.undisabled();
|
this.c.style.cursor = "crosshair";
|
this.delCursor = {
|
id: Id,
|
remarksName: fileName,
|
type: "2",
|
index: this.rectIndex - 1,
|
};
|
// console.log("刚画完的矩形标志:",this.delCursor)
|
this.url = this.c.toDataURL(); // 每次 mouseup 都保存一次画布状态
|
this.step++;
|
this.canvasHistory.length = this.step; // 截断数组
|
this.canvasHistory.push({ type: 2, src: this.c.toDataURL("image/png") }); // 将快照保存到历史记录中以供撤销之用
|
|
this.freedEdit();
|
},
|
// 画箭头时抬起
|
arrowMouseUp(e) {
|
if (
|
Math.abs(this.originX - e.offsetX) < 5 &&
|
Math.abs(this.originY - e.offsetY) < 5
|
) {
|
this.flag = false;
|
return;
|
}
|
this.flag = false;
|
let Id;
|
let fileName;
|
let coordinate = [];
|
coordinate.push({
|
x: this.originX,
|
y: this.originY,
|
});
|
coordinate.push({
|
x: e.offsetX,
|
y: e.offsetY,
|
});
|
// console.log("arrow的editObj:",this.editObj)
|
if (this.editObj.id == undefined) {
|
Id = this.getUuid();
|
fileName = this.remarks(this.originX, this.originY, "4");
|
this.canvasData.arrow.push({
|
id: Id,
|
name: fileName,
|
location: coordinate,
|
});
|
} else {
|
Id = this.editObj.id;
|
fileName = this.editObj.remarksName;
|
this.canvasData.arrow.splice(this.delCursor.index, 1, {
|
id: Id,
|
name: fileName,
|
location: coordinate,
|
});
|
}
|
this.clickSelect();
|
this.undisabled();
|
this.c.style.cursor = "crosshair";
|
// 将当前刚画完的图形加入删改游标,可以将刚画完的图形名称显示在左上角
|
this.delCursor = {
|
id: this.getUuid(),
|
remarksName: fileName,
|
type: "4",
|
index: this.arrowIndex - 1,
|
};
|
this.url = this.c.toDataURL(); // 每次 mouseup 都保存一次画布状态
|
this.step++;
|
this.canvasHistory.length = this.step; // 截断数组
|
this.canvasHistory.push({ type: 4, src: this.c.toDataURL("image/png") }); // 将快照保存到历史记录中以供撤销之用
|
// this.changeType('0')
|
this.freedEdit();
|
},
|
// 画多边形结束时双击
|
polygonDblclick(e) {
|
this.flag = false;
|
this.points.pop(); // 双击之后多一个点的重复坐标,需要删除
|
let Id;
|
let fileName;
|
let coordinate = [];
|
this.points.map((item, index) => {
|
coordinate.push(item);
|
});
|
// console.log("polygon的editObj:",this.editObj)
|
if (this.editObj.id == undefined) {
|
Id = this.getUuid();
|
fileName = this.remarks(this.points[0].x, this.points[0].y, "5");
|
this.canvasData.polygon.push({
|
id: Id,
|
name: fileName,
|
location: coordinate,
|
});
|
} else {
|
Id = this.editObj.id;
|
fileName = this.editObj.remarksName;
|
this.canvasData.polygon.splice(this.delCursor.index, 1, {
|
id: Id,
|
name: fileName,
|
location: coordinate,
|
});
|
}
|
this.clickSelect();
|
this.undisabled();
|
this.c.style.cursor = "crosshair";
|
this.points.length = 0;
|
// 将当前刚画完的图形加入删改游标,可以将刚画完的图形名称显示在左上角
|
this.delCursor = {
|
id: this.getUuid(),
|
remarksName: fileName,
|
type: "5",
|
index: this.polygonIndex - 1,
|
};
|
this.url = this.c.toDataURL();
|
this.step++;
|
this.canvasHistory.length = this.step; // 截断数组
|
this.canvasHistory.push({ type: 5, src: this.c.toDataURL("image/png") }); // 将快照保存到历史记录中以供撤销之用
|
// this.changeType('0')
|
// console.log("总数据:",this.canvasData)
|
this.freedEdit();
|
},
|
// 释放编辑状态
|
freedEdit() {
|
this.editObj = {};
|
},
|
// 回显历史数据时计算一下回显的每种元素的数量以便生成图形注解时获得正确的开头
|
indexInit() {
|
this.lineIndex = this.canvasData.line.length;
|
this.rectIndex = this.canvasData.rect.length;
|
this.arrowIndex = this.canvasData.arrow.length;
|
//排除this.canvasData.polygon全部区域(全部区域的id就是摄像机的id)
|
|
let filterPolygonArr = this.canvasData.polygon.filter(
|
(item) => item.id != this.TreeDataPool.selectedNode.id
|
);
|
this.polygonIndex = filterPolygonArr.length;
|
},
|
// 生成uuid
|
getUuid() {
|
let originStr = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx";
|
let originChar = "0123456789abcdef";
|
let len = originChar.length;
|
return originStr.replace(/x/g, function (match) {
|
return originChar.charAt(Math.floor(Math.random() * len));
|
});
|
},
|
// 判断一个点是否离一个图形的最小距离为n像素以内
|
minDistance(x, y, locations, n) {
|
let flag = false;
|
for (let i = 0; i < locations.length; i++) {
|
if (i == locations.length - 1) {
|
if (
|
this.point2Line(
|
x,
|
y,
|
locations[i].x,
|
locations[i].y,
|
locations[0].x,
|
locations[0].y
|
) < n
|
) {
|
flag = true;
|
}
|
} else {
|
if (
|
this.point2Line(
|
x,
|
y,
|
locations[i].x,
|
locations[i].y,
|
locations[i + 1].x,
|
locations[i + 1].y
|
) < n
|
) {
|
flag = true;
|
}
|
}
|
}
|
return flag;
|
},
|
point2Line(x, y, x1, y1, x2, y2) {
|
let cross = (x2 - x1) * (x - x1) + (y2 - y1) * (y - y1); // |AB| * |AC|*cos(x)
|
if (cross <= 0)
|
return Math.sqrt((x - x1) * (x - x1) + (y - y1) * (y - y1) + 0.0);
|
let d2 = (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1); // |AB|
|
if (cross >= d2)
|
return Math.sqrt((x - x2) * (x - x2) + (y - y2) * (y - y2) + 0.0);
|
|
let r = cross / d2;
|
let px = x1 + (x2 - x1) * r; // C在 AB上的垂足点(px,py)
|
let py = y1 + (y2 - y1) * r;
|
return Math.sqrt((x - px) * (x - px) + (y - py) * (y - py) + 0.0); //两点间距离公式
|
},
|
},
|
props: {
|
isGB28181: {
|
default: false,
|
type: Boolean,
|
},
|
// isShowDrawArrow: {
|
// default: false,
|
// type: Boolean
|
// },
|
disabled: {
|
default: false,
|
type: Boolean,
|
},
|
canvasDataToChild: {
|
default: () => {
|
return {
|
line: [],
|
rect: [],
|
arrow: [],
|
polygon: [],
|
};
|
},
|
type: Object,
|
},
|
snapshot_url: {
|
type: String,
|
default: "",
|
},
|
},
|
};
|
</script>
|
<style lang="scss" scoped>
|
.header {
|
margin-top: 56px;
|
border-bottom: 1px solid #e9ebee;
|
}
|
|
.remark {
|
.el-input ::v-deep {
|
margin-left: 950px;
|
width: 160px;
|
height: 32px;
|
.el-input__inner {
|
padding: 0 10px;
|
color: #3d3d3d;
|
border-radius: 0;
|
border-color: #c0c5cc;
|
&::-webkit-input-placeholder {
|
color: #999999;
|
}
|
|
&:focus {
|
border-color: #0065ff;
|
}
|
}
|
}
|
}
|
|
.body {
|
display: flex;
|
justify-content: space-between;
|
margin-top: 20px;
|
border-bottom: 1px solid #e9ebee;
|
|
.btn {
|
text-align: center;
|
margin-left: 20px;
|
margin-bottom: 20px;
|
padding: 0;
|
width: 48px;
|
height: 48px;
|
border: 1px solid #c0c5cc;
|
border-radius: 0;
|
|
.iconfont {
|
font-size: 32px;
|
}
|
|
&:hover {
|
background: rgba($color: #0065ff, $alpha: 0.5);
|
|
.iconfont {
|
color: #0065ff;
|
}
|
}
|
|
&:focus,
|
&.selectedBtn {
|
background: rgba($color: #0065ff, $alpha: 0.5);
|
border: 2px solid #0065ff;
|
|
.iconfont {
|
color: #0065ff;
|
}
|
}
|
}
|
}
|
</style>
|