• [技术干货] Vant Uploader 上传图片功能-转载
     van-uploader参考文档 限制上传数量 通过 max-count 属性可以限制上传文件的数量,上传数量达到限制后,会自动隐藏上传区域。 限制上传大小 通过 max-size 属性可以限制上传文件的大小,超过大小的文件会被自动过滤,文件信息通过 oversize 事件获取。 文件上传前进行校验和处理 通过before-read 传入函数,可以在上传前进行校验和处理,支持返回 Promise 对 file 对象进行自定义处理。例如压缩图片:使用compressorjs压缩图片,优化功能,压缩所有格式的图片。 文件上传完毕后获取数据 文件上传后会触发 after-read 回调函数,获取到对应的 file 对象。 更多细节请参考vant官方文档 <template>   <div>     <van-uploader       v-model="fileList"       :max-count="1"       :max-size="2048 * 1024"       :after-read="OnAfterRead"       :before-read="onBeforeRead"       @oversize="onOversize"       @delete="onDelete"     />   </div> </template>  <script> import Compressor from "compressorjs"; export default {   data() {     return {       fileList: [],       IMG_LIST: "", //图片路径            };   },   methods: {     OnAfterRead(result) {       let newImage = new Image();       // 这里将src传入需要获取信息的图片地址或base64       newImage.src = result.content;        newImage.onload = () => {         // 输出图片信息 比如可以获取图片宽高         console.log("图片宽", newImage.width);         console.log("图片高", newImage.height);         this.fileList[0].url = result.content;         // 点击上传图片的结果(base64的图片字符串)IMG_LIST         this.IMG_LIST = this.fileList[0].url;         // 控制台打印         console.log(" this.IMG_LIST ", this.IMG_LIST);       };     },     //上传图片压缩,需要安装依赖: npm i compressorjs,并引入(import) 图片0.6倍压缩     onBeforeRead(file) {       return new Promise((resolve) => {         new Compressor(file, {           quality: 0.6,           success: resolve,           error(err) {             console.log('图片压缩失败---->>>>>',err.message);           },         });       });     },     //图片大小超过2M提示     onOversize(file) {       this.$toast("图片大小不能超过 2M");     },     //点击移除图片     onDelete() {       this.fileList = [];     },   }, }; </script>  <style> </style> ———————————————— 版权声明:本文为CSDN博主「前端小程」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/CSSAJBQ_/article/details/128012890 
  • [技术干货] v-viewer插件使用
    Vue.js 是一个流行的 JavaScript 框架,可以用于构建交互式的 Web 应用程序。要在 Vue.js 中使用 v-viewer 插件,您可以按照以下步骤进行操作:安装 v-viewer:您可以使用 npm 或 yarn 安装 v-viewer 插件。使用 npm 进行安装:npm install v-viewer --save或使用 yarn 进行安装:yarn add v-viewer导入和使用 v-viewer:在您的 Vue 组件中,导入 v-viewer 并使用它。// 在您的组件文件中 import Viewer from 'v-viewer'; import 'viewerjs/dist/viewer.css'; export default { // 组件的其他选项 // 在组件中注册 v-viewer mounted() { this.$nextTick(() => { this.initViewer(); }); }, methods: { initViewer() { this.$viewer = new Viewer(this.$refs.viewerWrapper); }, }, };您需要引入 viewer.css 样式文件,确保 v-viewer 的样式正确加载。在模板中使用 v-viewer:在需要展示图片的模板中,使用 v-for 循环来渲染图片,并使用 ref 添加引用以便在组件中使用。<template> <div> <div ref="viewerWrapper"> <img v-for="(image, index) in images" :src="image" :key="index" @click="$viewer.show()"> </div> </div> </template> <script> export default { // 组件的其他选项 data() { return { images: ['image1.jpg', 'image2.jpg', 'image3.jpg'], }; }, }; </script>在上述示例中,images 数组包含了要展示的图片地址。当点击图片时,使用 $viewer.show() 方法来显示图片。以上是在 Vue.js 中使用 v-viewer 的简单示例。您可以根据自己的需求进行进一步的配置和定制。请确保按照 v-viewer 的文档了解更多可用的选项。
  • [技术干货] 京东微前端和阿里微前端对比
    京东微前端和阿里微前端都是当前比较流行的微前端框架,它们有各自的优点和特点。下面是对两者进行简要对比的一些关键点:总体架构:京东微前端:京东微前端采用了基于 Web Components 的技术栈,使用自研的微前端框架来进行组织和管理微前端应用。阿里微前端:阿里微前端采用了基于微前端架构飞冰(Ice)的技术栈,使用自研的插件化中台框架来实现微前端的组织和运行。技术栈和生态系统:京东微前端:京东微前端使用了 Web Components 技术,不依赖特定的前端框架,可以与任意前端技术栈集成。它提供了一套完整的解决方案,包括构建、打包、路由、通信等。阿里微前端:阿里微前端基于飞冰(Ice)框架,使用 React 和 Vue 作为主要的前端技术栈。它也提供了一套完整的解决方案,包括构建、打包、路由、通信等,并且有丰富的生态系统和插件可用。生态和社区支持:京东微前端:虽然京东微前端是京东自研的框架,但它在社区中的影响力逐渐增强,受到一些开发者的关注和支持。但它的生态系统相对较小。阿里微前端:阿里微前端基于飞冰(Ice)框架,得到了阿里巴巴集团内众多项目的使用和支持,具有较大的社区和生态系统,有更多的插件和工具可供选择。适用场景和定位:京东微前端:京东微前端定位于构建复杂应用系统的微前端架构,适用于需要将多个独立的前端应用集成在一起的场景。阿里微前端:阿里微前端定位于构建企业级中后台应用的微前端架构,适用于大型系统和组织内部的微服务架构。需要注意的是,以上对比仅是针对一些关键点进行的简要对比,具体选型还需要根据实际需求和具体情况进行综合考虑。对于特定的项目和团队,这些微前端框架都可能是合适的选择。建议在选择之前详细了解它们的特点、文档和使用案例,并根据实际需求进行评估和测试。
  • [技术干货] 【vue3】使用canvas-转载
     canvas是什么? 一个html5支持的新标签,见名知意,canvas就是画板的意思,可以在canvas上画画。css画三角形很简单,但是要画五角星呢,不妨试试canvas。  在html中使用canvas 1、canvas是html5中的一个标签。 新建一个html。并在body中加入canvas标签。  <body>     <canvas height="600" width="600"></canvas> </body> 此时canvas已经显示在画板中,只不过因为和body的颜色一样,所以看不出来。  在head中加入css样式。  <style>     canvas {         border:1px solid;     } </style> 这时我们就可以看到canvas了。   2、获取CanvasRenderingContext2D对象用于绘图。 先给canvas一个id属性,  <canvas id='canvas' height="600" width="600"></canvas> 1 获取。  <script>   const ctx=document.querySelector('#canvas').getContext('2d'); </script> 注意,script标签应该在body标签后(至少在canvas标签后),只有在canvas渲染后才能通过JavaScript代码获取到canvas中的CanvasRenderingContext2D对象。  <body>     <canvas height="600" width="600"></canvas> </body> <script>   const ctx=document.querySelector('.canvas').getContext('2d'); </script> 3、使用JavaScript代码进行绘画。 <script>   const ctx=document.querySelector('#canvas').getContext('2d');   ctx.moveTo(100,100);   ctx.lineTo(100,400);   ctx.stroke(); </script> 该代码绘制了一条直线。   关于CanvasRenderingContext2D对象更多的绘制方法请参考官方文档。至少现在我们知道canvas是如何使用的了。(一定要注意要在渲染完成后才能通过JavaScript代码获取CanvasRenderingContext2D对象)  在vue3中使用canvas 1、创建vite+vue3项目并运行。 npm init vue@latest 1  2、创建我们的canvas。 这是我们的App.vue文件  <script setup>  </script>  <template>  </template>  <style scoped>  </style> 创建我们的canvas  <script setup>  </script>  <template>   <canvas height="600" width="600"></canvas> </template>  <style scoped> canvas {   border: 1px solid; } </style>  3、获取CanvasRenderingContext2D对象并绘制直线。 给canvas标签添加一个ref属性  <canvas ref='canvas' height="600" width="600"></canvas> 1 获取canvas对象  <script setup> import {ref} from 'vue' const canvas = ref(); </script> 渲染完成后获取CanvasRenderingContext2D对象  <script setup> import { onMounted, ref } from 'vue'  const canvas = ref();  onMounted(() => {   const ctx = canvas.value.getContext('2d');  })  </script> 画一条直线  <script setup> import { onMounted, ref } from 'vue'  const canvas = ref();  onMounted(() => {   const ctx = canvas.value.getContext('2d');   ctx.moveTo(100, 100);   ctx.lineTo(100, 400);   ctx.stroke();   })  </script>  4、模板 <script setup> import { onMounted, ref } from 'vue'  const canvas = ref(); let ctx = ref();  const drawLine = () => {   ctx.moveTo(100, 100);   ctx.lineTo(100, 400);   ctx.stroke(); }  const initContext = () => {   ctx = canvas.value.getContext('2d'); }  onMounted(() => {   initContext();   drawLine(); })  </script>  <template>   <canvas ref='canvas' height="600" width="600"></canvas> </template>  <style scoped> canvas {   border: 1px solid; } </style>  canvas快速入门 绘制折线 一个moveTo配合多个lineTo。可以通过lineWidth设置线宽,还可以设置两个端点和转折处的形状(使用lineCap和lineJoin)  // 使用moveTo,lineTo,lineWidth,lineCap,lineJoin const drawCurvedLine = () => {   ctx.moveTo(100, 100);   ctx.lineTo(400, 100);   ctx.lineTo(100, 400);   ctx.lineTo(400, 400);   ctx.lineCap = 'round';   ctx.lineJoin = 'round';   ctx.stroke(); } 绘制矩形 rect方法以及strokeRect和fillRect。效果等效:strokeRect=rect+stroke,fillRect=rect+stroke。  绘制方式:绘制边框,使用stroke,绘制填充,使用fill。strokeStyle可以设置边框颜色,fillStyle可以设置填充颜色。  // 使用rect,srokeStyle,stroke,fillStyle,fill const drawStrokeRect = () => {   ctx.rect(100, 100, 100, 100);   ctx.strokeStyle = 'green';   ctx.stroke(); }  const drawFillRect = () => {   ctx.rect(300, 100, 100, 100);   ctx.fillStyle = 'blue';   ctx.fill(); } 将绘制一个绿色边框的矩形和蓝色的矩形。然而,当一同调用时,会发现变成了两个一模一样的矩形(绿色边框或者蓝色填充)。  属性作用域:解决上述问题,使用beginPath方法即可。beginPath将后面对于属性的设置隔离开来,以避免覆盖前面的属性。  // 这里还尝试了使用strokeRect和fillRect替代了rect、stroke、fill const drawStrokeRect = () => {     ctx.beginPath();     ctx.strokeStyle='green';     ctx.strokeRect(100,100,100,100); }  const drawFillRect = () => {   ctx.beginPath();   ctx.fillStyle = 'blue';   ctx.fillRect(300, 100, 100, 100); } 绘制弧线 圆圈  ctx.beginPath(); ctx.arc(100,75,50,0,2*Math.PI); ctx.stroke(); 圆弧  ctx.beginPath(); ctx.arc(100,75,50,90/180*Math.PI,2*Math.PI); ctx.stroke(); 扇形  ctx.beginPath(); ctx.moveTo(100,75); ctx.arc(100,75,50,90/180*Math.PI,2*Math.PI); ctx.closePath(); ctx.fill(); 圆环    const RINGWIDTH = 10;    ctx.beginPath();   ctx.arc(100, 75, 90, 0, 2 * Math.PI);   ctx.fill();   ctx.beginPath();   ctx.arc(100, 75, 90-2*RINGWIDTH, 0, 2 * Math.PI);   ctx.fillStyle = 'white';   ctx.fill(); 补充:  如你所见,绘制扇形时使用了closePath,意思是将所有端点连接起来(一般是将终点和起点连接起来,形成一个闭合图形。只有图形闭合时,fill才能生效)。 所有函数的参数不需要单位。(设置字体时,ctx.font=‘40px’,需要带单位,但确实不是函数的参数) 需要角度作为参数时,都是以弧度的形式提供。计算公式,弧度=角度*Math.PI/180。90度,记为90*Math.PI/180。 更多关于画布的使用,可以查看HTML Canvas 参考手册 (w3school.com.cn) ———————————————— 版权声明:本文为CSDN博主「46590928」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/weixin_46590928/article/details/127358493 
  • [技术干货] Vue中实现自定义excel下载-转载
     最近在工作中遇到一个需求,就是需要在前端实现一个错误模板Excel的下载功能。  实现下载有两种方式,一种是后端生成一个excel,放在服务器指定目录下,然后前端直接去后端拿。第二种是后端传给前端一个json的list,前端用后端传过来的json的list直接在前端合成一个excel。  第一种:后端生成excel java后端生成excel代码  生成excel工具方法  /** * @author: wu linchun * @creat: 2021-07-29 9:49 * @desc: 将list集合转成Excel文件 * list: 内容list * path: 上传的excel路径 * fileName: 上传的excel名称 **/public static String createExcel(List<? extends Object> list, String path, String fileName) {String result = "";if (list.size() == 0 || list == null) {result = "没有对象信息";} else {Object o = list.get(0);Class<? extends Object> clazz = o.getClass();String className = clazz.getSimpleName();//这里通过反射获取字段数组Field[] fields = clazz.getDeclaredFields();File folder = new File(path);if (!folder.exists()) {folder.mkdirs();}String name = fileName.concat(".xls");WritableWorkbook book = null;File file = null;try {file = new File(path.concat(File.separator).concat(name));//创建xls文件book = jxl.Workbook.createWorkbook(file);WritableSheet sheet = book.createSheet(className, 0);//列int i = 0;//行int j = 0;for (Field f : fields) {j = 0;//这里把字段名称写入excel第一行中Label label = new Label(i, j, f.getName());sheet.addCell(label);j = 1;for (Object obj : list) {Object temp = getFieldValueByName(f.getName(), obj);String strTemp = "";if (temp != null) {strTemp = temp.toString();}//把每个对象此字段的属性写入这一列excel中sheet.addCell(new Label(i, j, strTemp));j++;}i++;}book.write();result = file.getPath();} catch (Exception e) {// TODO Auto-generated catch blockresult = "SystemException";e.printStackTrace();} finally {fileName = null;name = null;folder = null;file = null;if (book != null) {try {book.close();} catch (WriteException e) {// TODO Auto-generated catch blockresult = "WriteException";e.printStackTrace();} catch (IOException e) {// TODO Auto-generated catch blockresult = "IOException";e.printStackTrace();}}}}//最后输出文件路径return result;}  1 <img src=“https://juejin.cn/ “点击并拖拽以移动”” style=“margin: auto” />  下载文件   /** * @author: wu linchun * @creat: 2021-07-29 13:28 * @desc: 下载错误模板 **/@Overridepublic ResponseEntity<Object> downloadErrorModel() throws FileNotFoundException {// 这里的fileName是指路径+文件名String fileName="move_cms/src/main/resources/static/errorList.xls";File file = new File(fileName);InputStreamResource resource = new InputStreamResource(new FileInputStream((file)));org.springframework.http.HttpHeaders headers = new org.springframework.http.HttpHeaders();headers.add("Content-Disposition", String.format("attachment;filename="%s"", file.getName()));headers.add("Cache-Control", "no-cache,no-store,must-revalidate");headers.add("Pragma", "no-cache");headers.add("Expires", "0");ResponseEntity<Object> responseEntity = ResponseEntity.ok().headers(headers).contentLength(file.length()).contentType(MediaType.parseMediaType("application/text")).body(resource);return responseEntity;}  1 <img src=“https://juejin.cn/ “点击并拖拽以移动”” style=“margin: auto” />  下载excel的接口   /** * @author: wu linchun * @creat: 2021-07-29 11:41 * @desc: 下载错误模板 **/@ApiOperation(value = "下载错误模板")@GetMapping(value = "/downloadErrorModel")@PassTokenpublic ResponseEntity<Object> downloadErrorModel() throws FileNotFoundException {System.out.println("下载错误模板");return welfareGrantService.downloadErrorModel();}  1 <img src=“https://juejin.cn/ “点击并拖拽以移动”” style=“margin: auto” />  在掉/downloadErrorModel这个接口是要注意,只能用超链接 “<a:href />调用,不能使用axios调这个接口。这是由于axios获取的是json  而这个接口返回的是file类型,file会以byte流的形式在http上面传输,因此调/downloadErrorModel这个接口将会接收到byte流,axios默认是接收不了byte的,需要进行一些特殊的设置。可以参考一下这篇:vue使用axios接收流文件_weixin_43869439的博客-CSDN博客  <img src=“https://juejin.cn/ “点击并拖拽以移动”” style=“margin: auto” />  如果使用超链接的方式访问接口进行文件下载的话,则需要获取到后端服务器的ip+port (实际中不建议这么做,因为get请求会暴露ip地址和端口,可通过浏览器f12看到ip地址和端口,不安全)  ip地址可通过 InetAddress.getLocalHost()获取到,端口号直接用@Value从配置文件中拿到。  第二种:前端合成excel 在vue工程中引入两个依赖:file-saver和xlsx  <img src=“https://juejin.cn/ “点击并拖拽以移动”” style=“margin: auto” />  使用“npm install”,安装package.json中新增的依赖  npm install  1 <img src=“https://juejin.cn/ “点击并拖拽以移动”” style=“margin: auto” />  <img src=“https://juejin.cn/ “点击并拖拽以移动”” style=“margin: auto” />  新增 ExportExcelUtil.js,用来根据list生成excel  /* eslint-disable */ import { saveAs } from 'file-saver' import XLSX from 'xlsx'  function generateArray(table) {var out = [];var rows = table.querySelectorAll('tr');var ranges = [];for (var R = 0; R < rows.length; ++R) {var outRow = [];var row = rows[R];var columns = row.querySelectorAll('td');for (var C = 0; C < columns.length; ++C) {var cell = columns[C];var colspan = cell.getAttribute('colspan');var rowspan = cell.getAttribute('rowspan');var cellValue = cell.innerText;if (cellValue !== "" && cellValue == +cellValue) cellValue = +cellValue;//Skip rangesranges.forEach(function (range) {if (R >= range.s.r && R <= range.e.r && outRow.length >= range.s.c && outRow.length <= range.e.c) {for (var i = 0; i <= range.e.c - range.s.c; ++i) outRow.push(null);}});//Handle Row Spanif (rowspan || colspan) {rowspan = rowspan || 1;colspan = colspan || 1;ranges.push({s: {r: R,c: outRow.length},e: {r: R + rowspan - 1,c: outRow.length + colspan - 1}});};//Handle ValueoutRow.push(cellValue !== "" ? cellValue : null);//Handle Colspanif (colspan)for (var k = 0; k < colspan - 1; ++k) outRow.push(null);}out.push(outRow);}return [out, ranges]; };  function datenum(v, date1904) {if (date1904) v += 1462;var epoch = Date.parse(v);return (epoch - new Date(Date.UTC(1899, 11, 30))) / (24 * 60 * 60 * 1000); }  function sheet_from_array_of_arrays(data, opts) {var ws = {};var range = {s: {c: 10000000,r: 10000000},e: {c: 0,r: 0}};for (var R = 0; R != data.length; ++R) {for (var C = 0; C != data[R].length; ++C) {if (range.s.r > R) range.s.r = R;if (range.s.c > C) range.s.c = C;if (range.e.r < R) range.e.r = R;if (range.e.c < C) range.e.c = C;var cell = {v: data[R][C]};if (cell.v == null) continue;var cell_ref = XLSX.utils.encode_cell({c: C,r: R});if (typeof cell.v === 'number') cell.t = 'n';else if (typeof cell.v === 'boolean') cell.t = 'b';else if (cell.v instanceof Date) {cell.t = 'n';cell.z = XLSX.SSF._table[14];cell.v = datenum(cell.v);} else cell.t = 's';ws[cell_ref] = cell;}}if (range.s.c < 10000000) ws['!ref'] = XLSX.utils.encode_range(range);return ws; }  function Workbook() {if (!(this instanceof Workbook)) return new Workbook();this.SheetNames = [];this.Sheets = {}; }  function s2ab(s) {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; }  export function export_table_to_excel(id) {var theTable = document.getElementById(id);var oo = generateArray(theTable);var ranges = oo[1];/* original data */var data = oo[0];var ws_name = "SheetJS";var wb = new Workbook(),ws = sheet_from_array_of_arrays(data);/* add ranges to worksheet */// ws['!cols'] = ['apple', 'banan'];ws['!merges'] = ranges;/* add worksheet to workbook */wb.SheetNames.push(ws_name);wb.Sheets[ws_name] = ws;var wbout = XLSX.write(wb, {bookType: 'xlsx',bookSST: false,type: 'binary'});saveAs(new Blob([s2ab(wbout)], {type: "application/octet-stream"}), "test.xlsx") }  export function export_json_to_excel({multiHeader = [],header,data,filename,merges = [],autoWidth = true,bookType = 'xlsx' } = {}) {/* original data */filename = filename || 'excel-list'data = [...data]data.unshift(header);for (let i = multiHeader.length - 1; i > -1; i--) {data.unshift(multiHeader[i])}var ws_name = "SheetJS";var wb = new Workbook(),ws = sheet_from_array_of_arrays(data);if (merges.length > 0) {if (!ws['!merges']) ws['!merges'] = [];merges.forEach(item => {ws['!merges'].push(XLSX.utils.decode_range(item))})}if (autoWidth) {/*设置worksheet每列的最大宽度*/const colWidth = data.map(row => row.map(val => {/*先判断是否为null/undefined*/if (val == null) {return {'wch': 10};}/*再判断是否为中文*/else if (val.toString().charCodeAt(0) > 255) {return {'wch': val.toString().length * 2};} else {return {'wch': val.toString().length};}}))/*以第一行为初始值*/let result = colWidth[0];for (let i = 1; i < colWidth.length; i++) {for (let j = 0; j < colWidth[i].length; j++) {if (result[j]['wch'] < colWidth[i][j]['wch']) {result[j]['wch'] = colWidth[i][j]['wch'];}}}ws['!cols'] = result;}/* add worksheet to workbook */wb.SheetNames.push(ws_name);wb.Sheets[ws_name] = ws;var wbout = XLSX.write(wb, {bookType: bookType,bookSST: false,type: 'binary'});saveAs(new Blob([s2ab(wbout)], {type: "application/octet-stream"}), `${filename}.${bookType}`); }   <img src=“https://juejin.cn/ “点击并拖拽以移动”” style=“margin: auto” />  在.vue中添加下载excel方法  // 下载ExceldownloadExcel () {import('@/utils/ExportExcelUtil').then(excel => {const filterVal = ['item', 'error']const tHeader = ['item', 'error']const data = this.formatModelJson(filterVal, this.errList)console.log(data)console.info(data)excel.export_json_to_excel({header: tHeader,data,filename: 'errorList'})})},// 格式化JSONformatModelJson (filterVal, list) {return list.map(v => filterVal.map(j => {return v[j]}))},  1 <img src=“https://juejin.cn/ “点击并拖拽以移动”” style=“margin: auto” />  errorList是从后端传过来的  exportExcel () {getErrorListExcel().then(response => {this.errList = response.data})}  1 <img src=“https://juejin.cn/ “点击并拖拽以移动”” style=“margin: auto” />  使用created方法,在加载.vue时,就调用/getErrorListExcel接口,获取errorList的值。  created () {this.exportExcel()},  1 <img src=“https://juejin.cn/ “点击并拖拽以移动”” style=“margin: auto” />  调用后端接口的api  import axios from 'axios'  export function getErrorListExcel () {return axios.get('http://127.0.0.1:8082/login/getErrorListExcel', {}).then(response => response).catch(error => error) }  <img src=“https://juejin.cn/ “点击并拖拽以移动”” style=“margin: auto” />  后端接口设置errorList中的值   @GetMapping("/getErrorListExcel")public List<ErrorItemBO> getErrorListExcel() {List<ErrorItemBO> list = new ArrayList<>();for (int i = 0; i < 10; i++) {ErrorItemBO errorItemBO = new ErrorItemBO();errorItemBO.setItem("item" + i);errorItemBO.setError("error" + i);list.add(errorItemBO);}return list;}  1 <img src=“https://juejin.cn/ “点击并拖拽以移动”” style=“margin: auto” />  <img src=“https://juejin.cn/ “点击并拖拽以移动”” style=“margin: auto” />  <img src=“https://juejin.cn/ “点击并拖拽以移动”” style=“margin: auto” />  总结 如果涉及到文件下载,尽量后端传一个list,然后都在前端合成相应的文件以到达减轻服务器负担的作用。 ​  最后 最近找到一个VUE的文档,它将VUE的各个知识点进行了总结,整理成了《Vue 开发必须知道的36个技巧》。内容比较详实,对各个知识点的讲解也十分到位。   ———————————————— 版权声明:本文为CSDN博主「梅花十三儿」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/Android062005/article/details/127207166 
  • [技术干货] vue-day01 使用cdn引入使用-转载
     vue? Vue 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。  vue简明例子 首先使用cdn引入vue.js文件,<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>。 vue.js?声明式渲染,条件循环指令,处理用户表单的输入,以及自定义组件,模板语法…  <!DOCTYPE html> <html lang="en"> <head>     <meta charset="UTF-8">     <title>vue-01</title>  </head> <body>      <div id="app">         {{message}}     </div>     <div id="app2" style="border: 1px solid red;">         <span v-bind:title="message">             鼠标悬停几秒查看信息         </span>     </div>     <div id="app3">         <p v-if="seen">现在你看到我了</p>     </div>     <div id="app4">         <ol>             <li v-for="todo in todos">                 {{todo.text}}             </li>         </ol>     </div>      <div id="app5">         <p>{{message}}</p>         <button v-on:click="reverseStr">点击反转</button>     </div>     <!-- 开发环境版本,包含了有帮助的命令行警告 -->     <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>      <script>         /*简单数据绑定,使用mustache语法将数据与app对象绑定*/         let app = new Vue({             el: '#app',             data: {                 message: 'Hello Vue!'             }         });            /*使用v-bind指令,响应式的更新html属性*/         let app2 = new Vue({             el:'#app2',             data: {                 message:"哈哈哈,我成功了!vue开始"             }         });         /*使用v-if指令,根据值来判断对应元素是否显示*/         let app3 = new Vue({             el: '#app3',             data:{                 seen:true             }         });         /*使用v-for指令,将todos中的内容遍历到html上*/         let app4 = new Vue({             el:'#app4',             data:{                 todos:[                     {text:"学习"},                     {text:"真的"},                     {text:"需要坚持"}                 ]             }         });         /*利用v-on:click来监听点击事件,然后对原本的字符串进行反转*/         let app5 = new Vue({             el:"#app5",             data:{                 message:"反转!"             },             methods:{                 reverseStr:function () {                     this.message=this.message.split('').reverse().join('')                 }             }         });     </script>  </body> </html> 
  • [技术干货] 使用Vue实现轮播图组件
    在本篇博客中,我们将使用Vue来实现一个简单的轮播图组件。我们将利用Vue的生命周期钩子、数据绑定和计算属性等特性来完成这个功能。准备工作首先,我们需要创建一个Vue组件,用于展示轮播图。在这个例子中,我们将创建一个名为Carousel的组件。模板我们首先来定义组件的模板,即HTML结构。我们将使用一个ul元素来包含轮播图的图片,以及一个指示器来显示当前显示的图片。<template> <div class="carousel"> <ul class="slides"> <li v-for="(slide, index) in slides" :key="index" :class="{ active: currentIndex === index }"> <img :src="slide.image" alt="Slide Image"> </li> </ul> <div class="indicators"> <span v-for="(slide, index) in slides" :key="index" :class="{ active: currentIndex === index }" @click="goToSlide(index)" ></span> </div> </div> </template>在上面的代码中,我们使用了Vue的指令v-for来循环渲染轮播图的每个图片。我们还根据当前索引currentIndex来设置每个轮播图项的active类,以及根据点击事件切换到对应的轮播图。数据和计算属性接下来,我们需要在组件的data中定义轮播图的数据和当前索引。我们使用一个数组slides来存储每个轮播图项的信息,以及一个整数currentIndex来表示当前显示的轮播图索引。<script> export default { data() { return { slides: [ { image: 'slide1.jpg' }, { image: 'slide2.jpg' }, { image: 'slide3.jpg' } ], currentIndex: 0 }; }, // ... }; </script>生命周期钩子我们需要在组件挂载后启动轮播功能。为此,我们可以使用Vue提供的mounted生命周期钩子。<script> export default { // ... mounted() { this.startCarousel(); }, // ... }; </script>在mounted钩子中,我们调用startCarousel方法来启动自动轮播功能。方法接下来,我们需要定义一些方法来实现轮播图的切换。<script> export default { // ... methods: { startCarousel() { setInterval(this.nextSlide, 3000); }, nextSlide() { this.currentIndex = (this.currentIndex + 1) % this.slides.length; }, goToSlide(index) { this.currentIndex = index; } } }; </script>在上面的代码中,我们使用setInterval函数来定时调用nextSlide方法,以实现自动切换轮播图。nextSlide方法将当前索引递增,并使用取余运算符确保索引在轮播图数组范围内循环。goToSlide方法用于切换到指定的轮播图。样式最后,我们需要为组件添加一些样式,以使其呈现为一个轮播图。<style scoped> .carousel { position: relative; width: 100%; height: 300px; } .slides { list-style: none; position: relative; width: 100%; height: 100%; } .slides li { position: absolute; /* 设置轮播图项的样式 */ } .indicators { /* 设置指示器的样式 */ } </style>在上述代码中,我们为组件容器.carousel设置了宽度和高度,并使用绝对定位来布局轮播图项。你可以根据需要自定义轮播图项和指示器的样式。运行代码现在,我们已经完成了轮播图组件的实现。你可以在Vue应用中使用<carousel></carousel>标签来引入这个组件。<template> <div> <!-- 其他内容 --> <carousel></carousel> <!-- 其他内容 --> </div> </template> <script> import Carousel from './Carousel.vue'; export default { components: { Carousel }, // ... }; </script>运行代码后,你将看到轮播图按照指定的时间间隔自动切换,并且你也可以点击指示器切换到对应的轮播图。总结通过使用Vue的特性,我们可以很方便地实现一个轮播图组件。我们利用了生命周期钩子、数据绑定和计算属性等功能,使得轮播图的切换和渲染变得简单而灵活。希望本篇博客能对你理解Vue实现轮播图组件有所帮助!如果你有任何问题或疑问,欢迎提出。
  • [技术干货] vue如何使用print-nb库来实现打印功能
    步骤1:安装print-nb首先,我们需要安装print-nb库。我们可以使用npm包管理器执行以下命令来安装:npm install print-nb --save步骤2:引入print-nb接下来,我们需要在Vue组件中引入print-nb库。以下是一种方法:import Print from 'print-nb';步骤3:创建打印功能现在,我们将创建一个Vue方法来处理打印功能。以下是示例代码:print() { let options = { // 打印样式表 styles: [ 'path/to/styles.css' ], // 要打印的元素的id id: 'printable' }; Print(options);}在这里,我们首先定义打印选项。我们指定要在打印中使用的样式表和要打印的元素的id。然后,我们使用Print()函数来处理打印。Print()函数将弹出一个打印对话框,并启动打印功能。步骤4:调用打印功能最后,我们需要在Vue组件中调用打印功能。以下是示例代码:<template> <div> <!-- 要打印的内容 --> <div id="printable"> ... </div> <!-- 触发打印功能 --> <button @click="print">打印</button> </div></template><script>import Print from 'print-nb';export default { methods: { print() { let options = { // 打印样式表 styles: [ 'path/to/styles.css' ], // 要打印的元素的id id: 'printable' }; Print(options); } }}</script>在这里,我们指定要打印的内容,并定义一个按钮来触发打印功能。当用户点击该按钮时,将调用print()方法并执行打印功能。结论到此为止,我们已经学会了如何使用print-nb库在Vue.js应用程序中实现打印功能。print-nb库提供了一个简单而强大的方式来实现打印功能,并可以轻松集成到Vue.js应用程序中。
  • [技术干货] 如何使用Vue.js来实现打印功能
    在Vue应用程序中,我们可能需要实现打印功能。幸运的是,在现代Web浏览器中,打印功能已经内置了。在本文中,我们将学习如何使用Vue.js来实现打印功能,并将其与打印机集成。步骤1:安装打印机驱动程序首先,我们需要确保计算机上安装了打印机驱动程序。如果您已经安装了打印机驱动程序,则可以跳过此步骤。否则,请从打印机制造商的网站下载并安装适当的驱动程序。步骤2:引入打印样式表接下来,我们需要引入一个打印样式表,以确保在打印时页面布局正确。以下是一个示例样式表:@media print { body * { visibility: hidden; } #printable, #printable * { visibility: visible; } #printable { position: absolute; left: 0; top: 0; }}在这里,我们使用@media print媒体查询来为打印定义样式。我们首先将所有元素的可见性设置为隐藏,然后将要打印的部分(具有id“printable”)和其所有子元素的可见性设置为可见。最后,我们将打印部分定位为绝对位置,以确保它在打印时正确显示。步骤3:创建打印功能现在,我们将创建一个Vue方法来处理打印功能。以下是示例代码:print() { const printable = document.getElementById('printable'); const originalBody = document.body.innerHTML; document.body.innerHTML = printable.innerHTML; window.print(); document.body.innerHTML = originalBody;}在这里,我们首先获取具有id“printable”的元素。然后,我们将当前文档的HTML内容替换为要打印的部分的HTML内容。接下来,我们使用window.print()方法来打印页面。最后,我们将文档的HTML内容恢复为原始值。步骤4:集成打印机最后,我们需要将打印与打印机集成。在现代Web浏览器中,打印对话框已经内置了打印机选择器。因此,只需调用打印方法即可使用用户选择的打印机。以下是修改后的打印方法示例代码:print() { const printable = document.getElementById('printable'); const originalBody = document.body.innerHTML; document.body.innerHTML = printable.innerHTML; window.print(); document.body.innerHTML = originalBody;}在这里,我们删除了打印机选择器,并在调用window.print()方法时使用默认打印机。结论到此为止,我们已经学会了在Vue.js应用程序中使用打印功能,并与打印机集成。Vue.js提供了一个简单而强大的方法来实现打印功能,而无需编写复杂的代码。
  • [技术干货] Vue-CLI 3.0中配置production gzip
     为了在Vue-CLI 3.0中配置production gzip,您需要按照以下步骤进行:  安装compression-webpack-plugin: npm install compression-webpack-plugin --save-dev  在vue.config.js文件中添加下面的代码:  javascript const CompressionWebpackPlugin = require('compression-webpack-plugin')  module.exports = {   configureWebpack: config => {     if (process.env.NODE_ENV === 'production') {       return {         plugins: [           new CompressionWebpackPlugin({             filename: '[path].gz[query]',             algorithm: 'gzip',             test: /\.(js|css)$/,             threshold: 10240,             minRatio: 0.8           })         ]       }     }   } } 其中,CompressionWebpackPlugin插件可以帮助我们生成gzip文件。  配置完成后,运行npm run build会自动生成压缩文件。  最后,在服务器上设置正确的响应头,才能让浏览器正确解压缩文件。例如,使用Nginx时,可以通过添加以下指令来启用gzip压缩:  gzip on; gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript; gzip_min_length 10240; gzip_comp_level 6; gzip_vary on; gzip_disable "msie6"; 这些是一些常见的gzip压缩设置,您可以根据自己的需求进行修改。 
  • [技术干货] vue之el-menu-item如何更改导航菜单栏选中的背景颜色【转】
    el-menu-item更改导航菜单栏选中的背景颜色修改element导航栏选中时的背景色,可以通过设置 .el-menu-item.is-active 选择器的样式属性 【background-color】使用 !important 使得优先级最大。如下:1234567.el-menu-item.is-active {      background-color: #3370ff !important;      color: #fff;      span {        color: #fff !important;      }    }element的导航栏修改其选中时颜色的问题修改element导航栏选中时的背景色,通过浏览器选中导航栏查看可知修改样式.el-menu-item.is-active即可。于是……123.el-menu-item.is-active {  background: #247bb4 !important;}以为完事了吗?不不不正常使用时效果确实达到了但是长时间闲置页面,再回头看这个页面时会出现哪里来的灰色???鼠标点击界面任意位置,灰色立马消失……我……疯狂百度,没人有这种情况但是看到别人的代码如下:123.el-menu-item.is-active {  background-color: #247bb4 !important;}于是尝试着用background-color替换background好像还真的OK了再也没出现过如上情况了然后过了好几个月,发现这个现象还是存在一次偶然的机会,被我捕获到了问题哈哈哈莫名其妙这个时候会给当前元素加上background-color: rgb(204, 204, 204);然后只要点击屏幕,瞬间这个值会变成background-color: rgba(0, 0, 0, 0.1);所以,我只要给它默认设一下背景色就好了啊!!!修改代码如下:123.el-menu-item {  background-color: rgba(0, 0, 0, 0.1) !important;}这次是真的OK了!!!心塞啊心塞,这个还是没解决因为直接强制修改el-menu-item的背景色,会导致想要它变色的时候变不了了啊啊啊,脑壳痛。直接看最终样式修改因为需要菜单透明,于是乎这么设置以后很久没有给我蒙上一层灰了。123456789<el-menu router class="el-menu-vertical-demo menu-Bar" background-color="rgba(36,123,180,0.51)" text-color="#b4dbf4" active-text-color="#7BE4FF">  <navMenu :navMenus="menuData"></navMenu></el-menu>.el-menu-item {  background-color: rgba(0, 0, 0, 0.1);}.el-menu-item.is-active {  background-color: #247bb4 !important;}总结以上为个人经验,希望能给大家一个参考
  • [问题求助] 高级页面上传apk文件无法上传
    我们现在需要上传一个apk文件,但是我在论坛搜到的是cube平台好像不支持此类文件的上传,有什么解决办法吗
  • [技术干货] Vue.js 状态管理:Pinia 与 Vuex-转载
    uex 和 Pinia 是用于管理 Vue.js 应用程序状态的标准 Vue.js 库。让我们比较一下他们的代码、语言、功能和社区支持。  如果没有合适的库,开发人员管理应用程序的状态可能会很困难。Vuex 和 Pinia 是标准的 Vue.js 库,用于处理应用程序中的条件。这两个库都非常适合状态管理,但是由于它们出色的特性和功能,选择哪个库用于你的项目需要时间并且令人沮丧。好吧,我们将在本文中看看为什么一个是最好的。  在本文中,我们将通过实际代码示例查看代码比较、它们的变体、功能,以及推荐使用哪一个来管理你的状态应用程序,以便更好地理解。我们还将考虑每个产品的语言、功能和社区支持。  Pinia 和 Vuex 简介 我将简要总结 Vuex 和 Pinia。如果你想要更详尽的解释,我建议阅读Vuex 文档和Pinia 文档。  什么是Pinia? Pinia是一个新的状态管理库,可帮助你在 Vue.js 应用程序中跨组件管理和存储响应数据和状态。Pinia 由 Vue 核心团队成员之一 Eduardo San Martin Morote 创建。  Pinia 使用新的反应系统来构建一个直观且完全类型化的状态管理系统。  库中引入的新特性是促成 Pinia 推荐的因素之一。总体而言,Pinia 显得轻巧、简单且易于掌握。它拥有可以使 Vue 3 和 Vue 2 中的编程变得通用的一切。因此,这是试用 Pinia 的理想机会。  什么是 Vuex? Vuex是一种状态管理模式和库,构建为集中式存储,可帮助你维护 Vue 应用程序中存在的所有组件的状态。Vuex 遵循确保你的状态突变为预测标准的规则。  使 Vuex 更强大的一个因素是组件从 Vuex store 中获取它们的状态,并且可以快速有效地响应 store 状态的变化。  Vuex虽然是维护你store的状态管理库,但建议你熟悉或搭建过大型SPA。如果你没有经验,Vuex 可能会冗长且令人生畏。  如果你的应用程序很广泛,你需要管理复杂的数据流,并且你有嵌套的组件,你可以使用 Vuex。查看官方文档以获取有关何时使用 Vuex 的更多信息。  Pinia 的特点 Pinia 和 Vuex 之间的区别之一是 Pinia 是“模块化设计”,换句话说,它被构建为拥有多个商店,而 Vuex 只有一个商店。在这些商店中,您可以拥有子模块。除此之外,Pinia 允许将这些模块中的每一个从他们的商店直接导入到需要的组件中。  模块化设计 如果您是一名 Vue 开发人员并且曾使用 Vuex 管理应用程序的状态,您会注意到 Vuex 只有一个商店。在该商店中,您可以在其中包含多个模块。使用 Pinia,您可以将这些模块中的每一个都存储在一个地方,并在需要时将它们直接导入到组件中。  此方法允许捆绑器自动对它们进行代码拆分,并提供更好的 TypeScript 推理。  完整的开发工具支持 Pinia 提供开发工具支持,以帮助您使用该库构建和轻松调试。当我们安装 Pinia 时,它会自动挂接到我们的 Vue.js 开发工具,并让我们跟踪对我们的商店所做的更改,这让我们在 Vue.js 版本(Vue 2 和 Vue3)中获得流畅的开发人员体验。  Pinia 很直观 Pinia 提供了一个简单的 API,使您的商店的编写变得简单且井井有条,就像创建组件一样。这意味着与 Vuex 解决方案相比,需要掌握的样板文件和概念更少。  Pinia 是可扩展的 Pinia 还提供了一个完整的插件系统,具有交易和本地存储同步等功能,适用于 Pinia 默认行为不足的情况。  TypeScript 支持 Pinia 提供比 Vuex 更好的 TypeScript 支持,具有 Javascript 自动完成功能,这使得开发过程变得简单。  Pinia 轻量的 Pinia 的重量只有 1 KB,因此很容易融入您的项目。这可能会提高您的应用程序性能。  Vuex的特点 模块 当您的应用程序扩展时,遍历变得很困难。但是,使用 Vuex 模块,您可以根据领域功能将您的商店拆分为多个文件,并从该特定命名空间中的模块访问状态循环。  开发工具支持 Vue devtools 中的 Vuex 选项卡允许我们查看状态并跟踪我们的变化。这使我们能够快速评估我们的程序如何执行和调试错误。  热重载 Vuex支持热重载功能,它使用 webpack 的热模块替换 API,可以在您开发时快速重载您的 mutations、modules、action 和 getters。  TypeScript 支持 如果你想编写一个 TypeScript 存储定义,Vuex 可以提供它的类型并且更容易实现。它有一个默认的 TypeScript 配置,不需要额外的设置。  Pinia和Vuex的代码对比 Pinia 是一个轻量级库,可帮助您管理整个应用程序的反应状态。与 Vuex 相比,Pinia API 易于学习,使您的代码更易于阅读。  让我们看一下使用 Pinia 和 Vuex 管理我们的状态的代码比较:  Vuex  在此示例中,我们将查看一个简单的 Vuex 存储,它跟踪待办事项列表项的状态:  import { createStore } from 'vuex'  const TodoListstore = createStore({   state() {     return {       todoListItems: []     }   },   actions: {     addNewList({ commit }, item) {     {       commit('createNewItem', item)     }   },   mutations: {     createNewItem(state, item) {       state.todoListItems.push(item)     }   } })  1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 如果你看上面的代码,你可以看到 Vuex 存储中的三个动作:状态、 动作和突变。状态返回当前的todoListItems,动作提交一个突变来创建一个新项目,最后,突变创建新项目并将其添加到列表中。当你构建一个更大的应用程序时,你可能会意识到动作和突变相对相似,导致冗余代码,因为每个状态变化都需要一个样板。  Pinia  使用 Pinia 简单 API,您可以消除突变和冗余代码。让我们查看一个代码示例,了解当您使用 Pinia 实现之前的代码时的样子:  import { defineStore } from 'pinia'  Export const useListStore = defineStore('list', {   state() => ({     return {       todoListItems: []     }   }),   actions: {     addNewList() {         this.todoListItems.push(item)     }   }}) 1 2 3 4 5 6 7 8 9 10 11 12 13 上面的示例是 Pinia API 在管理应用程序状态时如何工作的简单代码。使用 Pinia,我们删除了突变并将其直接更新到我们的动作中。  注意:在上面的代码示例中,当我们将项目直接提交给我们的操作时,我们不需要跟踪我们的项目。  Pinia 和 Vuex 的优缺点 Pinia 和 Vuex 是控制应用程序状态的优秀工具,但其中一个必须具有另一个所没有的某些功能。让我们来看看它们是什么。  Pinia 的优点 Pinia 允许你在不重新加载页面的情况下修改你的商店。 它在 JavaScript 中提供 TypeScript 支持和良好的自动完成功能。 Pinia 提供 devtool 支持,这有助于增强开发人员使用该工具的体验。 Pinia 只有状态、吸气剂和动作。不需要突变。 使用 Pinia,你可以将状态存储在一个地方,并在请求时将其传递给它们的组件。 它是一个重量为 1 KB 的轻量级库。 它提供服务器端渲染支持和自动类型模块,无需额外工作。 Pinia 兼容 Vue 2 和 Vue 3。 Pinia 的缺点 与 Vuex 相比,它没有庞大的社区支持和解决方案。 Pinia 不支持调试功能,如时间旅行和编辑。 Vuex 的优点 Vuex 有 mutations、getters 和 actions。 与 Pinia 相比,Vuex 的社区支持很大。 Vuex 支持调试功能,如时间旅行和编辑。 Vuex 的缺点 它对 TypeScript 不友好。 你只能将其用于大型 SPA。 我应该使用哪个:Pinia 还是 Vuex? 好吧,这就是它变得更具挑战性的地方,因为仍然有一些项目需要使用 Vuex 来处理状态应用程序,即使 Pinia 是新推荐的状态管理库。它有几个 Vuex 没有的有价值的功能。  Vuex 仍然是构建大型 SPA 的理想解决方案,因为对于构建中小型应用程序的人来说,它相当冗长。  Pinia 也一样。尽管如此,如果你需要所有列出的功能,例如 devtool 支持、TypeScript 支持和状态应用程序的轻松管理,那么 Pinia 是最好的解决方案——它为你提供流畅的开发体验。  如果你正在构建一个不太复杂的应用程序,无论是中等到广泛的应用程序,你都可以使用 Pinia,因为它的重量约为 1 KB。  结论 由于功能多样,在管理应用程序状态时,在 Vuex 和 Pinia 之间进行选择可能会造成混淆。不过,这两个框架都非常适合管理状态应用程序。本文简要比较了它们的特性、功能和对代码的影响。读完这篇文章后,也许你将能够找到适合你的库。 ———————————————— 版权声明:本文为CSDN博主「海拥✘」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/qq_44273429/article/details/128932617 
  • [技术干货] 怎么给Vue对象增加响应式属性
    这篇文章主要讲解了“怎么给Vue对象增加响应式属性”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“怎么给Vue对象增加响应式属性”吧!Vue.$set给对象新增属性Vue.$set()Object.assign()$set()方法不生效时,改用Object.assign()试试vue代码中,只要在data对象里定义的对象,赋值后,任意一个属性值发生变化,视图都会实时变化比如下面在data定义了obj对象,mounted里赋值后,(也可以在其他地方赋值)只要obj.a或者obj.b的值改变了,视图会跟着变化。data() { return { obj: {} } },mounted: { this.obj = {a:1, b: 2} // 改变this.obj.a this.obj.c的值视图会更新 this.obj.c = 3 // 改变this.obj.c的值  视图不会更新 Object.assign(this.obj, {d: 4}) // 改变this.obj.c的值 视图不会更新 this.$set(this.obj, 'e', 5) // 改百年this.obj.e时 视图会更新 console.log('obj' + this.obj) }但是我们在obj对象上新增的属性变化时,值会变化,但是视图不会实时变化比如obj.c或者obj.d变化时,值虽然会变,但是视图不会跟着变Vue.$set()使用Vue.$set()方法,既可以新增属性,又可以触发视图更新,比如obj.e改变时,视图会相应改变打印出的obj,可以看出,新增的属性只有通过this.set()的obj.e属性有get和set方法的,而新增的obj.c和obj.d没有根据官方文档定义:如果在实例创建之后添加新的属性到实例上,它不会触发视图更新。当我们把一个JavaScript 对象传入 Vue 实例作为 data 选项,Vue 将遍历此对象所有的属性,并使用 Object.defineProperty把这些属性全部转为 getter/setter。Object.assign()给data定义的对象新增属性,同时又要视图实时更新,除了用Vue.$set()方法,也可以通过Object.assign()实现data() { return { obj: {} } },mounted: { this.obj = { a: 1, b: 2 }    this.obj.c = 3     Object.assign(this.obj, { d: 4 })    // this.$set(this.obj, 'e', 5) // 下面这一行代码才触发了视图更新     this.obj = Object.assign({}, this.obj, {e: 5})    console.log("obj", this.obj) }以上的代码等同于data() { return { obj: {} } },mounted: { this.obj = { a: 1, b: 2, c: 3, d: 4, e: 5 } }$set()方法不生效时,改用Object.assign()试试使用this.$set()去改变一个传入组件的对象(没有直接定义在data对象中,通过mixins选项做相关操作,能够通过this.去获取),没有触发视图更新而改用Object.assign()去给对象重新赋值时,会触发视图更新通过重新给对象赋值,来使视图更新;使用Object.assign()新增,或者改变原有对象,// 起作用的是给对象重新赋值this.obj = Object.assign({}, this.obj, {e: 5})以上就是“怎么给Vue对象增加响应式属性”的内容了,经过本文的学习后,相信大家对怎么给Vue对象增加响应式属性这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。原文链接:https://www.yisu.com/zixun/734104.html
  • [技术干货] 基于Java+SpringBoot+vue+element驾校管理系统设计和实现-转载
     一、前言介绍:         随着社会的发展和科学技术的进步,互联网技术越来越受欢迎。网络传播的生活方式逐渐受到广大人民群众的喜爱。越来越多的互联网爱好者开始在互联网上满足他们的基本需求,同时逐渐进入各个用户的生活起居。互联网具有许多优点,例如便利性,速度,高效率和低成本。因此,类似于驾校管理,满足用户工作繁忙的需求,不仅是方便用户随时查看信息的途径,而且还能提高管理效率。本文首先以驾校管理过程的基本问题作为研究对象。在开发系统之前,我们对现有状况进行了详细的调查和分析。最后,我们利用计算机技术开发了一套完整合适的驾校管理。该系统的实现主要优势是:该系统主要采用计算机技术开发,它方便快捷;系统可以通过管理员界面查看系统所涉及的驾校管理所有信息管理。          驾校管理软件是一款方便、快捷、实用的信息服务查询软件。随着智能网络在全球市场的不断普及以及各种智能平台的使用,作为中国主流智能的技术开发系统,自然需要这样的软件来满足更多用户的需求和体验。系统的开发与人们的日常需求相关,如通过管理系统获取到个人中心、学员管理、驾校教练管理、预约教练管理、预约教练管理、评价教练管理、考试成绩管理、考试通知管理、报考信息管理、练车通知管理、预约练车管理、驾考套餐管理、报名信息管理、车辆信息管理等信息详细情况,了解最新资讯信息等。  二、主要技术:  2.1  Spring Boot框架介绍:         Spring框架是Java平台上的一种开源应用框架,提供具有控制反转特性的容器。尽管Spring框架自身对编程模型没有限制,但其在Java应用中的频繁使用让它备受青睐,以至于后来让它作为EJB(EnterpriseJavaBeans)模型的补充,甚至是替补。Spring框架为开发提供了一系列的解决方案,比如利用控制反转的核心特性,并通过依赖注入实现控制反转来实现管理对象生命周期容器化,利用面向切面编程进行声明式的事务管理,整合多种持久化技术管理数据访问,提供大量优秀的Web框架方便开发等等。Spring框架具有控制反转(IOC)特性,IOC旨在方便项目维护和测试,它提供了一种通过Java的反射机制对Java对象进行统一的配置和管理的方法。Spring框架利用容器管理对象的生命周期,容器可以通过扫描XML文件或类上特定Java注解来配置对象,开发者可以通过依赖查找或依赖注入来获得对象。Spring框架具有面向切面编程(AOP)框架,SpringAOP框架基于代理模式,同时运行时可配置;AOP框架主要针对模块之间的交叉关注点进行模块化。Spring框架的AOP框架仅提供基本的AOP特性,虽无法与AspectJ框架相比,但通过与AspectJ的集成,也可以满足基本需求。Spring框架下的事务管理、远程访问等功能均可以通过使用SpringAOP技术实现。Spring的事务管理框架为Java平台带来了一种抽象机制,使本地和全局事务以及嵌套事务能够与保存点一起工作,并且几乎可以在Java平台的任何环境中工作。  2.2 MYSQL数据库:                 数据库是系统开发过程中不可或缺的一部分。 在WEB应用方面,MySQL AB开发了一个具有很大优势的MySQL关系数据库管理系统。 MySQL可以将数据存储在不同的表中,这非常灵活,并且还可以提高系统在实际应用中的速度。 数据库访问最常用于标准SQL语言,MySQL用于SQL语言,因此它具有高度兼容性。数据库的操作是必不可少的,包括对数据库表的增加、删除、修改、查询等功能。现如今,数据库可以分为关系型数据库和非关系型数据库,Mysql属于关系性数据库,Mysql数据库是一款小型的关系型数据库,它以其自身特点:体积小、速度快、成本低等,Mysql数据库是目前最受欢迎的开源数据库。          在WEB应用技术中, Mysql数据库支持不同的操作系统平台,虽然在不同平台下的安装和配置都不相同,但是差别也不是很大,Mysql在Windows平台下两种安装方式,二进制版和免安装版。安装完Mysql数据库之后,需要启动服务进程,相应的客户端就可以连接数据库,客户端可通过命令行或者图形界面工具登录数据库。  三、系统设计: 3.1 系统架构设计: ​  3.2 登录时序图设计: ​  四、功能截图:  4.1 登录注册:  4.2 前端页面: 4.2.1 系统首页:   4.2.2 教练模块:   4.2.3 考试须知:   4.2.4 预约练车:   4.2.5 车辆信息:  4.2.6 系统公告信息:  4.2.7 个人中心模块:   4.2.8 用户后台模块:  4.3 后端管理:  4.3.1 教练管理:  4.3.2 考试通知:  4.3.3 报名管理:  4.3.4 车辆信息:  4.3.5 系统公告等  五、代码实现:     /**  * 登录相关  */ @RequestMapping("users") @RestController public class UserController{          @Autowired     private UserService userService;          @Autowired     private TokenService tokenService;       /**      * 登录      */     @IgnoreAuth     @PostMapping(value = "/login")     public R login(String username, String password, String captcha, HttpServletRequest request) {         UserEntity user = userService.selectOne(new EntityWrapper<UserEntity>().eq("username", username));         if(user==null || !user.getPassword().equals(password)) {             return R.error("账号或密码不正确");         }         String token = tokenService.generateToken(user.getId(),username, "users", user.getRole());         return R.ok().put("token", token);     }          /**      * 注册      */     @IgnoreAuth     @PostMapping(value = "/register")     public R register(@RequestBody UserEntity user){ //        ValidatorUtils.validateEntity(user);         if(userService.selectOne(new EntityWrapper<UserEntity>().eq("username", user.getUsername())) !=null) {             return R.error("用户已存在");         }         userService.insert(user);         return R.ok();     }       /**      * 退出      */     @GetMapping(value = "logout")     public R logout(HttpServletRequest request) {         request.getSession().invalidate();         return R.ok("退出成功");     }          /**      * 密码重置      */     @IgnoreAuth     @RequestMapping(value = "/resetPass")     public R resetPass(String username, HttpServletRequest request){         UserEntity user = userService.selectOne(new EntityWrapper<UserEntity>().eq("username", username));         if(user==null) {             return R.error("账号不存在");         }         user.setPassword("123456");         userService.update(user,null);         return R.ok("密码已重置为:123456");     }          /**      * 列表      */     @RequestMapping("/page")     public R page(@RequestParam Map<String, Object> params,UserEntity user){         EntityWrapper<UserEntity> ew = new EntityWrapper<UserEntity>();         PageUtils page = userService.queryPage(params, MPUtil.sort(MPUtil.between(MPUtil.allLike(ew, user), params), params));         return R.ok().put("data", page);     }       /**      * 列表      */     @RequestMapping("/list")     public R list( UserEntity user){            EntityWrapper<UserEntity> ew = new EntityWrapper<UserEntity>();           ew.allEq(MPUtil.allEQMapPre( user, "user"));          return R.ok().put("data", userService.selectListView(ew));     }       /**      * 信息      */     @RequestMapping("/info/{id}")     public R info(@PathVariable("id") String id){         UserEntity user = userService.selectById(id);         return R.ok().put("data", user);     }          /**      * 获取用户的session用户信息      */     @RequestMapping("/session")     public R getCurrUser(HttpServletRequest request){         Long id = (Long)request.getSession().getAttribute("userId");         UserEntity user = userService.selectById(id);         return R.ok().put("data", user);     }       /**      * 保存      */     @PostMapping("/save")     public R save(@RequestBody UserEntity user){ //        ValidatorUtils.validateEntity(user);         if(userService.selectOne(new EntityWrapper<UserEntity>().eq("username", user.getUsername())) !=null) {             return R.error("用户已存在");         }         userService.insert(user);         return R.ok();     }       /**      * 修改      */     @RequestMapping("/update")     public R update(@RequestBody UserEntity user){ //        ValidatorUtils.validateEntity(user);         UserEntity u = userService.selectOne(new EntityWrapper<UserEntity>().eq("username", user.getUsername()));         if(u!=null && u.getId()!=user.getId() && u.getUsername().equals(user.getUsername())) {             return R.error("用户名已存在。");         }         userService.updateById(user);//全部更新         return R.ok();     }       /**      * 删除      */     @RequestMapping("/delete")     public R delete(@RequestBody Long[] ids){         userService.deleteBatchIds(Arrays.asList(ids));         return R.ok();     } }      @Configuration public class InterceptorConfig extends WebMvcConfigurationSupport{          @Bean     public AuthorizationInterceptor getAuthorizationInterceptor() {         return new AuthorizationInterceptor();     }          @Override     public void addInterceptors(InterceptorRegistry registry) {         registry.addInterceptor(getAuthorizationInterceptor()).addPathPatterns("/**").excludePathPatterns("/static/**");         super.addInterceptors(registry);     }          /**      * springboot 2.0配置WebMvcConfigurationSupport之后,会导致默认配置被覆盖,要访问静态资源需要重写addResourceHandlers方法      */     @Override     public void addResourceHandlers(ResourceHandlerRegistry registry) {         registry.addResourceHandler("/**")         .addResourceLocations("classpath:/resources/")         .addResourceLocations("classpath:/static/")         .addResourceLocations("classpath:/admin/")         .addResourceLocations("classpath:/front/")           .addResourceLocations("classpath:/public/");         registry.addResourceHandler("/upload/**").addResourceLocations("file:D:/work/");           super.addResourceHandlers(registry);     } }  ———————————————— 版权声明:本文为CSDN博主「java李杨勇」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/weixin_39709134/article/details/128781905 
总条数:276 到第
上滑加载中