最近接了一个需求,要求:由于页面太多,实施自己在图片上进行画框然后实现操作引导.这个需求分为两步:
1.用户需要在页面上先上传指定大小图片
2.用户在图片内绘制区域,并输入操作引导的引导语
3.绘制好后保存到数据库中
4.在项目中按需进行查询指定的引导即可

1.intor.js
大家可以看我之前的文章是如何下载依赖引入的传送门→前端使用intro.js实现页面操作引导
话不多说开始操作↓
这里我是用html文件写的,大家换成vue直接按需粘过去就行
需要更改的地方:
1.上传图片的地址连接,换成自己公司的地址
2.样式可以自行更改~
详细步骤备注我就写在代码注释里面啦
- html>
- <html lang="zh_CN">
- <head th:include="include :: header">
- <title>引导设置title>
-
- <link rel="stylesheet" th:href="@{/css/introjs.css}">
- head>
- <style>
- #big_Img {
- position: relative;
- /*使大图随着小图移动的定位*/
- z-index: 2;
- }
-
- #big_Img .requ {
- border: 2px rgba(255, 27, 27, 0.76) dashed;
- position: absolute;
- cursor: move;
- resize: both;
- /*使绘制的div可以拖动改变大小的样式 */
- overflow: auto;
- z-index: 999;
- }
-
- .drag {
- border: 1px #5da8ff solid;
- width: 50px;
- height: 24px;
- background: #fff;
- cursor: pointer;
- margin: auto;
- margin-top: 8px;
- }
-
- .drag:active {
- box-shadow: 3px 3px 10px 0 #111111;
- width: 51px;
- height: 25px;
- }
-
- #big_Img {
- border: 1px solid #ccc;
- width: 900px;
- height: 450px;
- }
-
- #img {
- pointer-events: none;
- user-select: none;
- -webkit-user-drag: none;
- width: 900px;
- height: 450px;
- margin: auto;
- position: relative;
- }
-
- .guanbi {
- position: absolute;
- left: 0px;
- bottom: 0px;
- height: 21px;
- width: 20px;
- border-radius: 50%;
- background-color: #0f0f0f;
- color: #fff;
- text-align: center;
- line-height: 20px;
- cursor: pointer;
- }
- .el-form-item{
- width: 380px !important;
- }
- .el-form-item__content {
- width: 200px !important;
- }
- .el-form-item__label{
- width: 140px !important;
- }
- style>
- <body>
- <div id="main" style="display: flex;padding: 20px;" v-cloak>
- <div style="width: 70%;">
- <el-form label-width="160px" inline>
- <el-form-item label="选择菜单">
- <el-select v-model="menu" clearable placeholder="请选择菜单">
- <el-option v-for="item in pageData" :key="item.value" :label="item.label" :value="item.value">
- el-option>
- el-select>
- el-form-item>
- <el-form-item label="选择对应功能">
- <el-select v-model="Feature" clearable placeholder="请选择对应功能">
- <el-option v-for="item in featureData" :key="item.value" :label="item.label"
- :value="item.value">
- el-option>
- el-select>
- el-form-item>
- <el-button type="primary" @click="dialogVisible = true">添加功能el-button>
- <el-button type="primary" @click="removeMenu">删除功能el-button>
- <br>
- <el-form-item label="引导语">
- <el-input v-model="yindaoyu" style="width: 200px;" clearable>el-input>
- el-form-item>
- <el-form-item label="请拖拽右侧至图片区域">
- <div draggable="true" id="source" class="drag">div>
- el-form-item>
- <el-button @click="caozuo()">预览效果el-button>
- <el-button type="primary" @click="save">保存el-button>
- el-form>
- <h3 style="color: #000;font-size: 20px">图片区域h3>
- <div id="big_Img" class="v2" draggable="true">
- <div id="img">div>
- div>
- div>
- <div style="height: 148px">
- <el-upload
- v-model="dialogImageUrl"
- action="/wyservice/workquery/uploadImg"
- list-type="picture-card"
- :on-preview="handlePictureCardPreview"
- :limit="1"
- :on-exceed="handleExceed"
- :on-success="((response, file, fileList)=>handleSuccess(response, file, fileList,'imgs'))"
- :on-remove="handleRemove">
- <div style="text-align: center"><img style="margin-bottom: 20px" src="/img/lushang/upload_pictures.png"/>
- <p style="margin-top: -110px;">点击上传图片p>div>
- el-upload>
- <el-dialog :visible.sync="dialogVisible1">
- <img width="100%" :src="dialogImageUrl" alt="">
- el-dialog>
- div>
-
-
- <el-dialog title="添加功能按钮" :visible.sync="dialogVisible" width="30%">
- <el-form :model="formInline" class="demo-form-inline" label-width="160px">
- <el-form-item label="选择菜单">
- <el-select v-model="formInline.formPage" clearable placeholder="请选择菜单">
- <el-option v-for="item in pageData" :key="item.value" :label="item.label" :value="item.value">
- el-option>
- el-select>
- el-form-item>
- <el-form-item label="输入功能标题">
- <el-input v-model="formInline.featureTitle" clearable>el-input>
- el-form-item>
- <el-form-item label="输入功能描述">
- <el-input v-model="formInline.featureContent" clearable>el-input>
- el-form-item>
- <el-form-item>
- <el-button type="primary" @click="btnAdd">确定el-button>
- el-form-item>
- el-form>
- el-dialog>
- div>
-
- <div th:include="include :: footer">div>
- <div th:include="include::load">div>
- <script th:src="@{/js/intro/intro.js}">script>
- <script th:src="@{/js/intro/introComponent.js}">script>
- <script th:src="@{/js/appjs/operationGuidance/operationGuidance.js}">script>
- body>
- html>
- let vm = new Vue({
- el: '#main',
- data: {
- dialogImageUrl: 'https://t7.baidu.com/it/u=4198287529,2774471735&fm=193&f=GIF',
- dialogVisible1: false,
- dialogVisible: false,
- yindaoyu: '',
- formInline: {
- formPage: '',
- featureTitle: '',
- featureContent: '',
- },
- menu: '',
- Feature: '',
- pageData: [],
- featureData: [],
-
-
- tag_number: 0,
- mobileDiv: false,
- move_x: '',
- move_y: '',
- lt: '',
- ls: '',
- },
- mounted() {
- //拖拽元素绘制div
- source.ondragend = ev => {
- if (this.yindaoyu) {
- this.draw(ev)
- } else {
- layer.msg("请先输入引导语")
- }
- }
- },
- methods: {
- //确定添加
- btnAdd() {
- this.dialogVisible = false
- },
- //删除功能
- removeMenu(){
-
- },
- //点击保存
- save() {
- let htmlCode = document.getElementById('big_Img').outerHTML;
- console.log(htmlCode);
- },
- draw(ev) {
- var oBox = document.getElementById("big_Img");
- ev = window.event || ev;
- var move_x1, move_y1, move_x2, move_y2, move_x3, move_y3, move_x4, move_y4;
- var isMove = false;
- var angle_x;
- var x1 = ev.clientX - 130 - oBox.offsetLeft; //起始坐标,130是左边导航的宽度
- var y1 = ev.clientY - 30 - oBox.offsetTop;
- var x2 = x1 + 100;
- var y2 = y1;
- var x4 = x1;
- var y4 = y1 + 100;
- var x3 = ev.clientX - 80 - oBox.offsetLeft + 50; //终点坐标
- var y3 = ev.clientY + 20 - oBox.offsetTop + 50;
- var degree = 0;
- var oDiv = document.createElement("div");
- // var otext = document.createElement("div1");
- // oBox.appendChild(otext);
- oBox.appendChild(oDiv);
- // otext.style.left = x1 - 15 + "px"; //在绘制的大div左上角的位置
- // otext.style.top = y1 - 15 + "px";
- //15是要减去小div的大小,这样位置才会在div的外面
- this.tag_number++;
- // otext.id = this.tag_number;
- // otext.innerText = this.tag_number;
- //在绘制的div1中加上文本,这里是加上左上角的编号
- oDiv.style.left = (x3 > x1 ? x1 : x3) + "px"; //绘制oDiv的样式
- oDiv.style.top = (y3 > y1 ? y1 : y3) + "px";
- oDiv.style.width = Math.abs(x3 - x1) + "px";
- oDiv.style.height = Math.abs(y3 - y1) + "px";
- oDiv.id = this.tag_number
- oDiv.setAttribute("class", "requ");
- oDiv.setAttribute("data-step", this.tag_number); //添加指引步骤从0开始,如果用户删除中间的,也不会影响
- oDiv.setAttribute("data-intro", this.yindaoyu); //这里是用户输入的引导语
- oDiv.innerHTML = ""guanbi" + ">x" //关闭按钮
-
- let guan = oDiv.children[0]
- guan.onclick = ev => { //鼠标点击
- oDiv.remove()
- }
-
- oDiv.onmousedown = ev => {
- var down_x1 = ev.clientX; //获取鼠标按下的坐标
- var down_y1 = ev.clientY;
- //获取元素的left,top值
- var l = oDiv.offsetLeft;
- var t = oDiv.offsetTop;
- if (ev.offsetX < oDiv.clientWidth - 10 && ev.offsetY < oDiv.clientHeight - 10) { //判断是点击的右下角改变大小还是移动div
- this.mobileDiv = true //移动div
- } else {
- this.mobileDiv = false //改变大小
- }
- document.onmousemove = ev => { //鼠标移动事件
- var ev = ev || event;
- //获取鼠标移动时的坐标
- var down_x2 = ev.clientX;
- var down_y2 = ev.clientY;
- //计算出鼠标的移动距离
- this.move_x = down_x2 - down_x1;
- this.move_y = down_y2 - down_y1;
- //移动的数值与元素的left,top相加,得出元素的移动的距离
- this.lt = this.move_y + t;
- this.ls = this.move_x + l;
- if (this.mobileDiv == true) { //移动div
- //更改元素的left,top值
- oDiv.style.top = this.lt + 'px';
- oDiv.style.left = this.ls + 'px';
- } else {
- //改变div大小开始
- //获取鼠标移动时的坐标
- var down_x2 = ev.clientX;
- var down_y2 = ev.clientY;
- }
- }
- document.onmouseup = ev => { //鼠标抬起
- document.onmousemove = null;
- if (this.mobileDiv == true) {
- // otext.style.top = this.lt - 15 + 'px'; //随着大div的移动而移动
- // otext.style.left = this.ls - 15 + 'px';
- } else {
- //改变大小的鼠标抬起
- }
- }
- }
- },
- caozuo() {
- introJs('.v2').setOptions({
- nextLabel: "下一步", // 下一个的按钮文字
- prevLabel: "上一步", // 上一个按钮文字
- // skipLabel: "跳过", // 跳过指引的按钮文字
- doneLabel: "完成", // 完成按钮的文字
- hidePrev: false, // 是否在第一步中隐藏“上一步”按钮;不隐藏,将呈现为一个禁用的按钮
- hideNext: false, // 是否在最后一步中隐藏“下一步”按钮(同时会隐藏完成按钮);不隐藏,将呈现为一个禁用的按钮
- exitOnEsc: true, // 点击键盘的ESC按钮是否退出指引
- exitOnOverlayClick: false, // 点击遮罩层时是否退出介绍
- showStepNumbers: false, // 是否显示步骤编号
- disableInteraction: true, // 是否禁用高亮显示框内元素的交互
- showBullets: true, // 是否显示面板的指示点
- overlayOpacity: 0.7, // 遮罩层的透明度 0-1之间
- helperElementPadding: 10, // 选中的指引元素周围的填充距离
- showProgress: true, //进度条
- }).start();
- },
-
- handleRemove(file, fileList) {
- let url = this.dialogImageUrl.split('http://geecity-image.oss-cn-qingdao.aliyuncs.com/')
- $.ajax({
- type: "POST",
- url: "/workquery/gdlist/delimg",
- data: {filename: url[1]},
- error: function (request) {
- parent.layer.alert("Connection error");
- },
- success: (data) => {
- this.dialogImageUrl = ''
- $("#img").css({
- "background": "url("+this.dialogImageUrl+")" + ""+ "no-repeat",
- });
- layer.msg("删除成功")
- }
- })
- },
- handlePictureCardPreview(file) {
- this.dialogImageUrl = file.url;
- this.dialogVisible1 = true;
- },
- handleSuccess(response, file, fileList, type) {
- this.dialogImageUrl = response.data.src
- $("#img").css({
- "background": "url("+this.dialogImageUrl+")" + ""+ "no-repeat",
- "background-size": "950px"
- });
- },
- handleExceed() {
- return layer.msg("最多上传1张图片")
- }
- },
- })
结束
这里面代码都很详细啦,大家看一遍就懂了,如果不懂可以评论哦,回复超快
-
相关阅读:
Golang 结构化日志包 log/slog 详解(六):slog.Record
OpenTiny Vue 3.11.0 发布:增加富文本、ColorPicker等4个新组件,迎来了贡献者大爆发!
【附代码案例】深入理解 PyTorch 张量:叶子张量与非叶子张量
SpringBoot整合web放回json数据的方法
基于注意力机制的LSTM液体管道非稳态工况检测
【云原生 | 从零开始学Kubernetes】十、k8sPod节点亲和性和反亲和性
第一章、基于Ruoyi-Vue开发脚手架之对象存储实现
异步FIFO设计
使用VBS编写xshell/SecureCRT自动化脚本
vue3的ref,reactive的使用和原理解析
-
原文地址:https://blog.csdn.net/m0_75116475/article/details/132619646