本次框架是Ruoyi-plus+vue2+element组合。
由于是后台项目,样式要求统一,不可以有的输入框长有的短。着重几点:
在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-picker
v-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-option
v-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>
添加一个模板 <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-picker
v-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-input
v-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>
重点!!!先说说详情,编辑,行复制,删除,导入,导出,多选框的表格,单选框的表格,时间搜索,vue2数组和表单数据的渲染,文件的回显,图片的回显,还有es6语法,js逻辑。
详情的话可以取决于你的页面是否和新增长的一样,如果长的一样,可以表单控制一个disabled属性来控制禁止输入
例子
<el-form-item label="考核年度" prop="fulfillYear">
<el-date-picker
v-model="form.fulfillYear"
type="year"
placeholder="选择年度"
value-format="yyyy"
:disabled="disabled"
>
</el-date-picker>
</el-form-item>
外层的新增和编辑我会使用一个页面,没啥好说的。重点是内层新增她还可以编辑这个重点。如何编辑完可以拿到回调的值,这个难。
在编辑的页面增加一个标识符判断编辑还是新增
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查找对应的label
this.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);
},
编辑的话,通常后台需要传当前行的数据进去,后端会提供一个id,去查找数据,数据回显就好了。主要是文件和图片的回显看下面
代码如下 深拷贝一个对象这样不会影响原对象, 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 = null
copy.isRead = 0
copy.progress = 0
console.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;
// });
},
在这个项目中有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);
三部曲
第一步:
<el-upload
class="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);
}
},
<el-button
type="primary"
plain
icon="el-icon-download"
@click="handleExport"
>导出</el-button
>
/** 导出按钮操作 */
handleExport() {
this.download(
"/epb/dutyFulfill/export",
{
...this.queryParams,
},
`责任落实档案列表.xlsx`
);
},
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;
});
},
// 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的值,可以用push
if (!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);
}
});
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分隔成俩个数组,这边的操作就是把它们弄到一个数组里
<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>
map,filter
第一步:要先判断是编辑还是新增。第二步:要确定传给后端的格式。第三步:展示数据的格式。第四步:删除的排序要判断是有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的值,可以用push
if (!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);
}
},
子组件
<template>
<div>
<a-tree-select
v-model="orgId"
style="width: 100%"
size="large"
:dropdown-style="{ maxHeight: '400px', overflow: 'auto', zIndex: 3000 }"
placeholder="请选择"
allow-clear
tree-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>
this.$set(this.form, "date", [
dayjs(data.startTime).format("YYYY-MM-DD"),
dayjs(data.endTime).format("YYYY-MM-DD"),
]);
<Editor v-model="form.requirement" :height="100"></Editor>
// 提交数据时去除<p>标签
const processedRequirement = that.form.requirement?.replace(
/<\/?p>/g,
""
);
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",
};
},
},
},
<template>
<div class="upload-file">
<el-upload
v-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> -->
<FileUpload
v-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>
更多【能源-能源党建后台项目总结】相关视频教程:www.yxfzedu.com