|
|
@@ -81,21 +81,111 @@
|
|
|
</el-col>
|
|
|
</el-row>
|
|
|
|
|
|
- <el-form-item label="附件上传">
|
|
|
- <el-upload
|
|
|
- ref="uploadRef"
|
|
|
- action=""
|
|
|
- :auto-upload="false"
|
|
|
- :before-upload="beforeUpload"
|
|
|
- :file-list="fileList"
|
|
|
- :limit="5"
|
|
|
- :multiple="true"
|
|
|
- :on-change="handleFileChange"
|
|
|
- :on-remove="handleFileRemove">
|
|
|
- <el-button size="small" type="primary">选择文件1</el-button>
|
|
|
- <div slot="tip" class="el-upload__tip">最多上传5个文件,单个文件不超过20MB</div>
|
|
|
- </el-upload>
|
|
|
- </el-form-item>
|
|
|
+ <el-row :gutter="20">
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="执行人" prop="opsUserId">
|
|
|
+ <el-select
|
|
|
+ v-model="form.opsUserId"
|
|
|
+ clearable
|
|
|
+ filterable
|
|
|
+ :loading="loadingUsers"
|
|
|
+ placeholder="请选择执行人"
|
|
|
+ remote
|
|
|
+ :remote-method="remoteFetchUserList"
|
|
|
+ reserve-keyword
|
|
|
+ style="width: 100%"
|
|
|
+ @change="handleUserChange"
|
|
|
+ @visible-change="handleUserVisibleChange">
|
|
|
+ <el-option v-for="u in userOptions" :key="u.value" :label="u.label" :value="u.value" />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="执行时间" prop="completeTime">
|
|
|
+ <el-date-picker
|
|
|
+ v-model="form.completeTime"
|
|
|
+ placeholder="请选择执行时间"
|
|
|
+ style="width: 100%"
|
|
|
+ type="date"
|
|
|
+ value-format="yyyy-MM-dd" />
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+
|
|
|
+ <el-row :gutter="20">
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="物流单号" prop="attribute1">
|
|
|
+ <el-input v-model="form.attribute1" placeholder="请输入物流单号" />
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="实际工作量" prop="actualWorkHour">
|
|
|
+ <el-input-number
|
|
|
+ v-model="form.actualWorkHour"
|
|
|
+ :min="0"
|
|
|
+ placeholder="请输入实际工作量"
|
|
|
+ :precision="1"
|
|
|
+ :step="0.5"
|
|
|
+ style="width: 100%" />
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+
|
|
|
+ <!-- 硬件发货附件:固定上传测试报告和发货清单 -->
|
|
|
+ <template v-if="form.deliveryEventType === '40'">
|
|
|
+ <el-form-item label="测试报告" prop="testReportFile">
|
|
|
+ <el-upload
|
|
|
+ action=""
|
|
|
+ :auto-upload="false"
|
|
|
+ :before-upload="beforeUpload"
|
|
|
+ :file-list="testReportFileList"
|
|
|
+ :limit="1"
|
|
|
+ :multiple="false"
|
|
|
+ :on-change="(file, fileList) => handleFixedFileChange('testReport', file, fileList)"
|
|
|
+ :on-remove="(file, fileList) => handleFixedFileRemove('testReport', file, fileList)">
|
|
|
+ <el-button size="small" type="primary">选择文件</el-button>
|
|
|
+ <div slot="tip" class="el-upload__tip">请上传测试报告文件</div>
|
|
|
+ </el-upload>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="发货清单" prop="deliveryListFile">
|
|
|
+ <el-upload
|
|
|
+ action=""
|
|
|
+ :auto-upload="false"
|
|
|
+ :before-upload="beforeUpload"
|
|
|
+ :file-list="deliveryListFileList"
|
|
|
+ :limit="1"
|
|
|
+ :multiple="false"
|
|
|
+ :on-change="(file, fileList) => handleFixedFileChange('deliveryList', file, fileList)"
|
|
|
+ :on-remove="(file, fileList) => handleFixedFileRemove('deliveryList', file, fileList)">
|
|
|
+ <el-button size="small" type="primary">选择文件</el-button>
|
|
|
+ <div slot="tip" class="el-upload__tip">请上传发货清单文件</div>
|
|
|
+ </el-upload>
|
|
|
+ </el-form-item>
|
|
|
+ </template>
|
|
|
+ <!-- 硬件安装附件:最多上传5个文件 -->
|
|
|
+ <template v-else-if="form.deliveryEventType === '41'">
|
|
|
+ <el-form-item label="附件上传">
|
|
|
+ <el-upload
|
|
|
+ ref="uploadRef"
|
|
|
+ action=""
|
|
|
+ :auto-upload="false"
|
|
|
+ :before-upload="beforeUpload"
|
|
|
+ :file-list="fileList"
|
|
|
+ :limit="5"
|
|
|
+ :multiple="true"
|
|
|
+ :on-change="handleFileChange"
|
|
|
+ :on-remove="handleFileRemove">
|
|
|
+ <el-button size="small" type="primary">选择文件</el-button>
|
|
|
+ <div slot="tip" class="el-upload__tip">最多上传5个文件,单个文件不超过20MB</div>
|
|
|
+ </el-upload>
|
|
|
+ </el-form-item>
|
|
|
+ </template>
|
|
|
+ <!-- 未选择类型时不显示 -->
|
|
|
+ <template v-else>
|
|
|
+ <el-form-item label="附件上传">
|
|
|
+ <el-button disabled size="small">请先选择事件类型</el-button>
|
|
|
+ </el-form-item>
|
|
|
+ </template>
|
|
|
</el-form>
|
|
|
|
|
|
<div slot="footer">
|
|
|
@@ -111,8 +201,10 @@
|
|
|
import { Editor, Toolbar } from '@wangeditor/editor-for-vue'
|
|
|
import deliveryProjectApi from '@/api/devops/deliveryProject'
|
|
|
import deliveryProjectEventApi from '@/api/devops/deliveryProjectEvent'
|
|
|
+ import userApi from '@/api/system/user'
|
|
|
import store from '@/store'
|
|
|
import { uploadFileToRichtextServer, uploadRichtextImage } from '@/utils/richtextUpload'
|
|
|
+ import { DEVOPS_DEV_DEPT_ID } from '@/config/devops.config'
|
|
|
import debounce from 'lodash/debounce'
|
|
|
|
|
|
export default {
|
|
|
@@ -173,6 +265,11 @@
|
|
|
feedbackSource: '',
|
|
|
feedbackReporter: '',
|
|
|
onSite: '20',
|
|
|
+ opsUserId: null,
|
|
|
+ opsUserName: '',
|
|
|
+ attribute1: '',
|
|
|
+ completeTime: '',
|
|
|
+ actualWorkHour: 0,
|
|
|
attachments: [],
|
|
|
},
|
|
|
rules: {
|
|
|
@@ -189,6 +286,13 @@
|
|
|
deliveryEventTypeOptions: [],
|
|
|
feedbackSourceOptions: [],
|
|
|
onSiteOptions: [],
|
|
|
+ userOptions: [],
|
|
|
+ loadingUsers: false,
|
|
|
+ testReportFileList: [],
|
|
|
+ deliveryListFileList: [],
|
|
|
+ testReportUploadFiles: [],
|
|
|
+ deliveryListUploadFiles: [],
|
|
|
+ shipmentExtraAttachments: [],
|
|
|
}
|
|
|
},
|
|
|
computed: {
|
|
|
@@ -216,11 +320,17 @@
|
|
|
this.remoteSearchProject = debounce((query) => {
|
|
|
this.searchAllProjectsByStatus(query)
|
|
|
}, 300)
|
|
|
+ this.remoteFetchUserList = debounce((query) => {
|
|
|
+ this.fetchUserList(query)
|
|
|
+ }, 300)
|
|
|
},
|
|
|
beforeDestroy() {
|
|
|
if (this.remoteSearchProject && this.remoteSearchProject.cancel) {
|
|
|
this.remoteSearchProject.cancel()
|
|
|
}
|
|
|
+ if (this.remoteFetchUserList && this.remoteFetchUserList.cancel) {
|
|
|
+ this.remoteFetchUserList.cancel()
|
|
|
+ }
|
|
|
if (this.editor) {
|
|
|
this.editor.destroy()
|
|
|
this.editor = null
|
|
|
@@ -256,6 +366,11 @@
|
|
|
feedbackSource: data.feedbackSource || data.feedback_source || '',
|
|
|
feedbackReporter: data.feedbackReporter || data.feedback_reporter || '',
|
|
|
onSite: data.onSite || data.on_site || '20',
|
|
|
+ opsUserId: data.opsUserId || data.ops_user_id || null,
|
|
|
+ opsUserName: data.opsUserName || data.ops_user_name || '',
|
|
|
+ attribute1: data.attribute1 || '',
|
|
|
+ completeTime: data.completeTime || data.complete_time || '',
|
|
|
+ actualWorkHour: data.actualWorkHour || data.actual_work_hour || 0,
|
|
|
attachments: data.attachments || [],
|
|
|
}
|
|
|
},
|
|
|
@@ -266,6 +381,54 @@
|
|
|
projectName: project.projectName || project.project_name || '',
|
|
|
}
|
|
|
},
|
|
|
+ createExistingFileItem(item = {}) {
|
|
|
+ return {
|
|
|
+ name: item.fileName || item.name || '',
|
|
|
+ url: item.fileUrl || item.url || '',
|
|
|
+ ...item,
|
|
|
+ }
|
|
|
+ },
|
|
|
+ splitShipmentAttachments(attachments = []) {
|
|
|
+ const testReportMatchers = ['测试报告', 'test report', 'test_report', 'testreport']
|
|
|
+ const deliveryListMatchers = ['发货清单', 'delivery list', 'delivery_list', 'packing list']
|
|
|
+ const result = {
|
|
|
+ testReportFileList: [],
|
|
|
+ deliveryListFileList: [],
|
|
|
+ shipmentExtraAttachments: [],
|
|
|
+ }
|
|
|
+ const remaining = []
|
|
|
+
|
|
|
+ attachments.forEach((attachment) => {
|
|
|
+ const normalizedAttachment = this.normalizeAttachmentItem(attachment)
|
|
|
+ const fileItem = this.createExistingFileItem(normalizedAttachment)
|
|
|
+ const fileName = String(normalizedAttachment.fileName || '').toLowerCase()
|
|
|
+
|
|
|
+ if (!result.testReportFileList.length && testReportMatchers.some((matcher) => fileName.includes(matcher))) {
|
|
|
+ result.testReportFileList = [fileItem]
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ if (
|
|
|
+ !result.deliveryListFileList.length &&
|
|
|
+ deliveryListMatchers.some((matcher) => fileName.includes(matcher))
|
|
|
+ ) {
|
|
|
+ result.deliveryListFileList = [fileItem]
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ remaining.push(fileItem)
|
|
|
+ })
|
|
|
+
|
|
|
+ if (!result.testReportFileList.length && remaining.length) {
|
|
|
+ result.testReportFileList = [remaining.shift()]
|
|
|
+ }
|
|
|
+ if (!result.deliveryListFileList.length && remaining.length) {
|
|
|
+ result.deliveryListFileList = [remaining.shift()]
|
|
|
+ }
|
|
|
+
|
|
|
+ result.shipmentExtraAttachments = remaining.map((item) => this.normalizeAttachmentItem(item))
|
|
|
+ return result
|
|
|
+ },
|
|
|
async ensureProjectOption(projectId) {
|
|
|
if (!projectId) return
|
|
|
|
|
|
@@ -352,16 +515,33 @@
|
|
|
initForm() {
|
|
|
this.form = this.normalizeFormData(this.data)
|
|
|
this.searchAllProjectsByStatus('')
|
|
|
- if (this.form.attachments && this.form.attachments.length) {
|
|
|
- this.fileList = this.form.attachments.map((item) => ({
|
|
|
- name: item.fileName,
|
|
|
- url: item.fileUrl,
|
|
|
- ...item,
|
|
|
- }))
|
|
|
- } else {
|
|
|
+ if (this.form.deliveryEventType === '40') {
|
|
|
+ const shipmentAttachments = this.splitShipmentAttachments(this.form.attachments || [])
|
|
|
+ this.testReportFileList = shipmentAttachments.testReportFileList
|
|
|
+ this.deliveryListFileList = shipmentAttachments.deliveryListFileList
|
|
|
+ this.shipmentExtraAttachments = shipmentAttachments.shipmentExtraAttachments
|
|
|
this.fileList = []
|
|
|
+ } else {
|
|
|
+ this.shipmentExtraAttachments = []
|
|
|
+ this.testReportFileList = []
|
|
|
+ this.deliveryListFileList = []
|
|
|
+ this.fileList = (this.form.attachments || []).map((item) => this.createExistingFileItem(item))
|
|
|
}
|
|
|
this.uploadFiles = []
|
|
|
+ this.testReportUploadFiles = []
|
|
|
+ this.deliveryListUploadFiles = []
|
|
|
+ // 加载运维人员列表并确保当前执行人可选
|
|
|
+ this.fetchUserList().then(() => {
|
|
|
+ if (this.form.opsUserId && this.form.opsUserName) {
|
|
|
+ const exists = this.userOptions.some((u) => u.value === this.form.opsUserId)
|
|
|
+ if (!exists) {
|
|
|
+ this.userOptions.push({
|
|
|
+ value: this.form.opsUserId,
|
|
|
+ label: this.form.opsUserName,
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }
|
|
|
+ })
|
|
|
},
|
|
|
resetForm() {
|
|
|
const userName = store.getters['user/username']
|
|
|
@@ -376,11 +556,21 @@
|
|
|
feedbackSource: '',
|
|
|
feedbackReporter: nickName || userName || '',
|
|
|
onSite: '20',
|
|
|
+ opsUserId: null,
|
|
|
+ opsUserName: '',
|
|
|
+ attribute1: '',
|
|
|
+ completeTime: '',
|
|
|
+ actualWorkHour: 0,
|
|
|
attachments: [],
|
|
|
}
|
|
|
this.searchAllProjectsByStatus('')
|
|
|
this.fileList = []
|
|
|
this.uploadFiles = []
|
|
|
+ this.testReportFileList = []
|
|
|
+ this.testReportUploadFiles = []
|
|
|
+ this.deliveryListFileList = []
|
|
|
+ this.deliveryListUploadFiles = []
|
|
|
+ this.shipmentExtraAttachments = []
|
|
|
if (this.editor) {
|
|
|
this.editor.clear()
|
|
|
}
|
|
|
@@ -390,6 +580,51 @@
|
|
|
this.resetForm()
|
|
|
this.$emit('update:visible', false)
|
|
|
},
|
|
|
+ async fetchUserList(search) {
|
|
|
+ this.loadingUsers = true
|
|
|
+ try {
|
|
|
+ const payload = { deptId: DEVOPS_DEV_DEPT_ID, pageNum: 1, pageSize: 999 }
|
|
|
+ if (search) payload.keyWords = search
|
|
|
+ const res = await userApi.getList(payload)
|
|
|
+ const list = res.data?.list || []
|
|
|
+ this.userOptions = list.map((u) => ({
|
|
|
+ value: u.userId ?? u.user_id ?? u.id ?? null,
|
|
|
+ label: u.nickName ?? u.nick_name ?? u.name ?? '',
|
|
|
+ }))
|
|
|
+ } catch (error) {
|
|
|
+ console.error('获取用户列表失败:', error)
|
|
|
+ this.userOptions = []
|
|
|
+ } finally {
|
|
|
+ this.loadingUsers = false
|
|
|
+ }
|
|
|
+ },
|
|
|
+ handleUserChange(val) {
|
|
|
+ const user = this.userOptions.find((u) => u.value === val)
|
|
|
+ this.form.opsUserName = user ? user.label : ''
|
|
|
+ },
|
|
|
+ handleUserVisibleChange(visible) {
|
|
|
+ if (visible && !this.loadingUsers && !this.userOptions.length) {
|
|
|
+ this.fetchUserList('')
|
|
|
+ }
|
|
|
+ },
|
|
|
+ handleFixedFileChange(type, file, fileList) {
|
|
|
+ if (type === 'testReport') {
|
|
|
+ this.testReportFileList = fileList
|
|
|
+ this.testReportUploadFiles = fileList.filter((f) => f.raw).map((f) => f.raw)
|
|
|
+ } else if (type === 'deliveryList') {
|
|
|
+ this.deliveryListFileList = fileList
|
|
|
+ this.deliveryListUploadFiles = fileList.filter((f) => f.raw).map((f) => f.raw)
|
|
|
+ }
|
|
|
+ },
|
|
|
+ handleFixedFileRemove(type, file, fileList) {
|
|
|
+ if (type === 'testReport') {
|
|
|
+ this.testReportFileList = fileList
|
|
|
+ this.testReportUploadFiles = fileList.filter((f) => f.raw).map((f) => f.raw)
|
|
|
+ } else if (type === 'deliveryList') {
|
|
|
+ this.deliveryListFileList = fileList
|
|
|
+ this.deliveryListUploadFiles = fileList.filter((f) => f.raw).map((f) => f.raw)
|
|
|
+ }
|
|
|
+ },
|
|
|
handleFileChange(file, fileList) {
|
|
|
this.fileList = fileList
|
|
|
this.uploadFiles = fileList.filter((f) => f.raw).map((f) => f.raw)
|
|
|
@@ -416,9 +651,15 @@
|
|
|
getExistingAttachments() {
|
|
|
return this.fileList.filter((item) => !item.raw).map((item) => this.normalizeAttachmentItem(item))
|
|
|
},
|
|
|
- async uploadAttachments() {
|
|
|
+ getExistingShipmentAttachments() {
|
|
|
+ const fixedAttachments = [...this.testReportFileList, ...this.deliveryListFileList]
|
|
|
+ .filter((item) => item && !item.raw)
|
|
|
+ .map((item) => this.normalizeAttachmentItem(item))
|
|
|
+ return [...fixedAttachments, ...this.shipmentExtraAttachments]
|
|
|
+ },
|
|
|
+ async uploadTypeAttachments(files) {
|
|
|
const uploadedFiles = []
|
|
|
- for (const file of this.uploadFiles) {
|
|
|
+ for (const file of files) {
|
|
|
try {
|
|
|
const result = await uploadFileToRichtextServer(file)
|
|
|
uploadedFiles.push(
|
|
|
@@ -434,7 +675,20 @@
|
|
|
throw err
|
|
|
}
|
|
|
}
|
|
|
- return [...this.getExistingAttachments(), ...uploadedFiles]
|
|
|
+ return uploadedFiles
|
|
|
+ },
|
|
|
+ async uploadAttachments() {
|
|
|
+ // 硬件发货类型:上传固定两个文件
|
|
|
+ if (this.form.deliveryEventType === '40') {
|
|
|
+ const uploadedFiles = [...this.getExistingShipmentAttachments()]
|
|
|
+ const testReportFiles = await this.uploadTypeAttachments(this.testReportUploadFiles)
|
|
|
+ const deliveryListFiles = await this.uploadTypeAttachments(this.deliveryListUploadFiles)
|
|
|
+ return [...uploadedFiles, ...testReportFiles, ...deliveryListFiles]
|
|
|
+ }
|
|
|
+ // 硬件安装类型:上传通用文件
|
|
|
+ const uploadedFiles = [...this.getExistingAttachments()]
|
|
|
+ const newFiles = await this.uploadTypeAttachments(this.uploadFiles)
|
|
|
+ return [...uploadedFiles, ...newFiles]
|
|
|
},
|
|
|
async handleSubmit() {
|
|
|
this.$refs.form.validate(async (valid) => {
|
|
|
@@ -448,6 +702,7 @@
|
|
|
const submitData = {
|
|
|
...this.form,
|
|
|
projectId: this.form.projectId ? parseInt(this.form.projectId) : null,
|
|
|
+ opsUserId: this.form.opsUserId ? parseInt(this.form.opsUserId) : 0,
|
|
|
attachments: uploadedAttachments,
|
|
|
}
|
|
|
|