7.组件框架

7.0.Element UI

js -> jquery ->jqueryui /easyUI / bootstrap UI

js -> vue -> elementUI

vue 2

https://element.eleme.cn/#/zh-CN

$ npm i element-ui -S
<!-- 引入样式 -->
<link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
<!-- 引入组件库 -->
<script src="https://unpkg.com/element-ui/lib/index.js"></script>

在 main.js 中写入以下内容:

import Vue from 'vue';import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';import App from './App.vue';Vue.use(ElementUI);new Vue({el: '#app',render: h => h(App)
});

7.1.分列

一行 24列

   <el-form-item label="标签" prop="id">    <el-col :span="6">...</el-col><el-col align="right"  :span="3"  >标签&nbsp;&nbsp;&nbsp;&nbsp;</el-col><el-col :span="6">...</el-col><el-col align="right"  :span="3"  >标签&nbsp;&nbsp;&nbsp;&nbsp;</el-col><el-col :span="6">...</el-col></el-form-item>

7.2.上传下载

7.2.1.1.上传组件

       <!--list-type:  文件列表的类型    string  text/picture/picture-card   action :  必选参数,上传的地址 string:accept :  接受上传的文件类型:limit : 最大允许上传个数:on-exceed : 文件超出个数限制时的钩子:on-preview : 点击文件列表中已上传的文件时的钩子:on-remove : 文件列表移除文件时的钩子:before-upload : 上传文件之前的钩子:file-list : 上传的文件列表 array:auto-upload : 是否在选取文件后立即进行上传:data: 上传时附带的额外参数:multiple : 是否支持多选文件:show-file-list: 是否显示已上传文件列表 boolean:on-success: 文件上传成功时的钩子:headers="Myhead" 设置上传头--><el-uploadlist-type="picture-card"ref="upload":action="uploadUrl":accept="acceptFileType":limit="1":data="{'type':'brand_logo'}":on-exceed="handleExceed":on-preview="handlePreview":on-remove="handleRemove":before-upload="beforeUpload":file-list="fileList":auto-upload="true":headers="Myhead" :show-file-list="showFileList":on-success="handleUploadSuccess"><i class="el-icon-plus"></i><div slot="tip" class="el-upload__tip">只能上传jpg/png文件,且不超过1MB</div></el-upload><el-dialog :visible.sync="dialogVisible"><img width="100%" :src="dialogImageUrl" alt="" /></el-dialog>

data中的对应属性

   imageUrl: "",uploadUrl:  "http://localhost:8900/renren-fast/localUpload/uploadForType",uploadTemplateDialog: false,fileList: [],        uploadLoading: false,acceptFileType: "image/png, image/jpeg", // 设置上传文件类型downLoadLoading: "",dialogImageUrl: "",dialogVisible: false,showFileList: true,

7.2.1.2. 对应钩子函数放到methods里

      // 文件超出个数限制时的钩子handleExceed(files, fileList) {this.$message.warning("只能选择1个文件!");},// 文件列表移除文件时的钩子handleRemove(file, fileList) {//console.log(file,fileList);},// 点击文件列表中已上传的文件时的钩子 ,  在 模态窗中显示handlePreview(file) {// console.log(file);console.log(file.url);this.dialogImageUrl = file.url;this.dialogVisible = true;},// 上传文件之前的钩子beforeUpload(file) {var that = this;// console.log("file:",file)//文件类型var fileName = file.name.substring(file.name.lastIndexOf(".") + 1);if ( !(fileName == "png" || fileName == "jpg"  || fileName == "jpeg") ) {that.uploadTemplateDialog = false;that.$message({type: "error",showClose: true,duration: 3000,message: "文件类型不是.png/.jpg文件!",});return false;}//读取文件大小var fileSize = file.size;// console.log(fileSize);if (fileSize > 1048576) {that.uploadTemplateDialog = false;that.$message({type: "error",showClose: true,duration: 3000,message: "文件大于1M!",});return false;}// 上传时 的 遮罩that.downloadLoading = that.$loading({lock: true,text: "数据上传中...",spinner: "el-icon-loading",background: "rgba(0,0,0,0.7)",});return true;},//文件上传成功时的钩子handleUploadSuccess(res, file) {// console.log(res)console.log("上传成功..." )// 用于在页面回显 图片try {this.imageUrl = file.raw;} catch (error) {this.imageUrl = window.URL.createObjectURL(file.raw);}// console.log("this.imageUrl ", this.imageUrl  )this.downloadLoading.close();this.uploadLoading = false;this.dataForm.brandLogoPath = res.paththis.showFileList = true;},

7.2.1.3. 上传头信息

写在 计算函数里

  computed: {Myhead: function () {return {token: this.$cookie.get('token')}}},

7.2.1.4. 回显

在 init 函数里 查看到 对应 的记录信息之后

   this.fileList.pop();this.fileList.push({name:  this.dataForm.brandLogoPath, url:  this.dataForm.brandLogoPath });

7.2.1.5.后台处理

常量 配置

public class Const {/*** 本地存储 文件所在的路径*/public static final String UPLOAD_LOCAL_PATH = "D:/upload/images/webshop/";/*** 本地存储 文件存储的路径*/public static final String UPLOAD_LOCAL_PATH_WEB_URL = "http://127.0.0.1:8900/renren-fast/upload/";
}

在对应的controller中

    @RequestMapping("/upload")@ResponseBodypublic R upload(@RequestParam("file") MultipartFile file) throws IOException {System.out.println("file => " + file.getOriginalFilename());if (file == null || file.isEmpty()) {System.out.println(" = " + "上传失败,请选择文件");return R.error("上传失败,请选择文件");} else {String path = FileUtils.saveFile(file);System.out.println("path = " + path);return R.ok().put("path", path);}}@RequestMapping("/uploadForType")@ResponseBodypublic R uploadForType(@RequestParam("type")String type, @RequestParam("file") MultipartFile file) throws IOException {System.out.println("type = " + type);System.out.println("file => " + file.getOriginalFilename());if (file == null || file.isEmpty()) {System.out.println(" = " + "上传失败,请选择文件");return R.error("上传失败,请选择文件");} else {String path = FileUtils.saveFileForType(file, type);System.out.println("path = " + path);return R.ok().put("path", path).put("type", type);}}

