首页
归档
留言
友链
广告合作
壁纸
更多
美女主播
Search
1
博瑞GE车机升级/降级
5,610 阅读
2
Mac打印机设置黑白打印
4,952 阅读
3
修改elementUI中el-table树形结构图标
4,895 阅读
4
Mac客户端添加腾讯企业邮箱方法
4,674 阅读
5
intelliJ Idea 2022.2.X破解
4,357 阅读
后端开发
HarmonyOS Next
Web前端
微信开发
开发辅助
App开发
数据库
随笔日记
登录
/
注册
Search
标签搜索
Spring Boot
Java
Vue
Spring Cloud
Mac
MyBatis
WordPress
MacOS
asp.net
Element UI
Nacos
.Net
Spring Cloud Alibaba
MySQL
Mybatis-Plus
Typecho
jQuery
Java Script
IntelliJ IDEA
微信小程序
Laughing
累计撰写
627
篇文章
累计收到
1,421
条评论
首页
栏目
后端开发
HarmonyOS Next
Web前端
微信开发
开发辅助
App开发
数据库
随笔日记
页面
归档
留言
友链
广告合作
壁纸
美女主播
搜索到
29
篇与
的结果
2025-05-09
LogicFlow节点实现呼吸闪烁效果
LogicFlow是滴滴开源的流程图编辑框架,提供了一系列流程图编辑和交互功能。我们这里主要实现某个节点呼吸闪烁效果。如下图蓝色的节点,我们假设当前节点是活动节点。一、定义样式我们借助animation属性实现动画效果。.breathing-border { animation: breathing 2s infinite; } @keyframes breathing { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } }二、加载样式上面定义了样式之后,我们要做的就是将样式加载到我们的节点上。因为我这里使用的矩形节点,所以这里继承RectNode实现自己的规则。//自定义View class RectRadiusView extends RectNode { getShape() { // 获取XxxNodeModel中定义的形状属性 const { model } =this.props; const { x, y, width, height, radius, properties } =model; // 获取XxxNodeModel中定义的样式属性 const style=model.getNodeStyle(); const isActiveStep=properties.isActiveStep; return h('g', {} , [ h('rect', { ...style, x: x - width / 2, y: y - height / 2, width, height, rx: radius, ry: radius, className: isActiveStep ? 'breathing-border' : '', }), ]); } } // 自定义model class RectRadiusModel extends RectNodeModel { // 样式属性 getNodeStyle() { const style=super.getNodeStyle() const { fill } =this.properties style.fill=fill style.strokeWidth=0 return style } // 形状属性 initNodeData(data) { super.initNodeData(data) this.width=150 this.height=50 this.radius=25 } }扩展完自己的节点后,注册上lf.register({ type: "rectRadiusNode", view: RectRadiusView, model: RectRadiusModel })至此,就实现了我们节点的一个呼吸闪烁的效果。
2025年05月09日
10 阅读
0 评论
0 点赞
2025-03-27
RuoYi AI:一个全栈式 AI 开发平台
壹、简介RuoYi-AI是基于经典开源项目RuoYi深度扩展的AI开发平台,它不仅继承了RuoYi家族的高效开发特性,还支持对接OpenAI、C还GLM、讯飞星火等几十种大语言模型,实现了聊天对话、图像生成、语音克隆等前沿功能,成为开发者构建智能应用的“一站式”解决方案。贰、特色功能全套开源系统:提供完整的前端应用、后台管理以及小程序应用,全部开箱即用。基于MIT开源协议,自由度高,可灵活修改和分发代码。本地RAG方案:集成Milvus/Weaviate向量库、本地向量化模型、Ollama调用本地LLM,实现完全本地化RAG的高效检索与生成,保障数据隐私与性能。丰富插件功能:支持联网、SQL查询插件及Text2API插件,扩展系统能力与应用场景。内置SSE、websocket等网络协议,支持对接多种大语言模型,同时还集成了MidJourney和DALLE AI绘画功能强大的多媒体功能:支持AI翻译、PPT制作、语音克隆和翻唱等扩展功能:支持将大模型接入个人或企业微信支付功能:支持易支付、微信支付等多种支付方式三、开发部署3.1、环境要求JDK17MySQL 5.7或者 MySql 8.0Redis 5.X+Maven 3.8+NodeJS+(含pnpnm)3.2、后端安装3.2.1、Clone代码GiHub地址:https://github.com/ageerle/ruoyi-aiGitee地址:https://gitee.com/ageerle/ruoyi-ai3.2.2、Idea导入项目使用Idea导入项目并正确配置Maven,在application.yaml文件中修改数据库及Redis链接信息3.2.3、初始化数据库数据库初始化脚本位于script/sql/ruoyi-ai.sql。3.2.3、运行项目以上配置完成后,直接运行项目即可。3.3、安装管理端&客户端3.3.1、Clone代码3.3.1.1、管理端代码GiHub地址:https://github.com/ageerle/ruoyi-adminGitee地址:https://gitee.com/ageerle/ruoyi-admin3.3.1.1、客户端代码GiHub地址:https://github.com/ageerle/ruoyi-webGitee地址:https://gitee.com/ageerle/ruoyi-web3.3.2、安装依赖进入ruoyi-admin或者ruoyi-web,打开终端,执行pnpm install3.3.3、运行或打包项目运行项目:pnpm dev打包项目:pnpm build3.4、修改配置3.4.1、申请API KEYhttps://api.pandarobot.chat3.4.2、注册API KEY成功注册账号后点击添加令牌,参数可以全部默认,然后点击复制按钮可以获取API KEY3.4.3、进入后台管理配置默认账号为admin,默认密码为admin123进入运营管理-系统模型-新增模型,在请求密钥处填写上一步申请到的key信息
2025年03月27日
67 阅读
0 评论
0 点赞
2024-07-17
Vue使用mavon-editor插件实现Markdown文件编辑及预览
因为开发在线知识库系统,因为主要面向研发人员,所以在文档编辑器上,更加倾向于使用markdown,基于vue的markdown编辑器有很多,在经过实际体验后,从易用性、美观性等综合考量,最重选择了mavon-editor。MavonEditor简介MavonEditor是一款基于Vue的Markdown编辑器,它结合了Markdown语法和Typora的实时预览功能,提供了一个所见即所得(WYSIWYG)的编辑体验。MavonEditor不仅支持基本的Markdown语法,还支持LaTeX公式、代码高亮、目录生成、自动目录锚点链接等功能,使其成为撰写技术文档、博客文章等的理想工具。但是MavonEditor也有一个缺点,就是它依赖的highlightjs、katex插件,默认是基于CDN的,当然我们可以改成基于本地的,这个后面再详细介绍。安装插件npm install mavon-editor --save引入因为我涉及在后端编辑,前端预览,所以直接选择了全局引入,编辑main.js// mavonEditor全局注册 import mavonEditor from 'mavon-editor' import 'mavon-editor/dist/css/index.css' Vue.use(mavonEditor)后端编辑页面<mavon-editor ref="md" v-model="form.content" @imgAdd="markdownImageAdd" :toolbars="toolbars" @imgDel="markdownImageDelete" class="content-show" codeStyle="docco" :ishljs="true" :externalLink="externalLink"/>v-model是绑定的内容imgAdd是图片上传后的回调,mavon-editor上传图片后,会转成base64,在回调方法内,我们可以拿到base64编码的图片信息,上传到服务器或OSS后进行存储。imgDel是删除图片后的回调,这里需要注意一点,如果我们是将图片上传到自己服务器,需要有个记录表记录一下,因为在后期编辑文档的时候,工具栏是没有删除图片按钮的,如果我们需要删除图片的话,就需要一个专门维护的地方。toolbars是配置工具栏按钮codeStyle配置代码高亮样式。可选的配色方案可以参考https://hinesboy.github.io/mavonEditor/src/lib/core/hljs/lang.hljs.css.jsishljs是否开启代码高亮。externalLink配置外链应用,如果我们不使用CDN时,需要配置这个data() { return { toolbars: { bold: true, // 粗体 italic: true, // 斜体 header: true, // 标题 underline: true, // 下划线 strikethrough: true, // 中划线 mark: true, // 标记 superscript: true, // 上角标 subscript: true, // 下角标 quote: true, // 引用 ol: true, // 有序列表 ul: true, // 无序列表 link: true, // 链接 imagelink: true, // 图片链接 code: true, // code table: true, // 表格 fullscreen: true, // 全屏编辑 readmodel: false, // 沉浸式阅读 htmlcode: true, // 展示html源码 help: false, // 帮助 /* 1.3.5 */ undo: true, // 上一步 redo: true, // 下一步 trash: true, // 清空 save: false, // 保存(触发events中的save事件) /* 1.4.2 */ navigation: false, // 导航目录 /* 2.1.8 */ alignleft: true, // 左对齐 aligncenter: true, // 居中 alignright: true, // 右对齐 /* 2.2.1 */ subfield: true, // 单双栏模式 preview: true, // 预览 boxShadow: false }, //加载本地资源 externalLink: { markdown_css: function() { // 这是你的markdown css文件路径 return '/markdown/github-markdown.min.css' }, hljs_js: function() { // 这是你的hljs文件路径 return '/highlightjs/highlight.min.js' }, hljs_css: function(css) { // 这是你的代码高亮配色文件路径 return '/highlightjs/styles/' + css + '.min.css' }, hljs_lang: function(lang) { // 这是你的代码高亮语言解析路径 return '/highlightjs/languages/' + lang + '.min.js' }, katex_css: function() { // 这是你的katex配色方案路径路径 return '/katex/katex.min.css' }, katex_js: function() { // 这是你的katex.js路径 return '/katex/katex.min.js' } }, } },methods: { //编辑器图片删除 markdownImageDelete(file) { let filePath = file[0] if (filePath.indexOf(this.baseApi) === 0) { filePath = filePath.replace(this.baseApi, '') } delImage(filePath).then(response => { if (response.code === '200') { this.$modal.msgSuccess('图片删除成功') } }) }, //编辑器插入图片 markdownImageAdd(fileIndex, file) { if (!file.articleId) { file.articleId = this.form.articleId } uploadImage(JSON.stringify(file)).then((res) => { if (res.code === 200) { this.$refs.md.$img2Url(fileIndex, this.baseApi + res.data) this.$modal.msgSuccess('图片上传成功') } else { this.$refs.md.$img2Url(fileIndex, '') } }).catch(() => { this.$refs.md.$img2Url(fileIndex, '') }) } }至此,我们后端编辑功能基本完成了前端预览界面<mavon-editor ref="mavonEditor" :editable="false" v-model="article.content" :defaultOpen="'preview'" :subfield="false" :toolbarsFlag="false" :navigation="true" codeStyle="docco" :ishljs="true" :scroll-style="true" :box-shadow="false" preview-background="#ffffff" :externalLink="externalLink"/>编辑页面相同的属性就不介绍了,介绍一下其他的。editable是否可编辑,因为我们是预览界面,所以设置成不可编辑defaultOpen在单栏(subfield=false)时默认展示区域, edit: 默认展示编辑区域,preview: 默认展示预览区域。subfield单栏还是双栏暂时,true: 双栏(编辑预览同屏), false: 单栏(编辑预览分屏)toolbarsFlag是否显示工具栏navigation是否展示目录scroll-style开启滚动条样式box-shadow开启边框阴影preview-background预览框背景颜色配置本地外链加载如果你想自己引入而不希望mavon-editor加载的话,可以将externalLink设置为false.如果想本地按需加载,你需要安装copy-webpack-plugin插件(npm install copy-webpack-plugin -D) 配置webpack如下所示: (假定webpack配置文件位于项目的/webpack/webpack.js, 而你希望将hljs以及markdown相关文件导出位于项目的/dist/highlightjs以及/dist/markdown目录之下, katex和上面一样)var CopyWebpackPlugin = require('copy-webpack-plugin'); module.exports = { // ... plugins: [ // ... new CopyWebpackPlugin([{ from: 'node_modules/mavon-editor/dist/highlightjs', to: path.resolve(__dirname, '../dist/highlightjs'), // 插件将会把文件导出于/dist/highlightjs之下 }, { from: 'node_modules/mavon-editor/dist/markdown', to: path.resolve(__dirname, '../dist/markdown'), // 插件将会把文件导出于/dist/markdown之下 }, { from: 'node_modules/mavon-editor/dist/katex', // 插件将会把文件导出 to: path.resolve(__dirname, '../dist/katex') }]), // ... ], // ... }然后你需要给mavon-editor设置externalLink 相关代码如下所示: (假定你的web根目录位于项目的/dist/, 你的网站是www.site.com, 那么 markdown, hljs_js, hljs_css, hljs_lang, katex_css, katex_js返回的是你的网站对应文件位置, 比如www.site.com/markdown/github-markdown.min.css 对应的文件应该位于项目的/dist/markdown/github-markdown.min.css)<template> <div id="app"> <mavon-editor :subfield = "subfield" :code_style="code_style" :ishljs="true" :externalLink="externalLink" ></mavon-editor> </div> </template> <script> export default { data () { return { subfield: true, code_style: 'solarized-dark', externalLink: { markdown_css: function() { // 这是你的markdown css文件路径 return '/markdown/github-markdown.min.css'; }, hljs_js: function() { // 这是你的hljs文件路径 return '/highlightjs/highlight.min.js'; }, hljs_css: function(css) { // 这是你的代码高亮配色文件路径 return '/highlightjs/styles/' + css + '.min.css'; }, hljs_lang: function(lang) { // 这是你的代码高亮语言解析路径 return '/highlightjs/languages/' + lang + '.min.js'; }, katex_css: function() { // 这是你的katex配色方案路径路径 return '/katex/katex.min.css'; }, katex_js: function() { // 这是你的katex.js路径 return '/katex/katex.min.js'; }, } } }, } </script>Notice: 如果你想禁用mavon-editor的自动加载, 可以将externalLink设置为false或externalLink中的某函数值设置为false 如:export default { // ... data() { return { externalLink: false, // 这里只能为`true`/`false`和一个`Object`, 如果为`true`代表全使用外链且自动加载, 如果为`false`则禁用,如果为`Object`则如上所示 } } // ... }或者:export default { // ... data() { return { externalLink: { hljs_css: function(css) { // 这是你的代码高亮配色文件路径 return '/highlightjs/styles/' + css + '.min.css'; }, katex_css: false, // `false`表示禁用自动加载,它也可以是个函数,如果它是个函数,那么这个函数应该返回一个可访问的`katex`的css路径字符串 // 我们没有设置`katex_js`, `hljs_js`, `hljs_lang`, `markdown_css`, `mavon-editor`会认为它的值为`true`,它会默认使用`cdnjs`相关外链加载 }, } } // ... }
2024年07月17日
1,008 阅读
0 评论
0 点赞
2024-05-30
ElementUI中一个页面多个el-descriptions列不对齐问题处理
使用ElementUI框架时,如果我们希望列表形式展示多个字段。可能经常使用Descriptions描述列表,如果一个页面只有一个Descriptions时,列宽显示是没有问题的,但是如果有多个Descriptions,你会发现,各个Descriptions之间的列宽,可能显示的不一致。为了保持各列宽度一致,我们可以设置contentStyle属性。<el-descriptions border :label-style="{ width: '110px' }" :contentStyle="content_style"> </el-descriptions> 然后在data里面,设置具体的content_stylecontent_style: { // 居左 'text-align': 'left', // 设置长度 width: '400px', // 排列第二行 'word-break': 'break-all' }
2024年05月30日
1,046 阅读
0 评论
1 点赞
2024-03-22
若依系统上传图片压缩
虽然标题里面有若依,实际上所有的Vue项目都能够适用的。最近基于若依系统做点东西,若依系统本身封装了一个图片上传组件ImageUpload,但是这个组件对我来说不太适用,最主要的问题就是这个组件是自动上传的,这样就会导致我们业务数据跟附件无法做到同步,比如我们新增一个单据,点了上传图片,此时图片已经上传到服务器,但是我并没有保存单据,这样就造成了部分垃圾数据。当然不是说不能处理,只是来说更加麻烦。先来说下我们目前做的功能需求吧:基于ElementUI的el-upload上传图片为了减少图片占用的空间(目前基于阿里云OSS,这个后期介绍),我们在前端上传图片之前在不影响图片展示质量的前提下,对图片进行压缩表单数据与图片文件同步上传可以单张图片上传也可以同时上传多张图片(以下我们以多张图片上传来说明)壹、提取图片压缩公共方法在ruoyi.js公共方法中,封装图片压缩方法/** 图片压缩,默认同比例压缩 * @param {Object} fileObj * 图片对象 * 回调函数有一个参数,base64的字符串数据 */ export function compress(fileObj, callback) { // console.log('压缩前文件大小', fileObj.size) try { const image = new Image() image.src = URL.createObjectURL(fileObj) image.onload = function () { const that = this // 默认按比例压缩 let w = that.width let h = that.height const scale = w / h w = fileObj.width || w h = fileObj.height || (w / scale) let quality = 0.5 // 默认图片质量为0.7 // 生成canvas const canvas = document.createElement('canvas') const ctx = canvas.getContext('2d') // 创建属性节点 const anw = document.createAttribute('width') anw.nodeValue = w const anh = document.createAttribute('height') anh.nodeValue = h canvas.setAttributeNode(anw) canvas.setAttributeNode(anh) ctx.drawImage(that, 0, 0, w, h) // 图像质量 if (fileObj.quality && fileObj.quality <= 1 && fileObj.quality > 0) { quality = fileObj.quality } // quality值越小,所绘制出的图像越模糊 const data = canvas.toDataURL('image/jpeg', quality) // 压缩完成执行回调 const newFile = convertBase64UrlToBlob(data) callback(newFile) // console.log('压缩后文件信息', newFile) } } catch (e) { console.log('压缩失败!') } } // Base64 => 二进制(Blob) function convertBase64UrlToBlob(urlData) { // 去掉url的头,并转换为byte const bytes = window.atob(urlData.split(',')[1]) // 处理异常,将ascii码小于0的转换为大于0 const ab = new ArrayBuffer(bytes.length) const ia = new Uint8Array(ab) for (let i = 0; i < bytes.length; i++) { ia[i] = bytes.charCodeAt(i) } return new Blob([ab], {type: 'image/png'}) } 在main.js中挂载公共方法,方便在后面使用Vue.prototype.compress = compress贰、页面增加上传图片功能在表单中,增加上传图片功能<el-upload action="#" list-type="picture-card" :auto-upload="false" multiple :headers="headers" ref="carPhotoListRef" :on-preview="handlePictureCardPreview" :on-remove="handleRemove" :on-change="handleChange" :file-list="imageFileList" :disabled="form.inquiryStatus !== 'inquirying'" :class="{ hide: form.inquiryStatus !== 'inquirying' }" accept="image/bmp,image/jpg,image/png,image/svg,image/psd,image/webp,image/jpeg"> <i class="el-icon-plus"></i> </el-upload> <el-dialog :visible.sync="dialogImageVisible" :append-to-body="true"> <img width="100%" :src="dialogImageUrl" alt=""/> </el-dialog>因为我们要手工上传图片,所以需要对el-upload的属性进行配置action因为我们要收工上传,所以action需要设置成#auto-upload设置成false禁止自动上传headers是基于若依的认证,传递头部信息的on-preview预览图片on-remove删除图片同步处理我们后端绑定数据on-change图片改变的钩子,因为我们要手工上传图片,因此通过这个钩子完成图片的压缩其他的一些属性基本设置图片预览,限制上传图片类型的,按需设置即可。data里面主要设置图片预览、绑定图片列表等属性data() { return { headers: { Authorization: "Bearer " + getToken() }, //预览图片窗口是否可见 dialogImageVisible: false, //预览图片Url dialogImageUrl: null, imageFileList: [], } }method方法如下//删除图片 handleRemove(file, fileList) { this.imageFileList = fileList if (this.form.carPhotoList) { let index = -1 for (let i = 0; i < this.form.carPhotoList.length; i++) { if (this.form.carPhotoList[i].photoId === file.photoId) { index = i } } if (index !== -1) { this.$delete(this.form.carPhotoList, index) } } }, //图片改变 handleChange(file, fileList) { let that = this // 调用自定义的压缩方法 compress(file.raw, function (val) { // 图片格式: blob => file let newFile = new window.File([val], file.name, {type: file.raw.type}); // 新增属性(file)并赋值 let fileObj = {} fileObj.raw = newFile fileObj.name = file.name fileObj.uid = file.uid; fileObj.url = URL.createObjectURL(file.raw); that.imageFileList.push(fileObj) }) // this.imageFileList = fileList }, //预览图片 handlePictureCardPreview(file) { this.dialogImageUrl = file.url this.dialogImageVisible = true },实现图片压缩主要是通过on-change钩子,选择图片后,通过on-change钩子调用handleChange方法,我们拿到图片文件后,调用compress方法,通过回调函数,将压缩后的图片绑定到imageFileList表单保存/** 提交按钮 */ submitForm() { if (this.imageFileList.length <= 0) { this.msgInfo('请上传图片') return } const loading = this.$loading({ lock: true,//lock的修改符--默认是false text: "保存中",//显示在加载图标下方的加载文案 spinner: "el-icon-loading",//自定义加载图标类名 background: "rgba(0, 0, 0, 0.7)",//遮罩层颜色 target: document.querySelector("#table")//loadin覆盖的dom元素节点 }) this.$refs["form"].validate(valid => { if (valid) { let formData = new FormData() this.imageFileList.forEach(file => { formData.append("imageFileList", file.raw)// 图片列表 }) formData.append("form", JSON.stringify(this.form))//表单 if (this.form.inquiryId != null) { updateInquiry(formData).then(response => { loading.close() this.msgSuccess("修改成功") this.open = false this.getList() }).catch(error => { loading.close() }) } else { addInquiry(formData).then(response => { loading.close() this.msgSuccess("新增成功") this.open = false this.getList() }).catch(error => { loading.close() }) } } else { loading.close() } }) },在表单保存方法中,我们通过FormData将图片文件与表单数据,一起保存。
2024年03月22日
610 阅读
0 评论
0 点赞
2022-05-08
Vue 2.X过渡系统
Vue2.X过渡系统过渡系统是Vue.js为DOM动画效果提供的一个特性,它能在元素从DOM中插入或移除时触发你的CSS过渡(transition)和动画(animation),也就是说在DOM元素发生变化时为其添加特尔顶的class类名,从而产生过渡效果。一、Vue2.X定义过渡动画的方法不同于Vue1.X,Vue2.x过渡系统取消了v-transion指令,新增了<transition/>内置标签,用法如下<transition name="hello"> <h1 v-if="show">你好</h1> </transition>transition标签为一个抽象标签,并不会额外渲染一个DOM元素,仅仅时用于包裹过渡元素及触发过渡行为。v-if、v-show等指令仍旧标记在内容元素上。二、transition参数2.1、nameVue根据name自动生成对应的对应的类名。name-enter、name-enter-active、name-enter-to、name-leave、name-leave-active、name-leave-to六种状态类名说明name-enter:定义进入过渡的开始状态。在元素被插入之前生效,在元素被插入之后的下一帧移除。name-enter-active:定义进入过渡生效时的状态。在整个进入过渡的阶段中应用,在元素被插入之前生效,在过渡/动画完成之后移除。这个类可以被用来定义进入过渡的过程时间,延迟和曲线函数。name-enter-to: 2.1.8版及以上 定义进入过渡的结束状态。在元素被插入之后下一帧生效 (与此同时 name-enter 被移除),在过渡/动画完成之后移除。name-leave: 定义离开过渡的开始状态。在离开过渡被触发时立刻生效,下一帧被移除。name-leave-active:定义离开过渡生效时的状态。在整个离开过渡的阶段中应用,在离开过渡被触发时立刻生效,在过渡/动画完成之后移除。这个类可以被用来定义离开过渡的过程时间,延迟和曲线函数。name-leave-to: 2.1.8版及以上 定义离开过渡的结束状态。在离开过渡被触发之后下一帧生效 (与此同时 name-leave 被删除),在过渡/动画完成之后移除。2.2、appear元素首次渲染的时候是否启用transition,默认为false。即v-if绑定值初始为true时,是否渲染时调用transition效果。2.3、css如果设置为true,则只监听钩子函数的调用。2.4、type设置监听CSS动画结束事件的类型。2.5、mode控制过渡插入/移除的先后顺序,主要用于元素切换时。可选择的值有out-in、in-out,如果不设置则同时调用。三、钩子函数与类名类似,也可以使用对应的钩子函数,包括@before-enter、@enter、@after-enter、@before-leave、@leave、@after-leave、四、transition-grouptransition-group主要用于将过渡动画应用到多个DOM元素上。五、Demo<style rel="stylesheet" type="text/css"> /* 动画退出类名 */ .hello-leave-active { animation: amt1 0.5s linear reverse; } /* 定义动画 */ @keyframes amt1 { from { transform: translateX(-100%); } to { transform: translateX(0px); } } /* 进入的起点、离开的终点 */ .hello-enter, .hello-leave-to { transform: translateX(-100%); } /* 进入的过程 */ .hello-enter-active, .hello-leave-active { transition: 0.5s linear; } /* 进入的终点、离开的起点 */ .hello-enter-to, .hello-leave { transform: translateX(0); } </style> <!-- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css" /> --> </head> <script src="https://cdn.staticfile.org/vue/2.2.2/vue.min.js" type="text/javascript"></script> <body> <div id='app'> <button v-on:click="show = !show"> Toggle </button> <transition name="hello" @before-enter="beforeEnter"> <h1 v-if="show">你好</h1> </transition> <!-- <transition-group appear name="animate__animated animate__bounce" enter-active-class="animate__swing" leave-active-class="animate__backOutUp"> <h1 v-show="!show" key="1">你好啊!</h1> <h1 v-show="show" key="2">迪丽热巴!</h1> </transition-group> --> </div> <script> let vm = new Vue({ el: '#app', data: { show: false }, methods: { beforeEnter: function () { console.log('beforeEnter'); } } }) </script>
2022年05月08日
1,247 阅读
0 评论
0 点赞
2022-05-04
Vue自定义指令
Vue除了内置的v-bind、v-model、v-once、v-pre等内置指令外,我们还可以注册自定义指令,以便封装对DOM元素的重复处理行为,提高代码的复用率。指令的注册自定义指令分为两种,全局自定义指令及组件(局部)自定义指令。全局自定义指令在所有组件中都可以使用,组件(局部)自定义指令顾名思义,只能在当前组件中使用。全局自定义指令全局自定义指令通过Vue.directive(id,definition)进行注册。如下,我们定义一个自动获取焦点的自定义组件。实现功能,当页面打开时,让<input>控件自动获取焦点。<html> <head> <title></title> </head> <script src="https://cdn.staticfile.org/vue/2.2.2/vue.min.js" type="text/javascript"></script> <body> <div id='app'> <input type="text" v-g-focus="'1111'"/> </div> <script> Vue.directive('gFocus', { inserted: function (el,binding) { el.focus(); } }) let vm = new Vue({ el: '#app' }) </script> </body> </html> 组件(局部)自定义指令组件(局部)自定义指令通过组件的directives进行注册,如下let comp = Vue.extend({ directives: { 'localDirective': {} } })该指令只能在当前组件内调用,其他组件无法使用。指令的定义对象我们在注册指令的同时,可以传入definition定义对象,对指令赋予一些特殊的功能。这些对象主要包括五个钩子函数。bind:只被调用一次,在指令第一次绑定到元素上时调用。inserted:指令所绑定的元素插入到DOM中时调用update:指令在绑定之后以初始值作为参数进行第一次调用,之后每次当绑定值发生变化时调用。描述的是组件更新前的状态。componentUpdated:被绑定元素所在模板完成一次更新周期时调用。描述的是组件更新后的状态unbind:只被调用一次,当指令与元素解绑时调用。我们可以通过以下代码简单看一下使用方法<div id='app'> <input type="text" v-g-focus="param" v-if="exist" /> </div> <script> Vue.directive('gFocus', { bind:function(el, binding){ console.log('bind:' + binding.value) }, inserted: function (el, binding) { el.focus(); console.log('inserted:' + binding.value) }, update: function (el, binding) { console.log('update:' + binding.value) }, unbind:function(el, binding){ console.log('unbind:' + binding.value) }, componentUpdated:function(el, binding){ console.log('componentUpdated:' + binding.value) }, }) let vm = new Vue({ el: '#app', data: { param: 'first', exist: true } }) </script>当我们第一次打开页面时,查看控制台,会看到如下输出我们在控制台输入vm.param='second',可以看到如下输出我们在控制台输入vm.exist=false,可以看到如下输出钩子函数参数钩子函数参数 (即 el、binding、vnode 和 oldVnode)指令钩子函数会被传入以下参数:el:指令所绑定的元素,可以用来直接操作DOM。binding:一个对象,包含以下属性:name:指令名,不包括v-前缀。value:指令的绑定值,例如:我们上面代码传递的param。oldValue:指令绑定的前一个值,仅在update和componentUpdated钩子中可用。无论值是否改变都可用。expression:字符串形式的指令表达式。例如v-my-directive="1 + 1"中,表达式为1 + 1。arg:传给指令的参数,可选。例如v-my-directive:foo中,参数为foo。modifiers:一个包含修饰符的对象。例如:v-my-directive.foo.bar 中,修饰符对象为 { foo: true, bar: true }。vnode:Vue 编译生成的虚拟节点。oldVnode:上一个虚拟节点,仅在update和componentUpdated钩子中可用。
2022年05月04日
945 阅读
0 评论
0 点赞
2021-06-30
el-table隐藏顶部复选框
有时候我们使用el-table的选择框时,如果涉及修改、删除时,可能一次只允许用户选择一条,这样的话,如果使用顶部的全选复选框就不合适了。我们可以提供两种方式,一种是隐藏顶部的复选框,另一种是将顶部复选框改成文字,禁止点击。多选框那一列加label-class-name <el-table v-loading="loading" :data="assetClass.assetClassPropertyList" row-key="propertyId" :row-class-name="rowClassName" @selection-change="handleSelectionChange" ref="table"> <el-table-column type="index" width="50" align="center" label="序号"/> <el-table-column type="selection" width="50" align="center" label-class-name="DisabledSelection"/> <el-table-column prop="propertyName" label="属性名称" show-overflow-tooltip align="center"> <template slot-scope="scope"> <el-input v-model="scope.row.propertyName" size="mini" maxlength="64" :show-word-limit="true"></el-input> </template> </el-table-column> <el-table-column prop="isRequired" label="是否必填" :formatter="isRequiredFormatter" width="100"> <template slot-scope="scope"> <el-switch v-model="scope.row.isRequired" active-value="Y" inactive-value="N" ></el-switch> </template> </el-table-column> </el-table>style加样式隐藏顶部复选框<style scoped> /*表格全选框去除空框*/ .el-table >>> .DisabledSelection .cell .el-checkbox__inner { display: none; position: relative; } </style>将顶部复选框改成汉字<style scoped> /*表格全选框去除空框*/ .el-table >>> .DisabledSelection .cell .el-checkbox__inner { display: none; position: relative; } /*表格全选框改为:选择*/ .el-table >>> .DisabledSelection .cell:before { content: "选择"; position: absolute; left: 7px; } </style>
2021年06月30日
1,542 阅读
0 评论
0 点赞
2021-06-26
vue使用xlsx修改样式导出excel
安装依赖npm install --save xlsx npm install --save xlsx-style //如果需要修改样式,则需要安装解决引入xlsx-style报错问题找到:node_modules\xlsx-style\dist\xlsx.full.min.js 并把这个js文件拷贝到static文件夹下再index.html中引入 新建downExcel.js文件import XLSX from "xlsx" // 配置 const wopts = { bookType: "xlsx", bookSST: true, type: "binary", cellStyles: true }; function downloadExl(json, config, type) { var tmpdata = json[0]; json.unshift({}); var keyMap = []; //获取keys for (var k in tmpdata) { keyMap.push(k); json[0][k] = k; } var tmpdata = []; //用来保存转换好的json let border = { bottom: { style: "thin", color: { rgb: "000000" } }, top: { style: "thin", color: { rgb: "000000" } }, left: { style: "thin", color: { rgb: "000000" } }, right: { style: "thin", color: { rgb: "000000" } } }; let style0 = { border: border, alignment: { horizontal: "center", wrapText: true, vertical: "center" }, font: { sz: 18, bold: true, color: { rgb: "000000" }, outline: true }, fill: { bgColor: { indexed: 64 } } }; let style1 = { border: border, alignment: { horizontal: "center", wrapText: true, vertical: "center" }, font: { sz: 12, bold: true, color: { rgb: "000000" }, outline: true }, fill: { bgColor: { indexed: 64 } } }; json .map((v, i) => keyMap.map((k, j) => Object.assign({}, { v: v[k], position: (j > 25 ? getCharCol(j) : String.fromCharCode(65 + j)) + (i + 1) }) ) ) .reduce((prev, next) => prev.concat(next)) .forEach((v, i) => { // 在遍历时修改样式 let s = { font: { sz: 10, name: "微软雅黑" }, alignment: { horizontal: "center", wrapText: true, vertical: "center" } }; // 判断是否是标题 if (config._this.title.includes(v.v) && v.position.length == 2 && v.position[1] == "1") { s.fill = { fgColor: { rgb: "008000" } }; s.border = { right: { style: "thin", color: { rgb: "000000" } } }; // 判断是否是需要红色字体的标题字段 if (config._this.redTitle.includes(v.v)) { s.font.color = { rgb: "ff0000" }; } } tmpdata[v.position] = { v: v.v || "", s }; }); var outputPos = Object.keys(tmpdata); //设置区域,比如表格从A1到D10 //设置每列对应的宽度 tmpdata["!margins"] = [{ //工作表单元格合并配置项 可选 s: { //s start 开始 c: 0, //cols 开始列 r: 0 //rows 开始行 }, //开始 A1 e: { //e end 结束 c: 4, //cols 结束列 r: 0 //rows 结束行 } }, { s: { c: 0, r: 1 }, //开始 A2 e: { c: 1, r: 1 } //结束B2 }, { s: { c: 2, r: 1 }, //开始 C2 e: { c: 3, r: 1 } //结束D2 }, ] // !cols工作表列宽配置项 可选 tmpdata["!cols"] = config.colwidth; // 第一二行样式 tmpdata["A1"].s = style0; tmpdata["A2"].s = style1; tmpdata["C2"].s = style0; var tmpWB = { SheetNames: ["mySheet"], //工作表名数组 Sheets: { mySheet: Object.assign({}, tmpdata, //!ref设置工作表的范围 如 必须 否则不显示 { "!ref": outputPos[0] + ":" + outputPos[outputPos.length - 1] //设置填充区域 } ) } }; const tmpDown = new Blob( [ s2ab( XLSX.write( tmpWB, { bookType: type == undefined ? "xlsx" : type, bookSST: false, type: "binary" } //这里的数据是用来定义导出的格式类型 ) ) ], { type: "" } ); // 数据处理完后传入下载 saveAs( tmpDown, config._this.fileName + "." + (wopts.bookType == "biff2" ? "xls" : wopts.bookType) ); } // 获取26个英文字母用来表示excel的列 function getCharCol(n) { let temCol = "", s = "", m = 0; while (n > 0) { m = (n % 26) + 1; s = String.fromCharCode(m + 64) + s; n = (n - m) / 26; } return s; } function s2ab(s) { if (typeof ArrayBuffer !== "undefined") { var buf = new ArrayBuffer(s.length); var view = new Uint8Array(buf); for (var i = 0; i != s.length; ++i) view[i] = s.charCodeAt(i) & 0xff; return buf; } else { var buf = new Array(s.length); for (var i = 0; i != s.length; ++i) buf[i] = s.charCodeAt(i) & 0xff; return buf; } } // 下载功能 function saveAs(obj, fileName) { var tmpa = document.createElement("a"); tmpa.download = fileName || "未命名"; // 兼容ie 火狐 下载文件 if ("msSaveOrOpenBlob" in navigator) { window.navigator.msSaveOrOpenBlob(obj, fileName); } else if (window.navigator.userAgent.includes("Firefox")) { var a = document.createElement("a"); a.href = URL.createObjectURL(obj); a.download = fileName; document.body.appendChild(a); a.click(); } else { tmpa.href = URL.createObjectURL(obj); } tmpa.click(); setTimeout(function () { URL.revokeObjectURL(obj); }, 100); } export default downloadExl;使用import downloadfn from "@/config/downExcel data(){ return{ fileName:"testExcel", xmindJSON: [ { Code: 1, Name: "里斯", GBPerfix: "ABC-F", stop: "测试" }, { Code: 2, Name: "里斯1", GBPerfix: "ABC-B", stop: "测试1" }, { Code: 3, Name: "里斯2", GBPerfix: "ABC-D", stop: "测试1" } ], config: { colwidth: [ { wpx: 169 }, { wpx: 63 }, { wpx: 203 }, { wpx: 128 }, ], _this: this }, title: ["功能模块与路径", "用例编号", "前置条件"] } }methods:{ export() { downloadfn(this.xmindJSON, this.config); } }
2021年06月26日
1,880 阅读
0 评论
0 点赞
2021-06-16
请输入一个有效的值,最接近的值为24.01和25.01
在用到表单里,发现把鼠标移到type="number"的input框上时有时候会显示“请输入有效值,两个最接近的有效值为24.01,25.01”的提示,24.01和25.01跟你输入的整数值有关。代码里面并没有这个提示,发现只有当输入小数时会出现提示,原来是浏览器默认输入整数,如果要输入小数需要修改步长,比如将step设为0.01。 <el-input @input="((val)=>{editInput(val,'beforeWeight')})" type="number" @blur="onBlurBeforeWeight" v-model="form.beforeWeight" :min="0.01" :max="9999999999.99" :step="0.01" placeholder="请输入去种仁前重量"> <template slot="append">公斤</template> </el-input>
2021年06月16日
1,571 阅读
0 评论
0 点赞
2021-06-15
elementui textarea与input字体不一致问题解决
今天使用elementui中input及textarea时,发现两种控件默认显示的字体是不一致的,input的比较扁,而textarea显示的比较瘦高。可以看下下面这张图,比较明显。原因通过F12,我们可以看到textarea的字体为monospaceinput的字体为Arial解决在全局css文件加入以下代码即可, 因为我们项目使用的是ruoyi的系统,所以在sec/assets/styles/element-ui.scss文件中加入以下代码即可textarea { //font-family: Arial, Helvetica, sans-serif; //解决TextArea与input不一致的问题 font-family: "Helvetica Neue",Helvetica,"PingFang SC","Hiragino Sans GB","Microsoft YaHei","微软雅黑",Arial,sans-serif; }
2021年06月15日
1,712 阅读
0 评论
0 点赞
2021-06-08
vue-baidu-map通过路书实现轨迹回放
网上实现轨迹回放的代码很多,但是很少有vue实现的。本文通过vue-baidu-map对vue轨迹回放功能进行说明。安装依赖npm install vue-baidu-map --save修改main.jsmain.js增加以下内容,注意ak需要替换成自己的。import BaiduMap from 'vue-baidu-map' Vue.use(BaiduMap, { ak: '替换成自己的ak' })前端页面<template> <div class="app-container"> <el-form :model="queryParams" ref="queryForm" :inline="true"> <el-row> <el-col :span="6"> <el-form-item label="车辆" prop="vehicleLicence"> <el-select filterable v-model="queryParams.vehicleLicence" placeholder="请选择车辆" clearable size="small" style="width: 100%" > <el-option v-for="vehicle in vehicleOptions" :key="vehicle.licence" :label="vehicle.licence" :value="vehicle.licence" /> </el-select> </el-form-item> </el-col> <el-col :span="8"> <el-form-item label="查询时间" prop="dateRange"> <el-date-picker v-model="dateRange" size="small" style="width: 100%" value-format="yyyy-MM-dd HH:mm:ss" type="datetimerange" range-separator="-" start-placeholder="请选择开始时间" end-placeholder="请选择结束时间" :picker-options="pickerOptions" ></el-date-picker> </el-form-item> </el-col> <el-col :span="5"> <el-form-item label="轨迹速度"> <el-slider v-model="speed" style="width: 200px" :step="1"></el-slider> </el-form-item> </el-col> <el-col :span="5"> <el-form-item style="float: right"> <el-button type="cyan" icon="el-icon-search" size="mini" @click="handleQuery">查询</el-button> <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button> </el-form-item> </el-col> </el-row> </el-form> <baidu-map style="height:47.1rem;width:100%" :center="center" :zoom="15" :scroll-wheel-zoom="true"> <bm-polyline :path="path" stroke-color="blue" :stroke-opacity="0.5" :stroke-weight="3" :editing="false" ></bm-polyline> <bm-marker :position="{lng: startMark.lng, lat: startMark.lat}"></bm-marker> <bm-marker :position="{lng: endMark.lng, lat: endMark.lat}"></bm-marker> <bml-lushu @stop="reset" :path="path" :icon="icon" :play="play" :rotation="true" :speed="speed * 10" :infoWindow="true" :content="content" > </bml-lushu> </baidu-map> </div> </template> <script> //百度地图 import BaiduMap from 'vue-baidu-map/components/map/Map.vue' import BmScale from 'vue-baidu-map/components/controls/Scale' import BmNavigation from 'vue-baidu-map/components/controls/Navigation' import BmMarkerClusterer from 'vue-baidu-map/components/extra/MarkerClusterer' import BmMarker from 'vue-baidu-map/components/overlays/Marker' import BmInfoWindow from 'vue-baidu-map/components/overlays/InfoWindow' import { BmlLushu } from 'vue-baidu-map' import drugMarkerIcon from '@/assets/icons/map_marker_check.png' import { selectGpsOrbitList } from '@/api/dmp/industry/gps/orbit' import { listAllVehicle } from '@/api/basic/vehicle' export default { components: { BaiduMap, BmScale, BmNavigation, BmMarkerClusterer, BmMarker, BmInfoWindow, BmlLushu }, props: {}, data() { return { // 查询参数 queryParams: { vehicleLicence: undefined }, // 日期范围 dateRange: [new Date().getFullYear() + '-' + (new Date().getMonth() + 1) + '-' + new Date().getDate() + ' 00:00:00', new Date().getFullYear() + '-' + (new Date().getMonth() + 1) + '-' + new Date().getDate() + ' 23:59:59'], play: false, path: [], center: { lng: 116.984646, lat: 36.659686 }, startMark: {}, endMark: {}, icon: { url: drugMarkerIcon, size: { width: 32, height: 32 }, opts: { anchor: { width: 27, height: 13 } } }, content: undefined, speed: 20, pickerOptions: { disabledDate(time) { return time.getTime() > Date.now() - 8.64e6 } }, vehicleOptions: [] } }, methods: { reset() { this.play = false }, handleSearchComplete(res) { this.path = res.getPlan(0).getRoute(0).getPath() }, handleQuery() { const search = this.addDateRange(this.queryParams, this.dateRange) if (this.queryParams.vehicleLicence === undefined) { this.msgError('请选择车辆') return } if (search.beginTime === undefined || search.beginTime === '' || search.endTime === undefined || search.endTime === '') { this.msgError('请选择查询时间') return } selectGpsOrbitList(this.addDateRange(this.queryParams, this.dateRange)).then(response => { if (response.data.length <= 0) { this.msgError('未查询到该车辆的运行数据') return } let length = response.data.length let middle = -1 if (length % 2 === 0) { middle = length / 2 + 1 } else { middle = (length + 1) / 2 } response.data.forEach(item => { let obj = { lng: item.longitude, lat: item.latitude } this.path.push(obj) }) this.center = this.path[middle] this.startMark = this.path[0] this.endMark = this.path[this.path.length - 1] this.content = this.queryParams.vehicleLicence this.play = true }).catch(error => { }) }, /** 重置按钮操作 */ resetQuery() { this.resetForm('queryForm') this.dateRange = [new Date().getFullYear() + '-' + (new Date().getMonth() + 1) + '-' + new Date().getDate() + ' 00:00:00', new Date().getFullYear() + '-' + (new Date().getMonth() + 1) + '-' + new Date().getDate() + ' 23:59:59'] } }, mounted() { listAllVehicle().then(response => { this.vehicleOptions = response.data }) } } </script> <style> </style> data关键参数说明:play:true或false,为true时,开始播放轨迹。path:数组,格式为[{lng: 116.984646,lat: 36.659686}]center:地图中心点,格式{lng: 116.984646,lat: 36.659686}startMark:轨迹开始经纬度,格式{lng: 116.984646,lat: 36.659686}endMark:轨迹终点经纬度,格式{lng: 116.984646,lat: 36.659686}icon:轨迹图标,如下演示的小车图标content:轨迹显示内容,如下按时的车牌speed:轨迹绘制的速度,这里通过el-slider实现滑动调整速度bm-polyline组件属性说明path:绘制曲线的经纬度数组bml-lushuplay:true或false,为true时,开始播放轨迹。infoWindow:true或false,为true时,显示窗口,及content内容。
2021年06月08日
3,090 阅读
4 评论
2 点赞
1
2
3