| | |
| | | <template> |
| | | <el-dialog :visible.sync="visible" :title="editData ? '编辑培养皿观察记录' : '新增培养皿观察记录'" width="80%" @close="handleClose"> |
| | | <el-dialog :visible.sync="visible" :title="readOnly ? '培养皿观察记录详情' : editData ? '编辑培养皿观察记录' : '新增培养皿观察记录'" width="80%" @close="handleClose"> |
| | | <el-form :model="form" :rules="rules" ref="formRef" label-width="120px" label-position="top"> |
| | | <el-row :gutter="24"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="分离菌落编号" prop="separateColonyCode"> |
| | | <el-input :disabled="roleType!=4" v-model="form.separateColonyCode" placeholder="请输入分离菌落编号" /> |
| | | <el-input :disabled="!editable" v-model="form.separateColonyCode" placeholder="请输入分离菌落编号" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="形状强壮度排名" prop="strength"> |
| | | <el-input :disabled="roleType!=4" v-model="form.strength" placeholder="请输入形状强壮度排名" /> |
| | | <el-input :disabled="!editable" v-model="form.strength" placeholder="请输入形状强壮度排名" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | |
| | | </el-table-column> |
| | | <el-table-column prop="desc" label="形态记录"> |
| | | <template #default="{ row }"> |
| | | <el-input class="el-input-full" style="width: 100%;" :disabled="roleType!=4" v-model="row.desc" placeholder="请输入形态记录" |
| | | <el-input class="el-input-full" style="width: 100%;" :disabled="!editable" v-model="row.desc" placeholder="请输入形态记录" |
| | | @blur="handleDescBlur(row)" /> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="images" label="拍照上传" width="180"> |
| | | <el-table-column prop="images" label="拍照上传" width="160"> |
| | | <template #default="{ row }"> |
| | | <el-upload :file-list="row.images" :disabled="roleType!=4 || row.uploading" list-type="picture-card" |
| | | :on-preview="file => handlePreview(row, file)" |
| | | :on-remove="(file, fileList) => handleRemove(row, file, fileList)" |
| | | :on-success="(res, file, fileList) => handleUpload(row, file, fileList)" |
| | | :before-upload="file => beforeUpload(file, row)" |
| | | action="#" class="mini-upload"> |
| | | <template v-if="row.uploading"> |
| | | <i class="el-icon-loading"></i> |
| | | </template> |
| | | <template v-else> |
| | | <i class="el-icon-plus"></i> |
| | | </template> |
| | | <el-upload |
| | | class="custom-upload-row" |
| | | :ref="'uploadRef' + row.index" |
| | | action="#" |
| | | :file-list="[]" |
| | | :http-request="options => rowHandleUpload(options, row)" |
| | | :before-upload="file => rowBeforeUpload(file, row)" |
| | | :show-file-list="false" |
| | | :disabled="!editable || row.uploading" |
| | | accept="image/*" |
| | | :auto-upload="true" |
| | | > |
| | | <div class="upload-flex-box"> |
| | | <div |
| | | v-for="(img, idx) in row.images" |
| | | :key="img.uid || img.url" |
| | | class="upload-thumb" |
| | | @click.stop="handlePreview(row, img)" |
| | | > |
| | | <el-image |
| | | :src="getFullUrl(img.url)" |
| | | :preview-src-list="row.images.map(i => getFullUrl(i.url))" |
| | | :initial-index="idx" |
| | | fit="cover" |
| | | style="width: 100%; height: 100%;" |
| | | @click.stop.native="handlePreview(row, img)" |
| | | /> |
| | | <i class="el-icon-delete" v-if="editable" @click.stop="rowHandleRemove(row, img, row.images.filter(f => f !== img))"></i> |
| | | </div> |
| | | <div |
| | | v-if="editable" |
| | | class="upload-thumb upload-btn" |
| | | @click.stop="triggerUpload(row.index)" |
| | | > |
| | | <i class="el-icon-plus"></i> |
| | | </div> |
| | | </div> |
| | | </el-upload> |
| | | </template> |
| | | </el-table-column> |
| | |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | <div style="text-align: center;" v-if="roleType==4"> |
| | | <div style="text-align: center;" v-if="editable"> |
| | | <el-button type="primary" @click="handleOk" :disabled="hasUploading">保存</el-button> |
| | | </div> |
| | | </el-dialog> |
| | |
| | | |
| | | <script> |
| | | import moment from 'moment' |
| | | import { customUploadRequest, getFullUrl, getUploadedFilePath, normalizeFileList } from '@/utils/utils' |
| | | export default { |
| | | name: 'SlantRecordDialog', |
| | | props: { |
| | | visible: Boolean, |
| | | readOnly: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | editData: { |
| | | type: Object, |
| | | default: null |
| | |
| | | index: index + 1, |
| | | desc: record.desc || '', |
| | | time: record.time || this.getNowTime(), |
| | | images: record.images || [] |
| | | images: this.normalizeImages(record.images), |
| | | uploading: false |
| | | })) |
| | | while (this.tableData.length < 10) { |
| | | this.tableData.push({ |
| | |
| | | } |
| | | }, |
| | | visible(val) { |
| | | if (!val) { |
| | | if (val) { |
| | | if (!this.editData) { |
| | | this.reset() |
| | | this.tableData = Array.from({ length: 10 }, (_, i) => ({ |
| | | index: i + 1, |
| | | desc: '', |
| | | time: '', |
| | | images: [], |
| | | uploading: false |
| | | })) |
| | | } |
| | | } else { |
| | | this.reset() |
| | | } |
| | | } |
| | |
| | | } |
| | | }, |
| | | handleOk() { |
| | | if (!this.editable) return |
| | | this.$refs.formRef.validate(valid => { |
| | | if (!valid) return |
| | | |
| | |
| | | handleClose() { |
| | | this.$emit('update:visible', false) |
| | | }, |
| | | beforeUpload(file, row) { |
| | | row.uploading = true |
| | | return new Promise(resolve => { |
| | | const reader = new FileReader() |
| | | reader.onload = e => { |
| | | resolve() |
| | | rowBeforeUpload(file, row) { |
| | | if (!file.type || !file.type.startsWith('image/')) { |
| | | this.$message.error('只能上传图片文件') |
| | | return false |
| | | } |
| | | if (file.size > 10 * 1024 * 1024) { |
| | | this.$message.error('图片大小不能超过 10MB') |
| | | return false |
| | | } |
| | | return true |
| | | }, |
| | | rowHandleUpload(options, row) { |
| | | row.uploading = true; |
| | | const { file, onSuccess, onError } = options; |
| | | customUploadRequest({ |
| | | file, |
| | | onSuccess: (res) => { |
| | | const uploadedPath = getUploadedFilePath(res) |
| | | if (uploadedPath) { |
| | | const fileObj = { |
| | | name: file.name, |
| | | url: uploadedPath, |
| | | uid: new Date().getTime() |
| | | }; |
| | | if (!Array.isArray(row.images)) this.$set(row, 'images', []); |
| | | row.images.push(fileObj); |
| | | this.$set(row, 'images', [...row.images]); |
| | | this.$message.success('文件上传成功'); |
| | | onSuccess(res); |
| | | } else { |
| | | this.$message.error(res && res.msg ? res.msg : '上传接口未返回文件地址'); |
| | | onError(new Error('missing uploaded file path')); |
| | | } |
| | | row.uploading = false; |
| | | }, |
| | | onError: (err) => { |
| | | this.$message.error('文件上传失败'); |
| | | row.uploading = false; |
| | | onError(err); |
| | | } |
| | | reader.readAsDataURL(file) |
| | | }) |
| | | }); |
| | | }, |
| | | handleUpload(row, file, fileList) { |
| | | row.images = fileList.map(f => ({ ...f, url: f.url || URL.createObjectURL(f.raw) })) |
| | | row.uploading = false |
| | | }, |
| | | handleRemove(row, file, fileList) { |
| | | row.images = fileList |
| | | row.uploading = false |
| | | rowHandleRemove(row, file, fileList) { |
| | | row.images = fileList; |
| | | row.uploading = false; |
| | | }, |
| | | handlePreview(row, file) { |
| | | this.previewImg = file.url |
| | | this.previewVisible = true |
| | | }, |
| | | handleDescBlur(row) { |
| | | console.log(row) |
| | | if (row.desc) { |
| | | if (this.editable && row.desc) { |
| | | row.time = this.getNowTime() |
| | | this.$forceUpdate() |
| | | } |
| | | }, |
| | | triggerUpload(index) { |
| | | if (!this.editable) return |
| | | const uploadRef = this.$refs['uploadRef' + index]; |
| | | if (uploadRef && uploadRef.$el) { |
| | | const input = uploadRef.$el.querySelector('input[type=file]'); |
| | | if (input) input.click(); |
| | | } |
| | | }, |
| | | getFullUrl, |
| | | normalizeImages(images) { |
| | | return normalizeFileList(images) |
| | | } |
| | | }, |
| | | computed: { |
| | | editable() { |
| | | return [1, 4].includes(Number(this.roleType)) && !this.readOnly |
| | | }, |
| | | hasUploading() { |
| | | return this.tableData.some(row => row.uploading) |
| | | } |
| | |
| | | width: 100% !important; |
| | | } |
| | | |
| | | ::v-deep(.mini-upload){ |
| | | ::v-deep(.custom-upload-row .upload-flex-box) { |
| | | display: flex; |
| | | flex-wrap: wrap; |
| | | gap: 8px; |
| | | max-width: 140px; |
| | | } |
| | | |
| | | ::v-deep(.el-upload--picture-card) { |
| | | width: 40px !important; |
| | | height: 40px !important; |
| | | line-height: 40px !important; |
| | | } |
| | | |
| | | ::v-deep(.mini-upload .el-upload-list--picture-card .el-upload-list__item) { |
| | | width: 40px !important; |
| | | height: 40px !important; |
| | | } |
| | | |
| | | ::v-deep(.mini-upload .el-upload-list--picture-card .el-upload-list__item-thumbnail) { |
| | | width: 40px !important; |
| | | height: 40px !important; |
| | | object-fit: cover; |
| | | } |
| | | |
| | | ::v-deep(.mini-upload .el-upload-list--picture-card .el-upload-list__item-preview), |
| | | ::v-deep(.mini-upload .el-upload-list--picture-card .el-upload-list__item-delete) { |
| | | width: 18px; |
| | | height: 18px; |
| | | font-size: 14px; |
| | | } |
| | | |
| | | ::v-deep(.el-upload--picture-card) { |
| | | width: 40px !important; |
| | | height: 40px !important; |
| | | line-height: 40px !important; |
| | | .upload-thumb { |
| | | width: 40px; |
| | | height: 40px; |
| | | position: relative; |
| | | border: 1px solid #eee; |
| | | border-radius: 4px; |
| | | overflow: hidden; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | } |
| | | |
| | | ::v-deep(.mini-upload .el-upload--picture-card i.el-icon-plus) { |
| | | font-size: 18px; |
| | | /* 缩小icon */ |
| | | .upload-thumb img { |
| | | width: 100%; |
| | | height: 100%; |
| | | object-fit: cover; |
| | | } |
| | | .upload-thumb .el-icon-delete { |
| | | position: absolute; |
| | | top: 0px; |
| | | right: 0px; |
| | | font-size: 14px; |
| | | color: #f56c6c; |
| | | background: #fff; |
| | | border-radius: 50%; |
| | | cursor: pointer; |
| | | } |
| | | .upload-btn { |
| | | cursor: pointer; |
| | | background: #fafafa; |
| | | color: #999; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | width: 100%; |
| | | height: 100%; |
| | | } |
| | | </style> |