在 FileUtils 工具类中

    /*** @description 文件上传,保存在本地硬盘,返回文件请求路径* @param picFile* @return* @throws IOException*//**/public static String saveFile(MultipartFile picFile) throws IOException {String location = null;String destName = "";if(!picFile.isEmpty()){//得到真实路径String fileName = picFile.getOriginalFilename();String kzm = fileName.substring(fileName.lastIndexOf("."));//要保存的文件名destName = UUID.randomUUID().toString()+kzm;//取得上传文件存储路径//File directory = new File("");// 参数为空//String path = directory.getCanonicalPath()+"/src/main/webapp"+Const.PIC_FILE;String path = Const.UPLOAD_LOCAL_PATH;//如果上传文件存储路径不存在则创建一个File s2 = new File(path);if (s2.exists()==false) {s2.mkdirs();}//文件路径location = path+"/"+destName;try {picFile.transferTo(new File(location));} catch (IllegalStateException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}}String url = Const.UPLOAD_LOCAL_PATH_WEB_URL + destName;return url;}/*** @description 文件上传,保存在本地硬盘,返回文件请求路径* @param picFile* @return* @throws IOException*//**/public static String saveFileForType(MultipartFile picFile, String type) throws IOException {String location = null;String destName = "";if(!picFile.isEmpty()){//得到真实路径String fileName = picFile.getOriginalFilename();String kzm = fileName.substring(fileName.lastIndexOf("."));//要保存的文件名destName = UUID.randomUUID().toString()+kzm;//取得上传文件存储路径//File directory = new File("");// 参数为空//String path = directory.getCanonicalPath()+"/src/main/webapp"+Const.PIC_FILE;String path = Const.UPLOAD_LOCAL_PATH + type + "/";//如果上传文件存储路径不存在则创建一个File s2 = new File(path);if (s2.exists()==false) {s2.mkdirs();}//文件路径location = path+"/"+destName;try {picFile.transferTo(new File(location));} catch (IllegalStateException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}}String url = Const.UPLOAD_LOCAL_PATH_WEB_URL + type + "/" + destName;return url;}

7.2.2.图片显示

在后台 的项目中 要增加

其中 Const.UPLOAD_LOCAL_PATH 为 文件存储的本地路径

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {@Overridepublic void addResourceHandlers(ResourceHandlerRegistry registry) {registry.addResourceHandler("/upload/**").addResourceLocations("file:" + Const.UPLOAD_LOCAL_PATH);}
}

只要 实体类的属性值为 : http://127.0.0.1:8900/renren-fast/upload/brand_logo/3b73aaa7-ec21-4601-9432-ecabf07750d7.jpg

        <template slot-scope="scope" ><img :src="scope.row.brandLogoPath"  style="width: 150px; height: 60px" /></template>

在 renren-fast 项目中

路径 : io.renren.config.ShiroConfig

由于 没有传递 token 还要 关闭 shiro的权限控制 ShiroConfig 类

filterMap.put("/upload/**", "anon");

7.3.下拉框

7.3.0.vue-template

  <el-select v-model="value" placeholder="请选择"><el-optionv-for="item in options":key="item.value":label="item.label":value="item.value"></el-option></el-select>

7.3.1. 准备数据

// 在data 中增加用于 迭代的数组
data () {return {options:[],...
// 在 加载完成的钩子函数中 增加调用
created() {this.oneDataList();
},

// 在methods里增加 获取信息的方法
oneDataList() {console.log("请求后台");this.dataListLoading = true;this.$http({url: this.$http.adornUrl("/base/categoryone/listAll"),method: "get",}).then(({ data }) => {console.log(data)if (data && data.code === 0) {this.options = data.list;} else {this.options = [];}});
},

7.3.2. 下拉组件

因为要 同时 保存 一级分类 的 id 和 name 所以 option 的 :value 有变化 , 同时 要结合 @change 事件

 <el-select v-model="dataForm.coneName" placeholder="请选择一级分类" @change="checkedOption"  value-key="coneId" ><el-optionv-for="item in options":key="item.coneId":label="item.coneName +':' + item.coneId":value="item"></el-option>
</el-select>

7.3.3.封装数据

在methods 里 处理数据

// 改变 下拉框
checkedOption(item){this.dataForm.coneId = item.coneIdthis.dataForm.coneName = item.coneName
},

7.4.模态窗

通过 父子组件 + dialog 方式 完成

7.4.0.建立子组件

先在common下建立子组件 , 结构与 列表页相似

<template>
<!-- 模态窗-->
<el-dialog title="选择品牌"  width="80%":close-on-click-modal="false":visible.sync="visible"><!-- 显示内容 -->    <el-form :inline="true" :model="whereForm" @keyup.enter.native="getDataList()"><el-form-item><el-input    v-model="whereForm.key" placeholder="参数名" clearable></el-input></el-form-item><el-form-item><el-button @click="getDataList()">查询</el-button></el-form-item></el-form><el-table:data="dataList"borderv-loading="dataListLoading":highlight-current-row = "true"@current-change="handleCurrentChange"style="width: 100%;"><el-table-columnprop="brandId"header-align="center"align="center"label="品牌主键"></el-table-column><el-table-columnprop="brandCnName"header-align="center"align="center"label="中文名"></el-table-column><el-table-columnprop="brandEnName"header-align="center"align="center"label="英文名"></el-table-column><el-table-columnprop="brandLogoPath"header-align="center"align="center"  width="180"label="logo路径"><template slot-scope="scope" ><img :src="scope.row.brandLogoPath"  style="width: 80px; height: 30px" /></template></el-table-column><el-table-columnprop="brandWebUrl"header-align="center"align="center"width="220"label="网址"><template slot-scope="scope"><!-- <el-button type="text" size="small" >{{scope.row.brandWebUrl}}</el-button> --><a :href="scope.row.brandWebUrl"  target="_blank" >{{scope.row.brandWebUrl}}</a></template></el-table-column><el-table-columnprop="brandNum"header-align="center"align="center"width="80"label="权重"></el-table-column><el-table-columnprop="brandInfo"header-align="center"align="center"label="说明"></el-table-column><el-table-columnprop="brandFoundedDate"header-align="center"align="center":formatter="dateFormat"label="成立时间"></el-table-column></el-table><el-pagination@size-change="sizeChangeHandle"@current-change="currentChangeHandle":current-page="pageIndex":page-sizes="[10, 20, 50, 100]":page-size="pageSize":total="totalPage"layout="total, sizes, prev, pager, next, jumper"></el-pagination><!--操作按钮--><span slot="footer" class="dialog-footer"><el-button @click="visible = false">取 消</el-button><el-button type="primary" @click="selectedBrand()">确 定</el-button></span></el-dialog>
</template><script>export default {data () {return {currentRow: null,         visible: false, // 模态窗 显隐     whereForm: {key: ''},dataList: [],pageIndex: 1,pageSize: 10,totalPage: 0,dataListLoading: false,dataListSelections: [],addOrUpdateVisible: false}},methods: {/*** 选中 关闭模态窗*/selectedBrand() {// console.log(this.currentRow)this.visible = false;// 触发 父组件的事件this.$emit("selectedBrand", this.currentRow);},handleCurrentChange(val) {this.currentRow = val;},/*** 初始化方法 ,  由父组件调用*/init (id) {    this.visible = truethis.$nextTick(() => {this.getDataList();})},dateFormat(row,column){var dT=new Date(row.brandFoundedDate);//row 表示一行数据, dateTime 表示要格式化的字段名称return dT.getFullYear()+"-"+(dT.getMonth()+1)+"-"+dT.getDate();},// 获取数据列表getDataList () {this.dataListLoading = truethis.$http({url: this.$http.adornUrl('/goods/brandinfo/list'),method: 'get',params: this.$http.adornParams({'page': this.pageIndex,'limit': this.pageSize,'key':  this.whereForm.key})}).then(({data}) => {if (data && data.code === 0) {this.dataList = data.page.listthis.totalPage = data.page.totalCount} else {this.dataList = []this.totalPage = 0}this.dataListLoading = false})},// 每页数sizeChangeHandle (val) {this.pageSize = valthis.pageIndex = 1this.getDataList()},// 当前页currentChangeHandle (val) {this.pageIndex = valthis.getDataList()},// 多选selectionChangeHandle (val) {this.dataListSelections = val},}}
</script>

7.4.1.引入组件

先引入这个el-dialog组件

 import BrandDialog from "../common/brand-dialog";

根据具体的路径完成一个组件页面的引入,并且取名叫做BrandDialog ;

然后 声明组件的使用

 components: { BrandDialog },

根据规则定义一个组件,按照驼峰的形式BrandDialog 转换成组件的名字,

在template标签里面添加一个组件

特别注意: 子组件标签 与 原来模版内容 要并列在 template 结点下,

但template 必须有唯一根结点

<brand-dialogv-if="brandDialogVisible"ref="brandDialog" ></brand-dialog>

通过 brandDialogVisible 值来 控制 显隐, 在data中 声明并赋初始值为false

  // 品牌模态窗 显隐标识brandDialogVisible: false,

7.4.2.触发组件显示

增加元素, 触发的方法,

    <el-form-item label="选择品牌" prop="brdId"><el-col :span="12"><el-input v-model="dataForm.brandCnName" placeholder="选择品牌" :disabled="true" ></el-input></el-col><el-col :span="6"  ><el-button type="success" round @click="openBrandDialog" >选择品牌</el-button ></el-col></el-form-item>

添加对应的函数

      /*** 打开 模态窗*/openBrandDialog(){// 设置模态窗 显示this.brandDialogVisible = true;this.$nextTick(() => {// 调用 模态窗的初始化方法  this.$refs.brandDialog.init();});},

this.$nextTick()这是Vue生命周期中的钩子函数的其中之一,在显示的时候执行参数对应的函数,

然后就是this.$refs.brandDialog.init();使用this.$refs.组件名.组件方法(参数),

组件名称是在设定的时候通过ref="brandDialog"设定组件的名称变成refs的直接使用。

7.4.3.子组件数据

然后我们在组件里面,组件中的主体是在

<el-dialog title="选择品牌"  width="80%":close-on-click-modal="false":visible.sync="visible">... <!--数据显示 --><span slot="footer" class="dialog-footer"><el-button @click="visible = false">取 消</el-button><el-button type="primary" @click="selectedBrand()">确 定</el-button></span></el-dialog>

里面的,通过 :visible.sync="visible" 绑定一个boolean值来完成对于dialog的显示,

然后title完成对于弹窗的题目的设定,

然后展示init方法

    /*** 初始化方法 ,  由父组件调用*/init (id) {    this.visible = truethis.$nextTick(() => {//调用查询数据的方法this.getDataList();})},

所有的 v-if 想要产生效果都需要设定默认值才能够使用
在这里具体逻辑就是this.visible = true;显示dialog,

然后就是 this.getDataList(); 查询列表数据

7.4.4.点击选中

在 data 声明用于记录选中信息

  currentRow: null,

在 el-table 中增加 highlight-current-row 是否要高亮当前选中行

@current-change=“handleCurrentChange” 当表格的当前行发生变化的时候会触发该事件

 <el-table:data="dataList"borderv-loading="dataListLoading":highlight-current-row = "true"@current-change="handleCurrentChange"style="width: 100%;">

对就的处理函数

    handleCurrentChange(val) {// 将选中的行 赋值给  currentRow,  要先在 data中声明  this.currentRow = val;},

7.4.5.回传信息

点击 确定按钮 执行函数

    /*** 选中 关闭模态窗*/selectedBrand() {// console.log(this.currentRow)// 隐藏 模态窗this.visible = false;// 触发 父组件的事件, 将选中行信息传回this.$emit("selectedBrand", this.currentRow);},

7.4.6.父组件接值

在 组件引用里 加入

    <brand-dialogv-if="brandDialogVisible"ref="brandDialog"@selectedBrand="selectedBrand"  ></brand-dialog>

@selectedBrand="selectedBrand"

等号 左边 与 子组件调用的方法相对应

等号 右边 是对应 父组件的函数

对应的函数, 接收到子组件传来信息, 并对dataForm 进行赋值

  selectedBrand(brandData){console.log(brandData)this.dataForm.brdId = brandData.brandId;this.dataForm.brandCnName = brandData.brandCnName;},

7.5.树

7.5.1.vue页面

<template><div><el-switch v-model="draggable" active-text="开启拖拽" inactive-text="关闭拖拽"></el-switch><el-button v-if="draggable" @click="batchSave" >批量保存</el-button><el-button type="danger"    @click="batchDelete" >批量删除</el-button><el-tree :data="data" :props="defaultProps" @node-click="handleNodeClick" :expand-on-click-node="false"show-checkbox     node-key="areaId":default-expanded-keys="expandedKey":draggable="draggable":allow-drop="allowDrop"@node-drop="handleDrop"ref="dataTree"  ><span class="custom-tree-node" slot-scope="{ node, data }"><span>{{ node.label }}</span><span><el-button  v-if="node.level <=4"type="text"size="mini" @click="() => append(data)">添加</el-button><el-button type="text" size="mini" @click="edit(data)">修改</el-button><el-button v-if="node.childNodes.length==0"type="text"size="mini"@click="() => remove(node, data)">删除</el-button></span></span></el-tree><!-- 添加 对话窗 --><el-dialog:title="title":visible.sync="dialogVisible"width="30%":close-on-click-modal="false"><el-form :model="basearea"><el-form-item label="地区名称" ><el-input v-model="basearea.areaName" autocomplete="off"></el-input></el-form-item>    <el-form-item label="地区代号" ><el-input v-model="basearea.areaCode" autocomplete="off"></el-input></el-form-item>    </el-form><span slot="footer" class="dialog-footer"><el-button @click="dialogVisible = false">取 消</el-button><el-button type="primary" @click="submitData">确 定</el-button></span></el-dialog></div>
</template><script>export default {activated () {this.getDataList()},data() {return {basearea: {areaId: 0,areaParId: '',areaName: '',areaCode: '',areaLevel: '',areaParName: '',areaSort: ''},dialogType:"", // 对话窗类型 add/editdialogVisible: false, // 对话窗 显隐title: "",     // 对话窗 标题maxLevel: 0, // 最大深度draggable: false, // 是否可以拖拽updateNodes: [], // 拖拽后要更新的结点集合pCid: [],  //  父结点data: [],   // 树形 显示数据expandedKey: [],  // 展开结点IDdefaultProps: {children: 'children',label: 'areaName'}};},methods: {/***点击事件*/handleNodeClick(data) {console.log(data);},/*** 得到 数据*/getDataList(){this.dataListLoading = truethis.$http({url: this.$http.adornUrl('/base/basearea/list/tree'),method: 'get'}).then(({data}) => {console.log("数据加载成功!..." , data)this.data=data.data;this.dataListLoading = false})},/*** 批量删除*/batchDelete() {let areaIds = [];let checkedNodes = this.$refs.dataTree.getCheckedNodes();console.log("被选中的元素", checkedNodes);for (let i = 0; i < checkedNodes.length; i++) {console.log(checkedNodes[i]);// 没有子结点if(checkedNodes[i].children==null){areaIds.push(checkedNodes[i].areaId);}}this.$confirm(`是否批量删除【${areaIds}】分类?`, "提示", {confirmButtonText: "确定",cancelButtonText: "取消",type: "warning"}).then(() => {this.$http({url: this.$http.adornUrl("/base/basearea/delete"),method: "post",data: this.$http.adornData(areaIds, false)}).then(({ data }) => {this.$message({message: "信息批量删除成功",type: "success"});this.getDataList();//设置需要默认展开的菜单//  this.expandedKey = [this.basearea.areaParId];});}).catch(() => {});},/** * 拖拽成功完成时触发的事件函数* * 拖拽方法 四个参数* 被拖拽节点对应的 Node、* 结束拖拽时最后进入的节点、* 被拖拽节点的放置位置(before、after、inner)、* event*/handleDrop(draggingNode, dropNode, dropType, ev) {// console.log("handleDrop: ", draggingNode, dropNode, dropType);//1、当前节点最新的父节点idlet pCid = 0;let siblings = null;  // 兄弟结点集合if (dropType == "before" || dropType == "after") {pCid =dropNode.parent.data.areaId == undefined? 0: dropNode.parent.data.areaId;siblings = dropNode.parent.childNodes;} else {pCid = dropNode.data.areaId;siblings = dropNode.childNodes;}this.pCid.push(pCid);console.log(pCid, siblings)//2、当前拖拽节点的最新顺序,for (let i = 0; i < siblings.length; i++) {let parName = siblings[i].parent.data.areaName// 如果是拖动的结点if (siblings[i].data.areaId == draggingNode.data.areaId) {//如果遍历的是当前正在拖拽的节点let catLevel = draggingNode.level;if (siblings[i].level != draggingNode.level) {//当前节点的层级发生变化catLevel = siblings[i].level;//修改他子节点的层级this.updateChildNodeLevel(siblings[i]);}this.updateNodes.push({areaId: siblings[i].data.areaId,areaSort: i,areaParId: pCid,areaLevel: catLevel-1,areaParName: parName});} else {this.updateNodes.push({ areaId: siblings[i].data.areaId, areaSort: i });}}//3、当前拖拽节点的最新层级console.log("updateNodes", this.updateNodes);},/*** 更新被拖拽结点子结点信息*/updateChildNodeLevel(node) {console.log("updateChildNodeLevel:", node)if (node.childNodes.length > 0) {for (let i = 0; i < node.childNodes.length; i++) {var cNode = node.childNodes[i].data;this.updateNodes.push({areaId: cNode.areaId,areaLevel: node.childNodes[i].level-1});this.updateChildNodeLevel(node.childNodes[i]);}}},/*** 批量保存*/batchSave() {console.log("批量保存", this.updateNodes )this.$http({url: this.$http.adornUrl("/base/basearea/update/sort"),method: "post",data: this.$http.adornData(this.updateNodes, false)}).then(({ data }) => {this.$message({message: "地理信息顺序等修改成功",type: "success"});//刷新出新的菜单this.getDataList();//设置需要默认展开的菜单this.expandedKey = this.pCid;this.updateNodes = [];this.maxLevel = 0;// this.pCid = 0;});},/*** 拖拽时判定目标节点能否被放置*/allowDrop(draggingNode, dropNode, type) {//1、被拖动的当前节点以及所在的父节点总层数不能大于3//1)、被拖动的当前节点总层数console.log("allowDrop:", draggingNode, dropNode, type);//this.countNodeLevel(draggingNode);console.log(this.maxLevel ,"><", draggingNode.level)//当前正在拖动的节点+父节点所在的深度不大于3即可let deep = Math.abs(this.maxLevel - draggingNode.level) + 1;console.log("深度:", deep);//   this.maxLevelif (type == "inner") {// console.log(//   `this.maxLevel:${this.maxLevel};draggingNode.data.catLevel:${draggingNode.data.catLevel};dropNode.level:${dropNode.level}`// );return deep + dropNode.level <= 5;} else {return deep + dropNode.parent.level <= 5;}},/*** 找到所有子节点,求出最大深度*/countNodeLevel(node) {//判断是有子结点if (node.childNodes != null && node.childNodes.length > 0) {for (let i = 0; i < node.childNodes.length; i++) {if (node.childNodes[i].level > this.maxLevel) {this.maxLevel = node.childNodes[i].level;}// 递归 调用this.countNodeLevel(node.childNodes[i]);}}else{this.maxLevel=node.level}},/*** 点击修改按钮*/edit(data) {console.log("要修改的数据", data);this.dialogType = "edit";this.title = "修改地理信息";this.dialogVisible = true;//发送请求获取当前节点最新的数据this.$http({url: this.$http.adornUrl(`/base/basearea/info/${data.areaId}`),method: "get"}).then(({ data }) => {//请求成功console.log("要回显的数据", data);this.basearea.areaParId = data.baseArea.areaParId;this.basearea.areaLevel = data.baseArea.areaLevel;this.basearea.areaId = data.baseArea.areaId;this.basearea.areaName = data.baseArea.areaName;this.basearea.areaSort = data.baseArea.areaSort;this.basearea.areaCode = data.baseArea.areaCode;this.basearea.areaParName = data.baseArea.areaParName;});},/*** 点击添加按钮*/append(data) {this.dialogType = "add";this.title = "添加地理信息";// this.basearea.areaName = "";// 打开对话窗this.dialogVisible=true;// 初始化数据this.basearea.areaParId = data.areaId;this.basearea.areaLevel = data.areaLevel * 1 + 1;this.basearea.areaId = null;this.basearea.areaName = "";this.basearea.areaSort = 0;this.basearea.areaCode = "";this.basearea.areaParName = data.areaName;},/*** 点击 对话窗 确定按钮*/submitData(){if (this.dialogType == "add") {this.addCategory();}if (this.dialogType == "edit") {this.editCategory();}},/*** 修改数据*/editCategory(){var { areaId, areaName, areaCode} = this.basearea;this.$http({url: this.$http.adornUrl("/base/basearea/update"),method: "post",data: this.$http.adornData( {  areaId, areaName, areaCode}, false)}).then(({ data }) => {this.$message({message: "分类信息修改成功",type: "success"});//关闭对话框this.dialogVisible = false;//刷新出新的菜单this.getDataList();//设置需要默认展开的菜单this.expandedKey = [this.basearea.areaParId];});},/** *  添加 分类信息*/addCategory(){console.log("data", this.basearea)this.$http({url: this.$http.adornUrl("/base/basearea/save"),method: "post",data: this.$http.adornData(this.basearea, false)}).then(({ data }) => {this.$message({message: "地理信息保存成功",type: "success"});//关闭对话框this.dialogVisible = false;//刷新出新的菜单this.getDataList();//设置需要默认展开的菜单this.expandedKey = [this.basearea.areaParId];});},/*** 移除 结点*/remove(node, data) {var ids = [data.areaId]console.log(ids);this.$confirm(`确定对[${data.areaName}]删除操作?`, '提示', {confirmButtonText: '确定',cancelButtonText: '取消',type: 'warning'}).then(() => {this.$http({url: this.$http.adornUrl('/base/basearea/delete'),method: 'post',data: this.$http.adornData(ids, false)}).then(({data}) => {if (data && data.code === 0) {this.$message({message: '操作成功',type: 'success',duration: 1500,onClose: () => {this.getDataList()}})// 为默认打开的结点(也就是当前删除结点的父结点)赋值this.expandedKey = [node.parent.data.areaId];} else {this.$message({message: data.msg,type: 'error',duration: 1500,onClose: () => {this.getDataList()}})// 为默认打开的结点(也就是当前删除结点的父结点)赋值this.expandedKey = [node.parent.data.areaId];}console.log("删除成功!")})}).catch(()=>{ console.log("取消删除!")});},}}
</script>

7.5.2.后台准备数据

后台 实体类 增加 子结点集合属性

 @TableField(exist = false)private List<BaseAreaEntity> children;

后台controller 中 准备显示 树型数据

    /*** 查询出所有的分类及子类, 以数形结构组装越来*/@RequestMapping("/list/tree")public R listWithTree(){List<BaseAreaEntity> baseAreaTrees = baseAreaService.listWithTree();return R.ok().put("data", baseAreaTrees);}

serviceImpl中

    @Overridepublic List<BaseAreaEntity> listWithTree() {//1、查出所有分类List<BaseAreaEntity> allList = baseMapper.selectList(null);//2、组装成父子的树形结构//2.1)、找到所有的一级分类List<BaseAreaEntity> baseAreas = allList.stream().filter(baseAreaEntity ->baseAreaEntity.getAreaParId()==null).peek((baseAreaEntity)->{baseAreaEntity.setChildren(getChildrens(baseAreaEntity, allList));}).sorted((b1,b2)->{return (b1.getAreaSort()==null?0:b1.getAreaSort()) - (b2.getAreaSort()==null?0:b2.getAreaSort());}).collect(Collectors.toList());return baseAreas;}//递归查找所有菜单的子菜单private List<BaseAreaEntity> getChildrens(BaseAreaEntity root, List<BaseAreaEntity> all){return all.stream().filter(baseAreaEntity -> root.getAreaId().equals(baseAreaEntity.getAreaParId())).peek(baseAreaEntity -> {//1、找到子菜单List<BaseAreaEntity> childrens = getChildrens(baseAreaEntity, all);if (childrens != null && childrens.size()>0) {baseAreaEntity.setChildren(childrens);}}).sorted((b1,b2)->{return (b1.getAreaSort()==null?0:b1.getAreaSort()) - (b2.getAreaSort()==null?0:b2.getAreaSort());}).collect(Collectors.toList());}

7.5.3.批量保存修改结点数据

controller中的方法

    @RequestMapping("/update/sort")public R updateSort(@RequestBody BaseAreaEntity[] baseAreaEntities){System.out.println("Arrays.toString(baseAreaEntities) = " + Arrays.toString(baseAreaEntities));baseAreaService.updateBatchById(Arrays.asList(baseAreaEntities));return R.ok();}

7.6.联动

7.6.1.后台准备树形数据

@Overridepublic List<Map<String, Object>> listCascader() {List<Map<String, Object>> list = categoryOneDao.selectList(null).stream().map(categoryOneEntity -> {Map<String, Object> oneMap = new HashMap<>();oneMap.put("categoryId", categoryOneEntity.getConeId());oneMap.put("categoryName", categoryOneEntity.getConeName());List<CategoryTwoEntity> categoryTwoEntities = categoryTwoDao.selectList(new LambdaQueryWrapper<CategoryTwoEntity>().eq(CategoryTwoEntity::getConeId, categoryOneEntity.getConeId()));if (categoryTwoEntities != null && categoryTwoEntities.size()>0) {List<Map<String, Object>> twoList = categoryTwoEntities.stream().map(categoryTwoEntity -> {Map<String, Object> twoMap = new HashMap<>();twoMap.put("categoryId", categoryTwoEntity.getCtwoId());twoMap.put("categoryName", categoryTwoEntity.getCtwoName());List<CategoryThreeEntity> categoryThreeEntitys = this.list(new LambdaQueryWrapper<CategoryThreeEntity>().eq(CategoryThreeEntity::getCtwoId, categoryTwoEntity.getCtwoId()));if (categoryThreeEntitys != null && categoryThreeEntitys.size() > 0) {List<Map<String, Object>> thrList = categoryThreeEntitys.stream().map(categoryThreeEntity -> {Map<String, Object> thrMap = new HashMap<>();thrMap.put("categoryId", categoryThreeEntity.getCthrId());thrMap.put("categoryName", categoryThreeEntity.getCthrName());return thrMap;}).collect(Collectors.toList());twoMap.put("children", thrList);}return twoMap;}).collect(Collectors.toList());oneMap.put("children", twoList);}return oneMap;}).collect(Collectors.toList());return list;}

其中 categoryId 为 id , categoryName 为 name , children 为子结点

7.6.2.页面组件

 <el-cascader  style="width:280px;"filterableclearable placeholder="试试搜索:手机":props="cascaderProps"change-on-select:options="categoryOptions"        @change="handleChange"ref="cascader"v-model="categoryPath" >
</el-cascader>

data 增加

        categoryOptions: [],cascaderProps:{value:"categoryId",label:"categoryName",children:"children"},categoryPath:[],

methods

 handleChange(value) {console.log(value);   // 在高版本中才有这样的方法console.log(this.$refs["cascader"].getCheckedNodes()[0].label)console.log(this.$refs["cascader"].getCheckedNodes()[0].pathLabels) //对应的label数组},

低版本可以在

      for(let i=0; i<this.categoryOptions.length; i++ ){console.log( this.categoryOptions[i].categoryId )if(this.categoryOptions[i].categoryId == this.categoryPath[0]){this.dataForm.coneId = this.categoryOptions[i].categoryIdthis.dataForm.coneName = this.categoryOptions[i].categoryNamefor(let l=0; l<this.categoryOptions[i].children.length; l++){if(this.categoryOptions[i].children[l].categoryId == this.categoryPath[1]){this.dataForm.ctwoId = this.categoryOptions[i].children[l].categoryIdthis.dataForm.ctwoName = this.categoryOptions[i].children[l].categoryNamefor(let k=0; k<this.categoryOptions[i].children[l].children.length; k++){if(this.categoryOptions[i].children[l].children[k].categoryId == this.categoryPath[2]){this.dataForm.cthrId = this.categoryOptions[i].children[l].children[k].categoryIdthis.dataForm.cthrName = this.categoryOptions[i].children[l].children[k].categoryName}}}}}}console.log("data:", this.dataForm)

7.7.时间,日历

7.7.1.时间信息

Date.now() 取当前时间

原始格式 : 2018-10-30T10:06:20.000Z

new Date(this.xxDate).getTime() 转化为时间戳

7.7.2.按格式 显示 时间

  <el-table-columnprop="supplierFoundedDate"header-align="center"align="center":formatter="dateFormat"label="成立时间"></el-table-column>

对应 函数

     dateFormat(row,column){    var dT = new Date(row.supplierFoundedDate);//row 表示一行数据, supplierFoundedDate 表示要格式化的字段名称return dT.getFullYear()+"-"+(dT.getMonth()+1)+"-"+dT.getDate();},

7.7.3.日历插件

        <el-date-pickerv-model="dataForm.supplierFoundedDate"type="date"placeholder="选择成立时间"></el-date-picker>

可能是时区等原因, 返回原始格式 : 2018-10-30T10:06:20.000Z, 传递后台 不能成功转换成java.util.Date() 会报错
可以通过 new Date(this.dataForm.supplierFoundedDate).getTime() 转化为时间戳 来 解决

7.8.表格扩展

在 列表 页面的 el-table 中加入

      <el-table-column fixed type="expand"><template slot-scope="props"><el-form label-position="left" inline class="demo-table-expand"><el-form-item label="银行名称:"><span>{{ props.row.bankName }}</span></el-form-item><el-form-item label="银行账号:"><span>{{ props.row.bankSn }}</span></el-form-item><el-form-item label="地址:" style="width: 100%" ><span>{{ props.row.supplierAddress }}</span></el-form-item><el-form-item label="描述:" style="width: 100%" ><span>{{ props.row.supplierInfo }}</span></el-form-item></el-form></template></el-table-column>

7.9.富文本

7.9.1.tinymce

下载 地址 : https://www.tiny.cloud/get-tiny/self-hosted/

语言包 地址: https://www.tiny.cloud/get-tiny/language-packages/

# vue2.0版本应该使用
npm install --save "@tinymce/tinymce-vue@^3"# vue3.0版本应该使用
npm install --save "@tinymce/tinymce-vue@^4"# 再运行
npm install tinymce -S

安装之后,在 node_modules 中找到 tinymce/skins 目录,然后将 skins 目录拷贝到 static 目录下

结构 如:
static

​ tinymce

​ skins

​ zh_CN.js

在页面中引入以下文件

import tinymce from 'tinymce/tinymce'
import 'tinymce/themes/silver/theme'
import Editor from '@tinymce/tinymce-vue'import 'tinymce/icons/default/icons';
import 'tinymce/plugins/image';
import 'tinymce/plugins/media';// 插入视频插件
import 'tinymce/plugins/table';// 插入表格插件
import 'tinymce/plugins/lists';// 列表插件
import 'tinymce/plugins/wordcount';// 字数统计插件

tinymce-vue 是一个组件,需要在 components 中注册,

 components: { Editor },

然后直接使用

<Editor id="tinymce" v-model="tinymceHtml" :init="editorInit"></Editor>

编辑器需要一个 skin 才能正常工作,所以要设置一个 skin_url 指向之前复制出来的 skin 文件

在data 中配置初始化属性

editorInit: {language_url: '/static/tinymce/zh_CN.js',language: 'zh_CN',skin_url: '/static/tinymce/skins/ui/oxide', // skin路径height: 300, // 编辑器高度branding: false, // 是否禁用“Powered by TinyMCE”menubar: true, // 顶部菜单栏显示plugins: this.plugins,toolbar: this.toolbar,
}

同时在 mounted 中也需要初始化一次:

mounted (){tinymce.init({});
},

如果在这里传入上面的 init 对象,并不能生效,但什么参数都不传也会报错,所以这里传入一个空对象

https://blog.csdn.net/zjiang1994/article/details/79880806

https://blog.csdn.net/zjiang1994/article/details/79856058

完成了上面的初始化之后,就已经能正常运行编辑器了,但只有一些基本功能

tinymce 通过添加插件 plugins 的方式来添加功能

比如要添加一个上传图片的功能,就需要用到 image 插件,添加超链接需要用到 link 插件

<template><div class='tinymce'><h1>tinymce</h1><editor id='tinymce' v-model='tinymceHtml' :init='init'></editor><div v-html='tinymceHtml'></div></div>
</template><script>
import tinymce from 'tinymce/tinymce'
import 'tinymce/themes/modern/theme'
import Editor from '@tinymce/tinymce-vue'
import 'tinymce/plugins/image'
import 'tinymce/plugins/link'
import 'tinymce/plugins/code'
import 'tinymce/plugins/table'
import 'tinymce/plugins/lists'
import 'tinymce/plugins/contextmenu'
import 'tinymce/plugins/wordcount'
import 'tinymce/plugins/colorpicker'
import 'tinymce/plugins/textcolor'export default {name: 'tinymce',data () {return {tinymceHtml: '请输入内容',init: {language_url: '/static/tinymce/zh_CN.js',language: 'zh_CN',skin_url: '/static/tinymce/skins/ui/oxide', // skin路径content_css:'/static/tinymce/skins/content/default/content.css',height: 300, // 编辑器高度branding: false, // 是否禁用“Powered by TinyMCE”menubar: true, // 顶部菜单栏显示font_formats: '微软雅黑=Microsoft YaHei,Helvetica Neue,PingFang SC,sans-serif;苹果苹方=PingFang SC,Microsoft YaHei,sans-serif;宋体=simsun,serif',fontsize_formats:'12px 14px 16px 18px 20px 22px 24px 26px 28px 30px 32px 34px 36px 38px 40px 50px 60px 70px 80px 90px 100px 120px 140px 160px 180px 200px',plugins: 'link lists image code table colorpicker textcolor wordcount contextmenu',toolbar:'bold italic underline strikethrough | fontsizeselect | forecolor backcolor | alignleft aligncenter alignright alignjustify | bullist numlist | outdent indent blockquote | undo redo | link unlink image code | removeformat',}}},mounted () {tinymce.init({})},components: {Editor}
}
</script>

但是当富文本在某一个弹窗上使用时,

工具栏会出现下拉选择时的层级比弹窗的小,所以,选项会被弹窗遮挡。

而解决这个问题,需要把工具栏的下拉框的层级提高,

找到static/tinymce/skins/ui/oxide/skin.min.css文件

把class名为tox-tinymce-aux的第一个的z-index属性变大即可

tinymce 在初始化的属性对象中( init对象中 )提供了 images_upload_url 等 api 让用户配置上传图片的相关参数

但为了在不麻烦后端的前提下适配自家的项目,还是得用 images_upload_handler 来自定义一个上传方法

     images_upload_handler: (blobInfo, success, failure) => {// const img = 'data:image/jpeg;base64,' + blobInfo.base64();// success(img);this.handleImgUpload(blobInfo, success, failure);}

这个方法会提供三个参数:blobInfo, success, failure

其中 blobinfo 是一个对象,包含上传文件的信息

successfailure 是函数,上传成功的时候向 success 传入一个图片地址,失败的时候向 failure 传入报错信息

handleImgUpload (blobInfo, success, failure) {// 上传的文件信息let fd = new FormData();fd.append("file", blobInfo.blob());fd.append("type","tinymec")this.$http({url: this.$http.adornUrl("/localUpload/uploadForType" ),method: "post",data: fd,headers: {"Content-Type":"multipart/form-data;boundary=" + new Date().getTime(),"token": this.$cookie.get('token')},}).then(({ data }) => {if (data && data.code === 0) {this.$message({message: data.path + ",操作成功",type: "success",duration: 1500,onClose: () => {success(data.path)},});} else {failure('error')}});
}

7.10.Switch 开关

页面元素

 <el-table-columnprop="goodsImgsState"header-align="center"align="center"label="当前状态 : 0 不可用 1 使用"><template slot-scope="scope"><el-switchv-model="scope.row.goodsImgsState"active-color="#13ce66"inactive-color="#ff4949":active-value="'1'":inactive-value="'0'"@change="updateType(scope.row)"></el-switch></template></el-table-column>

对应 函数

    updateType(data){console.log(data);console.log("最新信息", data);// es6  var letlet { goodsImgsId, goodsImgsState } = data;//发送请求修改状态this.$http({url: this.$http.adornUrl("/goods/goodsimgs/update/state"),method: "post",data: this.$http.adornData({ goodsImgsId, goodsImgsState  }, false),}).then(({ data }) => {this.$message({type: "success",message: "状态更新成功",});this.getDataList();});},

7.11. tag

三元运算

  <el-table-columnprop="supplierState"header-align="center"align="center"label="状态"><template slot-scope="scope"><el-tag  :type="scope.row.supplierState !='1'?'danger':''" size="medium">{{ scope.row.supplierState !='1'?'关停':'正常' }}</el-tag></template></el-table-column>

v-if

   <el-table-columnprop="supplierIsJoin"header-align="center"align="center"label="是否入住"><template slot-scope="scope"><div v-if="scope.row.supplierIsJoin === '0'"><el-tag type="info"  >未入住</el-tag></div><div v-if="scope.row.supplierIsJoin === '1'"><el-tag type="success"  >入住</el-tag></div><div v-else-if="scope.row.supplierIsJoin === '2'"><el-tag    >协商中...</el-tag></div></template></el-table-column>

7.12.页面校验

在 data 的 dataRule 中 设置规则

dataRule: {adminNickname: [{required: true,message: "昵称不能为空",trigger: "blur",},],adminAccount: [{validator: (rule, value, callback) => {if (value === '') {callback(new Error('请输入管理员账号'));} else  {console.log("this.oldAccount==value", this.oldAccount==value)if(this.oldAccount==value){callback();}else{this.$http({url: this.$http.adornUrl(`/admin/admininfo/checkSingleLogin/${value}`),method: 'get',params: this.$http.adornParams(),}).then(({data}) => {if (data && data.code === 0) {callback();} else {callback(new Error('管理员账号已被使用'));}})}}},trigger: "blur",},],adminPassword: [{validator: (rule, value, callback) => {if (value === '') {callback(new Error('请输入管理员密码'));} else {if (this.dataForm.checkPass !== '') {this.$refs.dataForm.validateField('checkPass');}callback();}},trigger: "blur",},],checkPass: [{validator: (rule, value, callback) => {if (value === '') {callback(new Error('请输入确认密码'));} else if (value !== this.dataForm.adminPassword) {callback(new Error('两次输入密码不一致!'));} else {callback();}},trigger: "blur",},],adminPhone: [{validator: (rule, value, callback)=>{if (value === '') {callback(new Error('管理员电话不能为空'));} else if (!/^1[3-9]\d{9}$/.test(value)) {callback(new Error('请输入正确的手机号码'));} else {callback();}}, trigger: "blur",}],adminInfo: [{required: true,message: "管理员说明 不能为空",trigger: "blur",},{ max: 5, message: '长度要少于 5 个字符', trigger: 'blur' }],}

7.13.Steps 步骤条

<template>
<div><el-dialogtitle="维护一级分类关联的品牌":close-on-click-modal="false":visible.sync="visible"><el-col :span="18"><el-alert  style="box-shadow: 0 2px 4px rgba(0, 0, 0, .12), 0 0 6px rgba(0, 0, 0, .04)"effect="dark":title="dataForm.coneName"type="info":closable="false":description="dataForm.coneInfo"show-icon ></el-alert></el-col><el-col :span="6"  align="center" valign="center" ><el-button  type="primary"  round @click="openBrandDialog"  >选择品牌</el-button></el-col><el-table:data="tableData"borderstyle="width: 100%"><el-table-columnprop="brandId"label="品牌主键"width="180"></el-table-column><el-table-columnprop="brandCnName"label="品牌名称"width="180"></el-table-column><el-table-columnprop="coneBrdNum"label="序号"></el-table-column> <el-table-columnprop="coneBrdNum"label="序号"   width="200" ><template slot-scope="scope"><el-input-number :min="1" :step="1" v-model="scope.row.coneBrdNum" size="mini" placeholder="分类序号"></el-input-number></template></el-table-column> <el-table-columnfixed="right"header-align="center"align="center"label="操作"><template slot-scope="scope"><el-button type="text" size="small"@click.native.prevent="deleteRow(scope.$index, tableData)">取消</el-button></template></el-table-column></el-table><span slot="footer" class="dialog-footer"><el-button @click="visible = false">取消</el-button><el-button type="primary" @click="dataFormSubmit()">确定</el-button></span></el-dialog><brand-dialogv-if="brandDialogVisible"ref="brandDialog"@selectedBrand="selectedBrand"  ></brand-dialog></div>
</template><script>
import BrandDialog from "../common/brand-many-dialog";export default {components: { BrandDialog }, data () {return {// 品牌模态窗 显隐标识brandDialogVisible: false,tableData: [],visible: false,dataForm: {coneBrdId: 0,coneId: '',brandId: '',coneBrdNum: '',coneName: '',brandCnName: '',coneInfo:''},dataRule: {coneId: [{ required: true, message: '分类 : 一级分类 category_one 外键不能为空', trigger: 'blur' }],brandId: [{ required: true, message: '品牌 :品牌 brand_info 外键不能为空', trigger: 'blur' }],coneBrdNum: [{ required: true, message: '序号不能为空', trigger: 'blur' }],coneName: [{ required: true, message: '分类名称不能为空', trigger: 'blur' }],brandCnName: [{ required: true, message: '中文名不能为空', trigger: 'blur' }]}}},methods: {selectedBrand(brandData){console.log(brandData)let len =  this.tableData.lengthfor(let i=0;i<brandData.length;i++){this.tableData.push({"brandId":brandData[i].brandId,"brandCnName":brandData[i].brandCnName,"coneBrdNum":len + i,"coneId":this.dataForm.coneId,"coneName":this.dataForm.coneName,});}console.log(this.tableData)},/*** 打开 模态窗*/openBrandDialog(){console.log(this.tableData)var ids = this.tableData.map(item => {return item.brandId})console.log(ids)// 设置模态窗 显示this.brandDialogVisible = true;this.$nextTick(() => {// 调用 模态窗的初始化方法  this.$refs.brandDialog.init(ids);});},deleteRow(index, rows) {// console.log(index, rows)rows.splice(index, 1);this.tableData=rows;},init (id) {this.dataForm.coneId = id || 0this.visible = truethis.$nextTick(() => {console.log(id)this.$http({url: this.$http.adornUrl(`/goods/categorybrand/queryByConeId/${this.dataForm.coneId}`),method: 'get',params: this.$http.adornParams()}).then(({data}) => {if (data && data.code === 0) {console.log("data", data)this.dataForm.coneId = data.categoryOne.coneIdthis.dataForm.coneName = data.categoryOne.coneNamethis.dataForm.coneInfo = data.categoryOne.coneInfothis.tableData = data.list;}})})},// 表单提交dataFormSubmit () {// this.$refs['dataForm'].validate((valid) => {//   if (valid) {this.$http({url: this.$http.adornUrl('/goods/categorybrand/batchSave'),method: 'post',data: this.$http.adornData(this.tableData, false)}).then(({data}) => {if (data && data.code === 0) {this.$message({message: '操作成功',type: 'success',duration: 1500,onClose: () => {this.visible = falsethis.$emit('refreshDataList')}})} else {this.$message.error(data.msg)}})//   }// })}}}
</script>

VUE 入门及应用 ( 五 ) ElementUI 组件相关推荐

  1. Vue入门教程 第五篇 (组件)

    component(组件) vue是单页面web程序,这意味着需要大量模块化界面参与其中,这就是组件. 组件是一个实现单一功能的vue界面,也可以是一个以功能划分而成的复杂vue界面. 注册组件: V ...

  2. Vue(四):element-ui组件用法、表单验证、图标引入、webpack目录配置指向、export暴露方法

    目录 1.element-ui组件用法 2.表单验证(复制的默认样式) 3.图标引入 4.webpack目录配置指向 5.export暴露方法 1.element-ui组件用法 这里先补充一下安装依赖 ...

  3. Vue学习二:安装element-ui组件库

    上一章:搭建Vue环境 搭建完vue环境后,安装element-ui使用其组件库,提高开发效率. 1.打开cmd,cd到在项目目录下 执行npm install element-ui,安装完成后,查看 ...

  4. Vue入门八、非父子组件间通讯

    通过Bus总线机制实施非父子组件通讯 1.创建一个空实例(Bus中央事件总线也可以叫中间组件) 2.利用$emit $on 触发和监听事件实现非父子组件的通信 组件之间使用this.$bus.$on传 ...

  5. 通过cmd和npm指令,快速引入element-ui组件

    如何通过 cmd和 npm指令,在新建 vue.js项目中,快速引入 Element-ui组件 ? 范例 - 项目路径截图参考: 流程说明: 新建项目文件夹,命名如: m2-vue-element 启 ...

  6. Vue入门(五)之组件

    组件 1.组件的概念 2.组件的使用(定义,注册,和使用) 2.1.定义组件 2.2.注册组件 2.3.使用组件 2.4.组件编写方式与 Vue 实例的区别: 3.组件嵌套 4.组件的属性 4.1.使 ...

  7. Vue第二章,在项目中使用element-ui组件

    使用vue的童鞋相信都知道vue中有很多ui组件,这里推荐使用element-ui组件(个人测试还算强大,起码开发商野心不小),因为此组件可以集成到angular.react.vue任意一个框架中. ...

  8. vue全局安装jquery,vue使用bootstrap框架,vue中封装websocket通讯,vue引入element-ui 组件库,引入highcharts图表插件

    vue安装jquery: 1.使用vue-cli创建好vue项目后,在项目文件夹下,使用命令npm install jquery --save-dev 引入jquery. 2.修改项目文件 build ...

  9. vue 树形控件可编辑_vue.js element-ui组件改iview 第一期 tree树形控件

    此为第一期修改,后期也适配了其他组件,更多查看我得文章 element-ui组件的tree树形控件修改源码改为iview组件 实现原理 修改了element-ui源码,把源码里面的tree模块提取出来 ...

最新文章

  1. python async await报错_Python 3.7.7 发布 支持async并await现在为保留关键字
  2. android记录登录状态
  3. php和python的选择排序算法,图文讲解选择排序算法的原理及在Python中的实现
  4. 计算机位数与内存相关,弄懂电脑的各种位数、内存、存储
  5. 【OOP】零钱通项目
  6. 10步骤优化SQL Server 数据库性能
  7. rowspan 动态变化_使用colspan和rowspan动态删除html表中的多个列
  8. Android 在onCreate()方法中获取控件宽高值为0解决方案
  9. 经典面试题(53):以下代码将输出的结果是什么?
  10. 图书馆的uml概念类图怎么画_设计模式:UML?设计原则?
  11. linux模板机配置文件,制作Centos 7.4操作系统模板机
  12. 新手必看,物联网卡常见的三大问题!
  13. lisp自动生成界址点表_LISP语言在宗地界址点成果表的应用
  14. 如何与导师有效沟通你的论文选题?
  15. zookeeper Session Expired
  16. 玩转Luat 进阶篇③——远程升级实现过程详解
  17. 清理注册表 php,如何彻底清理注册表?小编教你清理注册表操作方法
  18. OpenGL-入门-绘制点线面
  19. Dva.js 入门级教学文档-1
  20. python 代理ip池_GitHub - xuan525/proxy_pool: Python爬虫代理IP池(proxy pool)

热门文章

  1. 新书交稿了!!!!!!!!!!!!!!!!
  2. 突破局域网中对用户上网的限制
  3. jst数组方法速查手册(记录)
  4. MySQL中In与Exists的区别
  5. 你不得不知道的设置canvas画布的宽和高的坑
  6. 【征文】Hadoop十周年特别策划——我与Hadoop不得不说的故事
  7. Android iOS 开发全面对比分析
  8. 一个简单的后台管理系统
  9. 系统清理优化工具:CCleaner
  10. 在计算机中打不开录音笔,录音笔有哪些常见故障