能源党建后台项目总结
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…...

大数据学习栈记——Neo4j的安装与使用
本文介绍图数据库Neofj的安装与使用,操作系统:Ubuntu24.04,Neofj版本:2025.04.0。 Apt安装 Neofj可以进行官网安装:Neo4j Deployment Center - Graph Database & Analytics 我这里安装是添加软件源的方法 最新版…...

【Oracle APEX开发小技巧12】
有如下需求: 有一个问题反馈页面,要实现在apex页面展示能直观看到反馈时间超过7天未处理的数据,方便管理员及时处理反馈。 我的方法:直接将逻辑写在SQL中,这样可以直接在页面展示 完整代码: SELECTSF.FE…...

微软PowerBI考试 PL300-选择 Power BI 模型框架【附练习数据】
微软PowerBI考试 PL300-选择 Power BI 模型框架 20 多年来,Microsoft 持续对企业商业智能 (BI) 进行大量投资。 Azure Analysis Services (AAS) 和 SQL Server Analysis Services (SSAS) 基于无数企业使用的成熟的 BI 数据建模技术。 同样的技术也是 Power BI 数据…...

visual studio 2022更改主题为深色
visual studio 2022更改主题为深色 点击visual studio 上方的 工具-> 选项 在选项窗口中,选择 环境 -> 常规 ,将其中的颜色主题改成深色 点击确定,更改完成...
使用van-uploader 的UI组件,结合vue2如何实现图片上传组件的封装
以下是基于 vant-ui(适配 Vue2 版本 )实现截图中照片上传预览、删除功能,并封装成可复用组件的完整代码,包含样式和逻辑实现,可直接在 Vue2 项目中使用: 1. 封装的图片上传组件 ImageUploader.vue <te…...
python爬虫:Newspaper3k 的详细使用(好用的新闻网站文章抓取和解析的Python库)
更多内容请见: 爬虫和逆向教程-专栏介绍和目录 文章目录 一、Newspaper3k 概述1.1 Newspaper3k 介绍1.2 主要功能1.3 典型应用场景1.4 安装二、基本用法2.2 提取单篇文章的内容2.2 处理多篇文档三、高级选项3.1 自定义配置3.2 分析文章情感四、实战案例4.1 构建新闻摘要聚合器…...
安卓基础(aar)
重新设置java21的环境,临时设置 $env:JAVA_HOME "D:\Android Studio\jbr" 查看当前环境变量 JAVA_HOME 的值 echo $env:JAVA_HOME 构建ARR文件 ./gradlew :private-lib:assembleRelease 目录是这样的: MyApp/ ├── app/ …...

HarmonyOS运动开发:如何用mpchart绘制运动配速图表
##鸿蒙核心技术##运动开发##Sensor Service Kit(传感器服务)# 前言 在运动类应用中,运动数据的可视化是提升用户体验的重要环节。通过直观的图表展示运动过程中的关键数据,如配速、距离、卡路里消耗等,用户可以更清晰…...

Docker 本地安装 mysql 数据库
Docker: Accelerated Container Application Development 下载对应操作系统版本的 docker ;并安装。 基础操作不再赘述。 打开 macOS 终端,开始 docker 安装mysql之旅 第一步 docker search mysql 》〉docker search mysql NAME DE…...

【网络安全】开源系统getshell漏洞挖掘
审计过程: 在入口文件admin/index.php中: 用户可以通过m,c,a等参数控制加载的文件和方法,在app/system/entrance.php中存在重点代码: 当M_TYPE system并且M_MODULE include时,会设置常量PATH_OWN_FILE为PATH_APP.M_T…...