能源党建后台项目总结
1.引入
本次框架是Ruoyi-plus+vue2+element组合。
2.样式
由于是后台项目,样式要求统一,不可以有的输入框长有的短。着重几点:
1.关于form表单应该如何水平布局
在element中,form有个属性叫::inline="true"时候就会水平。但是有问题有的输入框比较长,像日期选择器和普通输入框大小就不一致。此时就不应该使用这个属性。应该用el-row,el-col。因为输入框默认是独占一行的就像div
代码如下
<el-form :model="queryParams" ref="queryForm" label-width="80px"><el-row :gutter="20"><el-col :span="8"><el-form-item label="考核年度" prop="appraisalYear"><el-date-pickerv-model="queryParams.appraisalYear"type="year"placeholder="选择年"value-format="yyyy-MM-dd"></el-date-picker></el-form-item></el-col><el-col :span="8"><el-form-item label="负责人" prop="userId"><el-select v-model="queryParams.userId" placeholder="请选择"><el-optionv-for="(item, index) in userList":key="index":label="item.nickName":value="item.userId"></el-option></el-select></el-form-item></el-col><el-col :span="8"><el-button type="primary" icon="el-icon-search" @click="handleQuery">查询</el-button><el-button icon="el-icon-refresh-right" @click="resetQuery">重置</el-button></el-col></el-row></el-form>
2.当form表单过多时,如何布局
添加一个模板 <template v-if="advanced">利用advanced去控制遮住还是隐藏
<el-form :model="queryParams" ref="queryForm" label-width="80px"><el-row :gutter="20"><el-col :span="8"><el-form-item label="时间" prop="date"><el-date-pickerv-model="queryParams.date"type="daterange"range-separator="至"start-placeholder="开始日期"end-placeholder="结束日期"></el-date-picker></el-form-item></el-col><el-col :span="8"><el-form-item label="名称" prop="taskName"><el-inputv-model="queryParams.taskName"placeholder="请输入"clearable/></el-form-item></el-col><template v-if="advanced"><el-col :span="8"><el-form-item label="紧急程度" prop="level"><el-select v-model="queryParams.level" placeholder="请选择"><el-option label="一般" value="0"></el-option><el-option label="急" value="1"></el-option><el-option label="紧急" value="2"></el-option></el-select></el-form-item></el-col><el-col :span="8"><el-form-item label="阅读状态" prop="isRead"><el-select v-model="queryParams.isRead" placeholder="请选择"><el-option label="未读" value="0"></el-option><el-option label="已读" value="1"></el-option></el-select></el-form-item></el-col><el-col :span="8"><el-form-item label="任务状态" prop="status"><el-select v-model="queryParams.status" placeholder="请选择"><el-option label="未开始" value="0"></el-option><el-option label="进行中" value="1"></el-option><el-option label="已提交" value="2"></el-option><el-option label="已完成" value="3"></el-option></el-select></el-form-item></el-col></template><el-col :span="8"><div style="margin-left: 30px"><el-button type="primary" icon="el-icon-search" @click="handleQuery">查询</el-button><el-button icon="el-icon-refresh-right" @click="resetQuery">重置</el-button><a @click="toggleAdvanced" style="margin-left: 16px">{{ advanced ? "收起" : "展开" }}<i:class="advanced ? 'el-icon-arrow-up' : 'el-icon-arrow-down'"></i></a></div></el-col></el-row></el-form>
3.业务逻辑
重点!!!先说说详情,编辑,行复制,删除,导入,导出,多选框的表格,单选框的表格,时间搜索,vue2数组和表单数据的渲染,文件的回显,图片的回显,还有es6语法,js逻辑。
1.详情
详情的话可以取决于你的页面是否和新增长的一样,如果长的一样,可以表单控制一个disabled属性来控制禁止输入
例子
<el-form-item label="考核年度" prop="fulfillYear"><el-date-pickerv-model="form.fulfillYear"type="year"placeholder="选择年度"value-format="yyyy":disabled="disabled"></el-date-picker></el-form-item>
2.新增
外层的新增和编辑我会使用一个页面,没啥好说的。重点是内层新增她还可以编辑这个重点。如何编辑完可以拿到回调的值,这个难。
在编辑的页面增加一个标识符判断编辑还是新增
save() {console.log("this.form", this.form);// this.$emit("getList", this.form);this.$emit("getList", {...this.form,addType: this.title === "新增考评指标" ? "add" : "edit",time: new Date().getTime(),});this.innerVisible = false;},
在新增的页面判断是新增还是编辑
getList(value) {let newAddData = [];console.log("我收到了子组件新增标准的值", value);if (value.addType === "edit") {// 创建一个与this.tableData相同结构和内容的新数组// 使用JSON.parse(JSON.stringify(object))来深拷贝对象,避免直接修改原对象let marr = JSON.parse(JSON.stringify(this.tableData));// marr.forEach((item, index) => {// if (index == value.index) {// item = value;// }// });marr[value.index] = value;console.log(marr);this.tableData = marr;console.log(marr);} else {if (Array.isArray(value)) {newAddData = [...this.tableData, ...value];} else {// 若 res.data 不是数组,将其包装成数组再进行合并newAddData = [...this.tableData, value];}// const newList = [...this.tableData, value];this.tableData = newAddData;// this.tableData.sort((a, b) => {// // 先按照sort字段降序排序// if (a.sort !== b.sort) {// return a.sort - b.sort;// } // 如果sort相同,则按照time字段升序排序(转换为毫秒)// else {// return b.time - a.time;// }// });}// const newList = [...this.tableData, value];// this.tableData = newList;console.log("this.tableData", this.tableData);console.log("this.deptOptions", this.deptOptions);// 遍历tableData数组,根据deptId查找对应的labelthis.tableData = this.tableData.map((item) => {const deptName = this.findLabelById(item.deptId, this.deptOptions);const userIds = item.userIds?.split(",");if (userIds) {const userNames = userIds.map((item2) => {const user = this.userList.find((item3) => item3.userId === item2);// console.log("userNames", userNames);return user ? user.nickName : null;}).join(",");return { ...item, deptName, userNames };} else {return { ...item, deptName };}});console.log("this.tableData", this.tableData);},
这边要注意什么是浅拷贝和深拷贝,我之前都是浅拷贝,这边要改成深拷贝,深拷贝的话避免直接修改原对象。
3.编辑
编辑的话,通常后台需要传当前行的数据进去,后端会提供一个id,去查找数据,数据回显就好了。主要是文件和图片的回显看下面
4.行复制
代码如下 深拷贝一个对象这样不会影响原对象, delete copy.createTime;删除对象属性 es6语法,这边的逻辑就是复制一个和原对象无关的对象,然后用接口新增。 这边重点是es6语法和深浅拷贝的了解与引用
1.深拷贝
const copy = JSON.parse(JSON.stringify(row));
2. 浅拷贝
const copy = Object.assign({}, row);
- 如果修改的是第一层(直接)属性且它们是原始类型,
row
不受影响; - 如果修改的是第一层(直接)属性且它们是引用类型(如其他对象或数组),实际上是在修改共享的嵌套对象,此时
row
会相应地发生改变。 - 例子如下
-
const row = {name: 'John',address: {street: 'Main St.',number: 123,}, };const copy = Object.assign({}, row);// 修改第一层属性(字符串),不影响 row copy.name = 'Jane'; // row.name仍然是'John'// 修改嵌套对象的属性,会影响 row copy.address.street = 'New St.'; // row.address.street现在也是'New St.'
async copyRow(row) {console.log("row", row);// this.loading = true;const copy = JSON.parse(JSON.stringify(row));delete copy.createTime;copy.dutyAppraisalId = null;copy.status = nullcopy.isRead = 0copy.progress = 0console.log("copy", copy);await this.getDetail(row.dutyAppraisalId);addDemo({status: 0,type: 0,...copy,dutyItems: this.tableData,}).then((res) => {if (res.code == 200) {this.$message({message: "新建成功",type: "success",});this.loading = false;this.getList();}});// .finally(() => {// this.loading = false;// });},
5.删除
在这个项目中有3中删除
第一种:利用接口拿到id,接口删除
del(No, data) {// console.log("No", No, "DATA", data);if (data.workItemId) {delDetailList(data.workItemId).then((res) => {if (res.code == 200) {this.$message({message: "删除成功",type: "success",});this.deleteFuc(No, data);}});} else {this.deleteFuc(No, data);}},
第二种:不需要调用接口时候,利用filter es6的语法
// 使用数组的filter方法,传入一个回调函数作为筛选条件// 回调函数接受当前遍历项(item)作为参数,检查其"No"属性是否与传入的No参数相异// 若相异则保留该项,否则过滤掉const filterData = this.originalData.filter((item) => {return item.No !== No;});
第三种:直接用数组splice
this.fileList.splice(index, 1);
6.导入
三部曲
第一步:
<el-uploadclass="upload-demo":action="uploadFileUrl"multiple:show-file-list="false":headers="headers":on-success="handleUploadSuccess"><el-button>导入</el-button></el-upload>
第二步:定义, id不存在,存在,不确定存不存在的写法都在下方
import { getToken } from "@/utils/auth";
export default {components: { contentSet, editStandard },data() {return {activeName: "first",activeName2: "first",form: {},title: "",dialogVisible: false,// 接收数据tableData: [],updatedData: [],// 字典值List: [],showData: [], //展示数据originalData: [], //真正的数据// rowData: {}, //行数据uploadFileUrl: `${process.env.VUE_APP_BASE_API}/epb/workItem/importData${this.form && this.form.id ? "?workId=" + this.form.id : ""}`,// uploadFileUrl: `${process.env.VUE_APP_BASE_API}/epb/workItem/importData?workId=${this.form.id}`, // 上传文件服务器地址// uploadFileUrl: process.env.VUE_APP_BASE_API + "/epb/workItem/importData", // 上传文件服务器地址headers: {Authorization: "Bearer " + getToken(),},show: false,workList: [],loading: false,};},
第三步:
// 导入文件handleUploadSuccess(res, file) {// console.log("res", res, "file", file);if (res.code == 200) {//深拷贝let mArr = JSON.parse(JSON.stringify(tableData));// 页面操作 concat() 方法用于合并两个或多个数组。此方法不会更改现有数组,而是返回一个新数组。mArr = mArr.concat(res.data.workItemVos);mArr = mArr.map((item, index) => {return {...item,sort: item.sort + 1, //一开始就遍历加上序号markTime: new Date().getTime(),};});// console.log("mArr", mArr);this.getContentList(mArr);// this.workList = [];this.workList = this.workList.concat(res.data.workLabelVos);console.log("this.workList", this.workList);}},
7.导出
<el-buttontype="primary"plainicon="el-icon-download"@click="handleExport">导出</el-button>/** 导出按钮操作 */handleExport() {this.download("/epb/dutyFulfill/export",{...this.queryParams,},`责任落实档案列表.xlsx`);},
8.多选框的表格
9.单选框的表格
10.时间搜索
getList() {// this.getTime();let mData = Object.assign({pageNum: this.pagination.current,pageSize: this.pagination.pageSize,},// this.queryParams{...this.queryParams,startTime: this.queryParams?.date?.length ? dayjs(this.queryParams.date[0]).format("YYYY-MM-DD") : undefined,endTime: this.queryParams?.date?.length > 1 ? dayjs(this.queryParams.date[1]).format("YYYY-MM-DD") : undefined,});mData.createUserId = store.getters.userId;this.loading = true;listDemo(mData).then((res) => {if (res.code == 200) {console.log("res", res);this.remindersList = res.rows;console.log("this.remindersList", this.remindersList);this.pagination.total = res.total;this.loading = false;}}).finally(() => {this.loading = false;});},
11.对象的键名
// const obj = {// a: 1,// b: 2,// c: []// }// obj['c']就是数组,拿到的是obj.c的值,可以用push
groupAndMergeData(arr) {// console.log("a2222", arr);let groupedData = {}; // 遍历newAddData,按workContent字段分类arr.forEach((item) => {// const obj = {// a: 1,// b: 2,// c: []// }// obj['c']就是数组,拿到的是obj.c的值,可以用pushif (!groupedData[item.workContent]) {// [item.workContent]表示键名groupedData[item.workContent] = [item];} else {groupedData[item.workContent].push(item);// groupedData[item.workContent].sort((a, b) => a.sort - b.sort);groupedData[item.workContent].sort((a, b) => {// 先按照sort字段降序排序if (a.sort !== b.sort) {return a.sort - b.sort;} // 如果sort相同,则按照time字段升序排序(转换为毫秒)else {return b.markTime - a.markTime;}});}});// console.log("arr11111", arr);// console.log("groupedData", groupedData);// 合并所有子数组let mergedData = [];for (let category in groupedData) {mergedData = [...mergedData, ...groupedData[category]];}// console.log("mergedData", mergedData);// 全部数据保存到这个真正的数据里const sortData = [...mergedData].map((item, index) => {return {...item,No: index + 1, //一开始就遍历加上序号};});return sortData;},
分组 一种是对象的分组,一种的数组的分组
数组的分组
let classifyWorkContentList = []; //类别数组[...sourceData].forEach((item) => {// .includes()方法返回一个布尔值(true或false)if (classifyWorkContentList.indexOf(item.workContent) === -1) {classifyWorkContentList.push(item.workContent);}});
12.文件的回显
const fileArr =data?.fileUrl?.split(",")?.map((item, index) => {return {fileUrl: item,fileName: data?.fileName?.split(",")[index],};}) || [];this.form = data;this.$set(this.form, "fileList", fileArr);<el-descriptions-item label="附件" :span="2"><span v-for="(item, index) in form.fileList" :key="index"><a :href="item.fileUrl" target="_blank">{{ item.fileName }}</a>,</span></el-descriptions-item>
这边的index因为fileUrl和fileName不是一个数组里的,俩个本来是俩个字符串,然后用split分隔成俩个数组,这边的操作就是把它们弄到一个数组里
13.图片的回显
<template><div><el-image v-for="(item,index) in realSrcList" :key="index":src="`${item}`"fit="cover":style="`width:${realWidth};height:${realHeight};`":preview-src-list="realSrcList"><div slot="error" class="image-slot"><i class="el-icon-picture-outline"></i></div></el-image></div></template><script>export default {name: "ImagePreview",props: {src: {type: String,default: ""},width: {type: [Number, String],default: ""},height: {type: [Number, String],default: ""}},computed: {realSrc() {if (!this.src) {return;}let real_src = this.src.split(",")[0];return real_src;},realSrcList() {if (!this.src) {return;}let real_src_list = this.src.split(",");let srcList = [];real_src_list.forEach(item => {return srcList.push(item);});return srcList;},realWidth() {return typeof this.width == "string" ? this.width : `${this.width}px`;},realHeight() {return typeof this.height == "string" ? this.height : `${this.height}px`;}},
};
</script><style lang="scss" scoped>
.el-image {border-radius: 5px;background-color: #ebeef5;box-shadow: 0 0 5px 1px #ccc;::v-deep .el-image__inner {transition: all 0.3s;cursor: pointer;&:hover {transform: scale(1.2);}}::v-deep .image-slot {display: flex;justify-content: center;align-items: center;width: 100%;height: 100%;color: #909399;font-size: 30px;}
}
</style>
父组件
<ImagePreview:src="form.picUrl":width="150":height="150"></ImagePreview>
14.es6语法
map,filter
15.js逻辑关于党务工作清单的
第一步:要先判断是编辑还是新增。第二步:要确定传给后端的格式。第三步:展示数据的格式。第四步:删除的排序要判断是有id的还是直接删除。第五步:当导入进来的时候,相当于新增,要进行,说明新增的时候要进行判断,如果是数据还是对象。第六步:对于这种提交数据和展示数据不一样数据格式时候要注意操作。
第一步:
getContentList(value) {// console.log("我收到子组件传递的值", value);let newData = [];let newAddData = [];if (value.addType === "edit") {// console.log("this.originalData", this.originalData);newData = this.originalData.map((item) => {if (item.No === value.No) {return value;}return item;});} else {if (Array.isArray(value)) {newData = [...this.originalData, ...value];} else {// 若 res.data 不是数组,将其包装成数组再进行合并newData = [...this.originalData, value];}}// console.log("value111", value);// console.log("更新后的newData:", newData);const sortData = this.groupAndMergeData(newData);// 赋值给真正的数组this.originalData = sortData;// console.log("sortData时间字段", sortData);// 赋值给展示数据this.showData = this.handleData(sortData);},
数据分类
// 分类排序groupAndMergeData(arr) {// console.log("a2222", arr);let groupedData = {}; // 遍历newAddData,按workContent字段分类arr.forEach((item) => {// const obj = {// a: 1,// b: 2,// c: []// }// obj['c']就是数组,拿到的是obj.c的值,可以用pushif (!groupedData[item.workContent]) {// [item.workContent]表示键名groupedData[item.workContent] = [item];} else {groupedData[item.workContent].push(item);// groupedData[item.workContent].sort((a, b) => a.sort - b.sort);groupedData[item.workContent].sort((a, b) => {// 先按照sort字段降序排序if (a.sort !== b.sort) {return a.sort - b.sort;} // 如果sort相同,则按照time字段升序排序(转换为毫秒)else {return b.markTime - a.markTime;}});}});// console.log("arr11111", arr);// console.log("groupedData", groupedData);// 合并所有子数组let mergedData = [];for (let category in groupedData) {mergedData = [...mergedData, ...groupedData[category]];}// console.log("mergedData", mergedData);// 全部数据保存到这个真正的数据里const sortData = [...mergedData].map((item, index) => {return {...item,No: index + 1, //一开始就遍历加上序号};});return sortData;},
重点是对象键名 变数组
// const obj = {// a: 1,// b: 2,// c: []// }// obj['c']就是数组,拿到的是obj.c的值,可以用push
展示数据处理
// 处理数据handleData(sourceData) {// 分类let classifyWorkContentList = []; //类别数组[...sourceData].forEach((item) => {// .includes()方法返回一个布尔值(true或false)if (classifyWorkContentList.indexOf(item.workContent) === -1) {classifyWorkContentList.push(item.workContent);}});console.log("classifyWorkContentList", classifyWorkContentList);// 按照表格样式修改数据结构// 例如[{workContent: 'xxx', workNormList: [{workItemId: 1111,workId: 11111 ,workContent:'xxx',sort: 1,workNorm:'xxx',No: 1,isDone: 0}]const handleData = classifyWorkContentList.map((item) => {return {workContent: item,workNormList: sourceData.filter((item2) => item2.workContent === item),};});return handleData;},
删除数据
deleteFuc(No, data) {const filterData = this.originalData.filter((item) => {return item.No !== No;});const sortData = filterData.map((item, index) => {return {...item,No: index + 1, //一开始就遍历加上序号};});this.originalData = sortData;// 赋值给展示数据this.showData = this.handleData(sortData);},// 删除del(No, data) {// console.log("No", No, "DATA", data);if (data.workItemId) {delDetailList(data.workItemId).then((res) => {if (res.code == 200) {this.$message({message: "删除成功",type: "success",});this.deleteFuc(No, data);}});} else {this.deleteFuc(No, data);}},
导入文件
// 导入文件handleUploadSuccess(res, file) {// console.log("res", res, "file", file);if (res.code == 200) {const tableData = [];let mArr = JSON.parse(JSON.stringify(tableData));// 页面操作 concat() 方法用于合并两个或多个数组。此方法不会更改现有数组,而是返回一个新数组。mArr = mArr.concat(res.data.workItemVos);mArr = mArr.map((item, index) => {return {...item,sort: item.sort + 1, //一开始就遍历加上序号markTime: new Date().getTime(),};});// console.log("mArr", mArr);this.getContentList(mArr);// this.workList = [];this.workList = this.workList.concat(res.data.workLabelVos);console.log("this.workList", this.workList);}},
16.树形下拉框
子组件
<template><div><a-tree-selectv-model="orgId"style="width: 100%"size="large":dropdown-style="{ maxHeight: '400px', overflow: 'auto', zIndex: 3000 }"placeholder="请选择"allow-cleartree-default-expand-all:disabled="disabled":tree-data="vorganTreeData":replaceFields="replaceFields"@change="onChange"></a-tree-select></div>
</template><script>
//注意!!!!!
//在modal弹窗组件中使用该组件需要在关闭弹窗方法里清空数据否则会报错
import { userdepList } from "@/api/user/user";
export default {name: "vorganTree",props: {value: {// 如果希望value可以接收int类型的值而不报错,可以将type类型修改为可以兼容字符串和整数的类型type: [String, Number],default: "",},disabled: {type: Boolean,default: false,},replaceFields: {type: Object,default: () => {return {children: "children",title: "label",key: "id",value: "id",};},},},data() {return {orgId: this.value,vorganTreeData: [],deptId: "",};},watch: {value: {handler(newVal) {this.orgId = newVal;},immediate: true,},},mounted() {// this.$bus.$on("id", (data) => {// console.log("我是任务组件,收到了数据", data);// this.deptId = data;// });this.deptId = this.$bus.id;this.userdepList();},methods: {userdepList() {userdepList({ ancestors: this.deptId }).then((res) => {console.log("res.data", res);this.vorganTreeData = res.data;});},selectClear() {this.orgId = undefined;},onChange(value, item, xx) {console.log(11111, value, item, xx);// this.$emit("update:value", value);this.$emit("getdep", value);},},
};
</script><style scoped lang="less">
</style>
父组件
<orginTree v-model="form.deptId" @getdep="getdep"></orginTree>
17.时间的回显
this.$set(this.form, "date", [dayjs(data.startTime).format("YYYY-MM-DD"),dayjs(data.endTime).format("YYYY-MM-DD"),]);
18.富文本去除标签 富文本组件事Editor
<Editor v-model="form.requirement" :height="100"></Editor>// 提交数据时去除<p>标签const processedRequirement = that.form.requirement?.replace(/<\/?p>/g,"");
19.props接收兼容性写法
props: {value: {// 如果希望value可以接收int类型的值而不报错,可以将type类型修改为可以兼容字符串和整数的类型type: [String, Number],default: "",},disabled: {type: Boolean,default: false,},replaceFields: {type: Object,default: () => {return {children: "children",title: "label",key: "id",value: "id",};},},},
20.文本的总体代码
<template><div class="upload-file"><el-uploadv-if="isUpload"multiple:action="uploadFileUrl":before-upload="handleBeforeUpload":file-list="fileList":limit="limit":on-error="handleUploadError":on-exceed="handleExceed":on-success="handleUploadSuccess":show-file-list="false":headers="headers"class="upload-file-uploader"ref="fileUpload"><!-- 上传按钮 --><el-button size="mini" type="primary">选取文件</el-button><!-- 上传提示 --><div class="el-upload__tip" slot="tip" v-if="showTip">请上传<template v-if="fileSize">大小不超过 <b style="color: #f56c6c">{{ fileSize }}MB</b></template><template v-if="fileType">格式为 <b style="color: #f56c6c">{{ fileType.join("/") }}</b></template>的文件</div></el-upload><!-- 文件列表 --><transition-group class="upload-file-list el-upload-list el-upload-list--text" name="el-fade-in-linear" tag="ul" v-if="isFileList"><li :key="file.url" class="el-upload-list__item ele-upload-list__item-content" style="padding: 0 4px" v-for="(file, index) in fileList"><el-link :href="`${file.url}`" :underline="false" target="_blank"><span class="el-icon-document"> {{ getFileName(file.name) }} </span></el-link><div class="ele-upload-list__item-content-action" v-if="isUpload"><el-link :underline="false" @click="handleDelete(index)" type="danger">删除</el-link></div></li></transition-group></div>
</template><script>
import { getToken } from "@/utils/auth";
import { listByIds, delOss } from "@/api/system/oss";export default {name: "FileUpload",props: {// 值value: [String, Object, Array],// 数量限制limit: {type: Number,default: 5,},// 大小限制(MB)fileSize: {type: Number,default: 5,},// 文件类型, 例如['png', 'jpg', 'jpeg']fileType: {type: Array | Boolean,default: () => ["doc", "xls", "ppt", "txt", "pdf"],},// 是否显示提示isShowTip: {type: Boolean,default: true,},// 是否显示文件列表isFileList: {type: Boolean,default: true,},dataIndex: {type: Number | String,default: 0,},// 是否可以上传isUpload: {type: Boolean,default: true,},},data() {return {number: 0,uploadList: [],baseUrl: process.env.VUE_APP_BASE_API,uploadFileUrl: process.env.VUE_APP_BASE_API + "/system/oss/upload", // 上传文件服务器地址headers: {Authorization: "Bearer " + getToken(),},fileList: [],};},watch: {value: {async handler(val) {if (val) {let temp = 1;// 首先将值转为数组let list;if (Array.isArray(val)) {list = val;} else {console.log(val);await listByIds(val).then((res) => {list = res.data.map((oss) => {oss = {name: oss.originalName,url: oss.url,ossId: oss.ossId,};return oss;});this.$emit("getfile", list, this.dataIndex);});}// 然后将数组转为对象数组this.fileList = list.map((item) => {item = { name: item.name, url: item.url, ossId: item.ossId };item.uid = item.uid || new Date().getTime() + temp++;return item;});} else {this.fileList = [];return [];}},deep: true,immediate: true,},},computed: {// 是否显示提示showTip() {return this.isShowTip && (this.fileType || this.fileSize);},},methods: {// 上传前校检格式和大小handleBeforeUpload(file) {// 校检文件类型if (this.fileType) {const fileName = file.name.split(".");const fileExt = fileName[fileName.length - 1];const isTypeOk = this.fileType.indexOf(fileExt) >= 0;if (!isTypeOk) {this.$modal.msgError(`文件格式不正确, 请上传${this.fileType.join("/")}格式文件!`);return false;}}// 校检文件大小if (this.fileSize) {const isLt = file.size / 1024 / 1024 < this.fileSize;if (!isLt) {this.$modal.msgError(`上传文件大小不能超过 ${this.fileSize} MB!`);return false;}}this.$modal.loading("正在上传文件,请稍候...");this.number++;return true;},// 文件个数超出handleExceed() {this.$modal.msgError(`上传文件数量不能超过 ${this.limit} 个!`);},// 上传失败handleUploadError(err) {this.$modal.msgError("上传文件失败,请重试");this.$modal.closeLoading();},// 上传成功回调handleUploadSuccess(res, file) {if (res.code === 200) {this.uploadList.push({name: res.data.fileName,url: res.data.url,ossId: res.data.ossId,});this.uploadedSuccessfully();} else {this.number--;this.$modal.closeLoading();this.$modal.msgError(res.msg);this.$refs.fileUpload.handleRemove(file);this.uploadedSuccessfully();}},// 删除文件handleDelete(index) {let ossId = this.fileList[index].ossId;// delOss(ossId);this.fileList.splice(index, 1);this.$emit("input", this.listToString(this.fileList));},// 上传结束处理uploadedSuccessfully() {if (this.number > 0 && this.uploadList.length === this.number) {this.fileList = this.fileList.concat(this.uploadList);this.uploadList = [];this.number = 0;this.$emit("input", this.listToString(this.fileList));this.$modal.closeLoading();}},// 获取文件名称getFileName(name) {// 如果是url那么取最后的名字 如果不是直接返回if (name.lastIndexOf("/") > -1) {return name.slice(name.lastIndexOf("/") + 1);} else {return name;}},// 对象转成指定字符串分隔listToString(list, separator) {let strs = "";separator = separator || ",";for (let i in list) {strs += list[i].ossId + separator;}return strs != "" ? strs.substr(0, strs.length - 1) : "";},},
};
</script><style scoped lang="scss">
.upload-file-uploader {margin-bottom: 5px;
}
.upload-file-list .el-upload-list__item {border: 1px solid #e4e7ed;line-height: 2;margin-bottom: 10px;position: relative;
}
.upload-file-list .ele-upload-list__item-content {display: flex;justify-content: space-between;align-items: center;color: inherit;
}
.ele-upload-list__item-content-action .el-link {margin-right: 10px;
}
</style>
父组件
<el-table-column label="操作" prop="action" width="100"><template slot-scope="scope"><!-- <el-upload class="upload-demo" action="https://jsonplaceholder.typicode.com/posts/" :on-change="handleChange" :show-file-list="false"><el-button size="small" type="primary">点击上传</el-button></el-upload> --><FileUploadv-model="tableData[scope.$index].materialIds":dataIndex="scope.$index":isShowTip="false":fileType="false":isFileList="false":isUpload="scope.row.status == 3 || scope.row.status == 0 || scope.row.status == null"@getfile="getfile"></FileUpload></template></el-table-column>
相关文章:

能源党建后台项目总结
1.引入 本次框架是Ruoyi-plusvue2element组合。 2.样式 由于是后台项目,样式要求统一,不可以有的输入框长有的短。着重几点: 1.关于form表单应该如何水平布局 在element中,form有个属性叫::inline"true"…...

股票高胜率的交易法则是什么?
股票交易中的高胜率交易法则并非一成不变,而是根据市场状况、个人投资风格和经验等多种因素综合而定的。以下是一些有助于提升交易胜率的法则和策略: 1.趋势跟踪法则:在股票交易中,趋势跟踪是一种有效的策略。通过观察大盘和个股…...

C语言 | sizeof与strlen的区别(附笔试题)
目录: 1. sizeof和strlen的对比 2. 数组和指针 笔试题解析 3. 指针运算 笔试题解析 内容多多,需耐心看完,加油!!! 一.sizeof和strlen的对比 1.1 sizeof 在学习操作符的时候,我们学习了 s…...

AI自动绘画器介绍和应用场景
AI自动绘画器是一种利用人工智能技术来生成绘画作品的工具。以下是一些常见的AI自动绘画器: DeepDream: 风格:可以生成三种风格的图片,包括深度梦幻风格、深度风格和浅层风格。应用场景:起初设计用于帮助研究人员理解…...

java二叉树前中后序遍历
代码随想录解题思路🆒力扣前序题目🆒力扣中序题目🆒力扣后序题目 递归遍历 // 前序遍历 class Solution {public List<Integer> preorderTraversal(TreeNode root) {List<Integer> res new ArrayList<>();preorder(root…...

【LeetCode刷题笔记】LeetCode 1365.有多少小于当前数字的数字
创作不易,本篇文章如果帮助到了你,还请点赞 关注支持一下♡>𖥦<)!! 主页专栏有更多知识,如有疑问欢迎大家指正讨论,共同进步! 更多算法知识专栏:算法分析🔥 给大家跳段街舞感谢…...

室内定位中文综述阅读
1 室内高精度定位技术总结与展望 [4]柳景斌,赵智博,胡宁松等.室内高精度定位技术总结与展望[J].武汉大学学报(信息科学 版),2022,47(07):997-1008.DOI:10.13203/j.whugis20220029. 1.1.1 WiFi‐RTT定位 2016 年 12 月,随着新版 IEEE802.11 标准的公布,…...

微信小程序uniapp+vue电力巡线任务故障报修管理系统2q91t
uni-app框架:使用Vue.js开发跨平台应用的前端框架,编写一套代码,可编译到Android、小程序等平台。 前端开发:vue 语言:javapythonnodejsphp均支持 运行软件:idea/eclipse/vscode/pycharm/wamp均支持 框架支持:Ssm/django/flask/t…...

springboot国际化多语言
1,新建国际化多语言文件 在resources目录下新建 messages.properties 其他语言的文件 编辑messages.properties文件,下方从text切换到Resource Bundle ,即可对照着编辑多语言文件 (如果没有找到Resource Bundle,先在settings->plugins中安装Resource Bundle Editor) 2,配…...

set和map
这里是目录标题 setinsertfinderasecountlower_boundupper_boundmultisetset的应用 mappairinsertinsert的pair map的遍历map对[ ]的重载(重点)multimap set set的普通迭代器和const迭代器都不支持修改。(这点可以根据源代码看出来,都是对const iterator进行了type…...

Open CASCADE学习|求曲面的参数空间
在三维空间中,任意的曲面都可以通过特定的方法映射到一个二维参数平面上,从而对其进行详细的几何分析和处理。首先,我们需要从三维模型中提取出特定的曲面,这通常被称为“Face”。一个face可以被视为三维空间中的一个封闭区域&…...

代码随想录阅读笔记-二叉树【总结】
二叉树的理论基础 代码随想录 (programmercarl.com):二叉树的种类、存储方式、遍历方式、定义方式 二叉树的遍历方式 深度优先遍历 代码随想录阅读笔记-二叉树【递归遍历】-CSDN博客:递归三部曲初次亮相代码随想录阅读笔记-二叉树【迭代遍历】-CSDN博…...

【SpringBoot整合系列】SpringBoot整合FastDFS(二)
目录 SpringBoot整合FastDFSJava客户端/依赖常用api接口解释1.uploadFile参数返回值 2.uploadSlaveFile参数返回值 3.getMetadata参数返回值 4.overwriteMetadata参数:返回值:无 5.mergeMetadata参数:返回值:无 6.queryFileInfo参…...

L2-2 巴音布鲁克永远的土(二分+并查集)
思路:我们可以二分答案,然后判断当前答案合不合理。 对于判断答案合理,可以用并查集,看mid能否把所有检查点连进一个集合中,枚举每个结点,如何当前结点周围的四个方向可以连的话,就加进同一个集…...

Spring Cloud学习笔记:Eureka简介,Eureka简单样例
这是本人学习的总结,主要学习资料如下 - 马士兵教育 [TOC](目录)1、Eureka 1.1、架构 Eureka是SpringCloud Nexflix的核心子模块,其中包含Server和Client。 Server提供服务注册,存储所有可用服务节点。 Client用于简化和Server的通讯复杂…...

【漏洞复现】WordPress Welcart 任意文件读取漏洞(CVE-2022-4140)
0x01 产品简介 Welcart 是一款免费的 WordPress 电子商务插件。Welcart 具有许多用于制作在线商店的功能和自定义设置。您可以轻松创建自己的原始在线商店。 0x02 漏洞概述 Welcart存在任意文件读取漏洞,未授权的攻击者可以通过该漏洞读取任意文件,获…...

快速排序:深入解析其原理、实现与性能特性
快速排序,以其名字所示,是一种追求速度的高效排序算法。作为分治法在排序问题上的典型应用,快速排序凭借其平均情况下近乎理想的O(n log n)时间复杂度和简洁的实现逻辑,在实际编程与数据处理中占据着重要地位。本篇博客将详细解析…...

一文看懂Mac地址
一、Mac地址是什么? 虽然IP地址已经成为一个家喻户晓的术语,但还有一个同样重要的数字标识符值得我们关注——MAC地址。在本文中,我们旨在阐明网络中这个经常被忽视的方面。加入我们,深入研究 MAC 地址的世界,了解它们…...

2024.4.10作业
#include "widget.h" #include "ui_widget.h" Widget::Widget(QWidget *parent) : QWidget(parent) , ui(new Ui::Widget) { ui->setupUi(this); } Widget::~Widget() { delete ui; } //显示时间 void Widget::timerEvent(QTimerEvent *e) { QT…...

python - Django创建项目
项目运行命令 根目录下运行命令: python manage.py runserver win环境创建项目 直接使用 Pycharm 创建项目 在 cmd 或 Linux 命令行环境下创建 Django 项目 django-admin startproject mysite 这样就会在当前目录下创建一个叫做 mysite 的Django项目。 可以看到Djang…...

WPF —— 动画缩放变换
ScaleTransform:在二维x-y坐标系统内缩放对象; 在故事板中依赖的属性为RenderTransform.ScaleX或RenderTransform.ScaleY,这要根据你要沿哪个轴进行缩放,X代表x轴,Y代表y轴; key属性当我们使用静态资源访问时候--> <!--TargetType"{x:Type Button} 直接应用…...

SQL注入---盲注
文章目录 目录 一.盲注概述 布尔盲注: 时间盲注: 一.盲注概述 注是一种SQL注入攻击的形式,在这种攻击中,攻击者向目标应用程序发送恶意注入代码,然后通过观察应用程序的响应来推断出数据库中的信息。与常规的SQL注入…...

PlanUML和Mermaid哪个好?
引言 在当今信息化快速发展的时代,数据可视化和图表工具不仅对于程序员,也对于非技术背景的人士至关重要。绘图工具可以帮助我们更好地理解和表达复杂的概念或数据流。PlantUML和Mermaid是两款被广泛使用的绘图语言,它们都能够通过简洁的文本…...

leetcode 343. 整数拆分
题目 给定一个正整数 n ,将其拆分为 k 个 正整数 的和( k > 2 ),并使这些整数的乘积最大化。 返回 你可以获得的最大乘积 。 示例 1: 输入: n 2 输出: 1 解释: 2 1 1, 1 1 1。 示例 2: 输入: n 10 输出: 36 解释: 1…...

【MATLAB源码-第180期】基于matlab的PTS,SLM,CPFilter三种降低OFDM系统的PAPR仿真。
操作环境: MATLAB 2022a 1、算法描述 1. 限幅和滤波(Clipping and Filtering) 原理简介 限幅和滤波是一种基础且直观的方法,用于降低OFDM信号的PAPR。在限幅阶段,信号的幅度在达到设定阈值时会被削减,…...

学透Spring Boot — 004. Spring Boot Starter机制和自动配置机制
如果你项目中一直用的是 Spring Boot,那么恭喜你没有经历过用 Spring 手动集成其它框架的痛苦。 都说 Spring Boot 大大简化了 Spring 框架开发 Web 应用的难度,这里我们通过配置 Hibernate 的两种方式来深刻体会这一点: 使用 Spring 框架集…...

面试算法-170-二叉树的最大深度
题目 给定一个二叉树 root ,返回其最大深度。 二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数。 示例 1: 输入:root [3,9,20,null,null,15,7] 输出:3 解 class Solution {public int maxDepth(TreeNod…...

【数据结构】哈希
文章目录 1. 哈希概念2. 哈希冲突3. 哈希函数4. 哈希冲突解决4.1 闭散列4.2 开散列 unordered 系列的关联式容器之所以效率比较高,是因为其底层使用了哈希结构。 1. 哈希概念 顺序结构以及平衡树中,元素关键码与其存储位置之间没有对应的关系ÿ…...

Kubernetes(k8s)监控与报警(qq邮箱+钉钉):Prometheus + Grafana + Alertmanager(超详细)
Kubernetes(k8s)监控与报警(qq邮箱钉钉):Prometheus Grafana Alertmanager(超详细) 1、部署环境2、基本概念简介2.1、Prometheus简介2.2、Grafana简介2.3、Alertmanager简介2.4、Prometheus …...

STM32-04基于HAL库(CubeMX+MDK+Proteus)中断案例(按键中断扫描)
文章目录 一、功能需求分析二、Proteus绘制电路原理图三、STMCubeMX 配置引脚及模式,生成代码四、MDK打开生成项目,编写HAL库的按键检测代码五、运行仿真程序,调试代码 一、功能需求分析 在完成GPIO输入输出案例之后,开始新的功能…...