VUE 入门及应用 ( 五 ) ElementUI 组件
VUE 入门及应用 ( 五 ) ElementUI 组件
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" >标签 </el-col>
<el-col :span="6">
...
</el-col>
<el-col align="right" :span="3" >标签 </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-upload
list-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.path
this.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")
@ResponseBody
public 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")
@ResponseBody
public 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 {
@Override
public 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-option
v-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-option
v-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.coneId
this.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"
border
v-loading="dataListLoading"
:highlight-current-row = "true"
@current-change="handleCurrentChange"
style="width: 100%;">
<el-table-column
prop="brandId"
header-align="center"
align="center"
label="品牌主键">
</el-table-column>
<el-table-column
prop="brandCnName"
header-align="center"
align="center"
label="中文名">
</el-table-column>
<el-table-column
prop="brandEnName"
header-align="center"
align="center"
label="英文名">
</el-table-column>
<el-table-column
prop="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-column
prop="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-column
prop="brandNum"
header-align="center"
align="center"
width="80"
label="权重">
</el-table-column>
<el-table-column
prop="brandInfo"
header-align="center"
align="center"
label="说明">
</el-table-column>
<el-table-column
prop="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 = true
this.$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 = true
this.$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.list
this.totalPage = data.page.totalCount
} else {
this.dataList = []
this.totalPage = 0
}
this.dataListLoading = false
})
},
// 每页数
sizeChangeHandle (val) {
this.pageSize = val
this.pageIndex = 1
this.getDataList()
},
// 当前页
currentChangeHandle (val) {
this.pageIndex = val
this.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-dialog
v-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 = true
this.$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"
border
v-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-dialog
v-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/edit
dialogVisible: false, // 对话窗 显隐
title: "", // 对话窗 标题
maxLevel: 0, // 最大深度
draggable: false, // 是否可以拖拽
updateNodes: [], // 拖拽后要更新的结点集合
pCid: [], // 父结点
data: [], // 树形 显示数据
expandedKey: [], // 展开结点ID
defaultProps: {
children: 'children',
label: 'areaName'
}
};
},
methods: {
/**
*点击事件
*/
handleNodeClick(data) {
console.log(data);
},
/**
* 得到 数据
*/
getDataList(){
this.dataListLoading = true
this.$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、当前节点最新的父节点id
let 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.maxLevel
if (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中
@Override
public 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.后台准备树形数据
@Override
public 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;"
filterable
clearable
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].categoryId
this.dataForm.coneName = this.categoryOptions[i].categoryName
for(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].categoryId
this.dataForm.ctwoName = this.categoryOptions[i].children[l].categoryName
for(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].categoryId
this.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-column
prop="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-picker
v-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/
# 安装
npm install tinymce -S
# 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/silver/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 是一个对象,包含上传文件的信息
success 和 failure 是函数,上传成功的时候向 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-column
prop="goodsImgsState"
header-align="center"
align="center"
label="当前状态 : 0 不可用 1 使用">
<template slot-scope="scope">
<el-switch
v-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 let
let { 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-column
prop="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-column
prop="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-dialog
title="维护一级分类关联的品牌"
: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"
border
style="width: 100%">
<el-table-column
prop="brandId"
label="品牌主键"
width="180">
</el-table-column>
<el-table-column
prop="brandCnName"
label="品牌名称"
width="180">
</el-table-column>
<el-table-column
prop="coneBrdNum"
label="序号">
</el-table-column>
<el-table-column
prop="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-column
fixed="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-dialog
v-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.length
for(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 || 0
this.visible = true
this.$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.coneId
this.dataForm.coneName = data.categoryOne.coneName
this.dataForm.coneInfo = data.categoryOne.coneInfo
this.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 = false
this.$emit('refreshDataList')
}
})
} else {
this.$message.error(data.msg)
}
})
// }
// })
}
}
}
</script>
更多推荐
所有评论(0)