This encapsulates a table that is used by the entire project and can be reused multiple times. It is placed under a common component file for global use.
General function introduction, encapsulate custom instructions, click to get focus, display input box, lose focus to display text content, the type is determined by a dictionary, the picture can display the picture name or upload the picture
Subassembly
<script> export default { props: { //Generate table header fields: { type: Array, default: () => [], }, //data tableData: { type: Array, default: () => [], }, }, data() { return {}; }, created() {}, //Custom instructions directives: { focus: { inserted: function (el) { el.querySelector("input").focus(); }, }, }, methods: { // Click the box to get focus column column, row row cellClick(column, row) { column.iseditor = true; row.isnameSelected = true; }, //The input box loses focus and is triggered. Here, a prompt box is used to prompt for modification. blurEvent(column, row) { // console.log(column, row); column.iseditor = false; row.isnameSelected = false; }, }, }; </script> <template> <div> <el-table :data="tableData" border style="width: 100%"> <el-table-column :prop="field.prop" :label="field.label" :min-width="field.minWidth || 140" v-for="(field, index) in fields" :key="index" show-overflow-tooltip > <template slot-scope="scope"> <div v-if="field.slot"> <slot :name="field.slot" :row="scope.row" /> </div> <div v-else> <div> <el-input v-if="field.iseditor & amp; & amp; scope.row.isnameSelected" v-model="scope.row[field.prop]" @focus="cellClick(field, scope.row)" @blur="blurEvent(field, scope.row)" v-focus > </el-input> <p @click="cellClick(field, scope.row)" v-else> {<!-- -->{ scope.row[field.prop] || "--" }} </p> </div> </div> </template> </el-table-column> </el-table> </div> </template> <style scoped> </style> <style> .el-tooltip__popper { max-width: 1000px; } .disabled .el-upload--picture-card { display: none; } .avatar-uploader .el-upload { border: 1px dashed #d9d9d9; border-radius: 6px; cursor: pointer; position: relative; overflow: hidden; } .avatar-uploader .el-upload:hover { border-color: #409eff; } .avatar-uploader-icon { font-size: 28px; color: #8c939d; width: 178px; height: 178px; line-height: 178px; text-align: center; } .avatar { width: 178px; height: 178px; display: block; } </style>
parent component
There is also a component that encapsulates file upload and is nested in the table, which is an expansion.
<script> import editComponents from "../../components/editComponents"; export default { components: { editComponents }, data() { return { fields: [ { prop: "industryCode", label: "product", iseditor: false, }, { prop: "id", label: "id", iseditor: false, }, { prop: "paragraphType", label: "product type", iseditor: false, slot: "paragraphType", //Decide whether to use the slot based on this slot, so that the parent component can better use the data without passing it around. The following is the same }, { prop: "paragraphImage", label: "product picture", slot: "paragraphImage", iseditor: false, }, { prop: "action", label: "operation", slot: "action", }, ], tableData: [ { industryCode: "Apple", id: "15", paragraphType: "1", paragraphImage: "15.png", }, { industryCode: "Apple", id: "16", paragraphType: "2", paragraphImage: "15.png", }, { industryCode: "Apple", id: "17", paragraphType: "1", paragraphImage: "15.png", }, ], fileList: [], //Total number of files fileList1: [], //Total number of detailed files dialogImageUrl: "", dialogVisible: false, disabled: false, }; }, computed: { //File upload address upLoadUrl() { //process.env.VUE_APP_BASE_API detects the current environment to determine the interface path //Test environment VUE_APP_BASE_API = '/stage-api' if (process.env.VUE_APP_BASE_API == "/stage-api") { return "interface address"; // For example: '/minio/upload' // Production } else if (process.env.VUE_APP_BASE_API == "Production Interface") { return "interface address"; } else { // local return "interface address"; } }, }, created() { this.getlist(); }, methods: { //retrieve data getlist() { // Send a request to get data and write it here this.tableData = this.tableData.map((item) => { return { ...item, isnameSelected: false }; }); }, // Add to hAdd() { const productObj1 = { industryCode: "", id: "", paragraphType: "", paragraphImage: "", iseditor: false, uuId: Math.random(), }; this.tableData.push(productObj1); }, // delete del(row) { this.tableData = this.tableData.filter( (item) => item.uuId !== row.uuId || item.id !== row.id ); }, //Image upload============================ handleRemove(file) { console.log(file); console.log(this.$refs.upload); let uploadFiles = this.$refs.upload.uploadFiles; for (var i = 0; i < uploadFiles.length; i + + ) { if (uploadFiles[i]["url"] == file.url) { uploadFiles.splice(i, 1); } } }, handlePictureCardPreview(file) { this.dialogImageUrl = file.url; this.dialogVisible = true; }, handleDownload(file) { console.log(file); }, //File uploaded successfully----------------- async handleSuccess(response, row) { // Process the logic after successful upload console.log("Upload successful", response, row); this.fileName = response.data.fileName; row.paragraphImage = response.data.fileName; }, handleError(err, file, fileList) { // Handle the logic after upload failure console.error("Upload failed", err); }, // selectedLabel(selectedValue) { // if (selectedValue === "1") { // return "Choose one"; // } else if (selectedValue === "2") { // return "Choice 2"; // } // }, async onSubmit() { // Delete unnecessary attributes this.tableData.forEach(function (obj) { if (obj.hasOwnProperty("iseditor")) { delete obj.iseditor; } if (obj.hasOwnProperty("uuId")) { delete obj.uuId; } if (obj.hasOwnProperty("isnameSelected")) { delete obj.isnameSelected; } }); //Add if (!this.industryCode) { //Write new request here console.log(res); if (res.message == "success") { this.$message({ message: res.data, type: "success", duration: 1000, }); } else { this.$message.error(res.data); } } else { // Modification request is written here console.log(res); if (res.message == "success") { this.$message({ message: res.data, type: "success", duration: 1000, }); this.goBack(); } else { this.$message.error(res.data); } } }, }, }; </script> <template> <div> <el-button @click="hAdd" type="primary">Add</el-button> <editComponents :fields="fields" :tableData="tableData"> <template #action="{ row }"> <el-button type="text" size="small" @click="del(row)">Delete</el-button> </template> <template #paragraphImage="{ row }"> <div> <p>{<!-- -->{ row.paragraphImage || "--" }}</p> <el-upload ref="upload" :file-list="fileList1" :action="upLoadUrl" list-type="picture-card" :on-success="(e) => handleSuccess(e, row)" :on-error="handleError" > <i slot="default" class="el-icon-plus"></i> <div slot="file" slot-scope="{ file }"> <img class="el-upload-list__item-thumbnail" :src="file.url" alt="" /> <span class="el-upload-list__item-actions"> <span class="el-upload-list__item-preview" @click="handlePictureCardPreview(file)" > <i class="el-icon-zoom-in"></i> </span> <span v-if="!disabled" class="el-upload-list__item-delete" @click="handleRemove(file)" > <i class="el-icon-delete"></i> </span> </span> </div> </el-upload> <el-dialog :visible.sync="dialogVisible"> <img width="100%" :src="dialogImageUrl" alt="" /> </el-dialog> </div> </template> <template #paragraphType="{ row }"> <div> <el-select v-model="row.paragraphType" placeholder="Please select"> <el-option label="Choose one" value="1"></el-option> <el-option label="Choose Two" value="2"></el-option> </el-select> <!-- <p @click="cellClick(row)"> {<!-- -->{ selectedLabel(row.titleTypeSecond) || "--" }} {<!-- -->{row}} </p> --> </div> </template> </editComponents> <el-card> <div> <el-button type="primary" round @click="onSubmit">Submit</el-button> </div> </el-card> </div> </template> <style scoped> </style>