| | |
| | | |
| | | <!-- 新建工单对话框 --> |
| | | <el-dialog v-model="dialogVisible" title="新建工单" width="70%" :close-on-click-modal="false" @close="resetForm"> |
| | | <el-form :model="form" :rules="rules" ref="form" label-width="100px" class="create-ticket-form"> |
| | | <!-- 基本信息 --> |
| | | <el-form :model="form" :rules="rules" ref="form" label-width="90px" class="create-ticket-form"> |
| | | <div class="form-section"> |
| | | <div class="section-title">基本信息</div> |
| | | <el-row :gutter="12"> |
| | | <el-row :gutter="16"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="工单名称" prop="name"> |
| | | <el-input v-model="form.name" placeholder="请输入工单名称"></el-input> |
| | |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="12"> |
| | | |
| | | <el-row :gutter="16"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="所属部门" prop="department"> |
| | | <el-select v-model="form.department" placeholder="请选择所属部门" @change="handleDepartmentChange" |
| | | class="full-width"> |
| | | <el-select v-model="form.department" placeholder="请选择所属部门" @change="handleDepartmentChange" class="full-width"> |
| | | <el-option v-for="dept in departments" :key="dept.value" :label="dept.label" :value="dept.value" /> |
| | | </el-select> |
| | | </el-form-item> |
| | |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="12"> |
| | | |
| | | <el-row :gutter="16"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="关联算法" prop="algorithm"> |
| | | <el-select v-model="form.algorithm" placeholder="请选择关联算法" class="full-width"> |
| | |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | </div> |
| | | |
| | | <!-- 修改位置信息部分 --> |
| | | <div class="form-section"> |
| | | <div class="section-title">位置信息</div> |
| | | <el-row :gutter="12"> |
| | | <el-col :span="24"> |
| | | <el-form-item label="地图选址" prop="location"> |
| | | <div class="map-select"> |
| | | <!-- 替换地图为按钮 --> |
| | | <avue-input-map v-model="form.location" :params="mapParams" @change="handleLocationChange" |
| | | type="button"> |
| | | <el-button type="primary" plain> |
| | | <i class="el-icon-map-location"></i> 选择位置 |
| | | <el-col :span="12"> |
| | | <el-form-item label="选择位置" prop="location"> |
| | | <div class="location-wrapper"> |
| | | <avue-input-map v-model="form.location" :params="mapParams" @change="handleLocationChange" type="button"> |
| | | <el-button type="primary" plain class="map-button"> |
| | | <i class="el-icon-map-location"></i> 地图选点 |
| | | </el-button> |
| | | </avue-input-map> |
| | | |
| | | <!-- 添加位置信息显示 --> |
| | | <div v-if="form.location && form.location.length >= 2" class="selected-location"> |
| | | <p>已选位置: {{ formatLocation(form.location) }}</p> |
| | | <p>详细地址: {{ form.address || '获取中...' }}</p> |
| | | </div> |
| | | <div v-if="form.location?.length >= 2" class="location-text">{{ form.address || '获取地址中...' }}</div> |
| | | </div> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | </div> |
| | | |
| | | <!-- 工单内容 --> |
| | | <div class="form-section"> |
| | | <div class="section-title">工单内容</div> |
| | | <el-form-item label="工单描述" prop="content"> |
| | | <el-input type="textarea" v-model="form.content" rows="3" placeholder="请输入工单内容描述" resize="none"></el-input> |
| | | </el-form-item> |
| | | <el-form-item label="附件图片" class="upload-item"> |
| | | <el-upload ref="upload" :action="'#'" :auto-upload="false" list-type="picture-card" |
| | | :on-change="handleFileChange" :on-remove="handleUploadRemove" :before-upload="beforeUpload" |
| | | :file-list="form.photos" :limit="1" accept="image/*"> |
| | | <i class="el-icon-plus"></i> |
| | | </el-upload> |
| | | <div class="el-upload__tip">支持jpg/png格式图片,最多1张,单张不超过5MB</div> |
| | | </el-form-item> |
| | | <el-row :gutter="16"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="工单内容" prop="content"> |
| | | <el-input type="textarea" v-model="form.content" :rows="4" placeholder="请输入工单内容描述"></el-input> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="附件图片" prop="photos" class="upload-wrapper"> |
| | | <el-upload ref="upload" |
| | | :action="'#'" |
| | | :auto-upload="false" |
| | | list-type="picture-card" |
| | | :on-change="handleFileChange" |
| | | :on-remove="handleUploadRemove" |
| | | :before-upload="beforeUpload" |
| | | :file-list="form.photos" |
| | | :limit="1" |
| | | accept="image/*" |
| | | class="uploader" |
| | | > |
| | | <i class="el-icon-plus"></i> |
| | | </el-upload> |
| | | <div class="upload-tip">支持jpg/png格式图片,单张不超过5MB</div> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | </div> |
| | | </el-form> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button size="small" @click="dialogVisible = false">取 消</el-button> |
| | | <el-button size="small" type="info" @click="saveDraft">存草稿</el-button> |
| | | <el-button size="small" type="primary" @click="submitForm">提 交</el-button> |
| | | <el-button type="danger" @click="submitForm">确认提交</el-button> |
| | | <el-button type="primary" plain @click="saveDraft">存草稿</el-button> |
| | | <el-button @click="dialogVisible = false">取 消</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | |
| | | department: [{ required: true, message: '请选择所属部门', trigger: 'change' }], |
| | | handler: [{ required: true, message: '请选择处理人员', trigger: 'change' }], |
| | | content: [{ required: true, message: '请输入工单内容', trigger: 'blur' }], |
| | | algorithm: [{ required: true, message: '请选择关联算法', trigger: 'change' }], |
| | | location: [{ |
| | | validator: (rule, value, callback) => { |
| | | if (!value || value.length < 2) { |
| | | callback(new Error('请选择位置信息')); |
| | | } else { |
| | | callback(); |
| | | } |
| | | }, |
| | | trigger: 'change' |
| | | }], |
| | | photos: [{ |
| | | validator: (rule, value, callback) => { |
| | | if (!this.form.photos || this.form.photos.length === 0) { |
| | | callback(new Error('请上传工单图片')); |
| | | } else { |
| | | callback(); |
| | | } |
| | | }, |
| | | trigger: 'change' |
| | | }], |
| | | }, |
| | | departmentUsers: {}, |
| | | loading: false, |
| | |
| | | }, // 新增:派发表单验证规则 |
| | | stepInfos: [], // 新增:存储步骤信息 |
| | | fixedStatuses: ["2", "0", "3", "4", "5"], // 固定的五个状态 |
| | | userNameToIdMap: {}, // 新增用户名到ID的映射 |
| | | }; |
| | | }, |
| | | created() { |
| | |
| | | label: item.dict_value, // 修改为 label |
| | | value: item.dict_key, // 修改为 value |
| | | })); |
| | | |
| | | // 构建用户ID和名称的映射关系 |
| | | this.userNameToIdMap = {}; |
| | | dept_data.forEach(dept => { |
| | | (dept.user_data || []).forEach(user => { |
| | | this.userNameToIdMap[user.name] = user.id; |
| | | }); |
| | | }); |
| | | |
| | | console.log('用户名到ID的映射:', this.userNameToIdMap); |
| | | } catch (error) { |
| | | console.error('获取下拉框数据失败:', error); |
| | | this.$message.error('加载下拉框数据失败'); |
| | |
| | | }, |
| | | |
| | | async submitForm() { |
| | | this.$refs.form.validate(async (valid) => { |
| | | if (valid) { |
| | | if (!this.form.location || this.form.location.length < 2) { |
| | | this.$message.warning('请在地图上选择位置'); |
| | | return; |
| | | } |
| | | |
| | | try { |
| | | const submitData = { |
| | | eventName: this.form.name, |
| | | content: this.form.content, // 传递工单内容 |
| | | workType: "1", |
| | | longitude: String(this.form.location[0]), |
| | | latitude: String(this.form.location[1]), |
| | | address: this.form.address, |
| | | eventDictKey: this.form.type, |
| | | aiType: this.form.algorithm, |
| | | updateUser: this.form.handler, |
| | | createDept: this.form.department, |
| | | isDraft: 0 |
| | | }; |
| | | |
| | | // 如果是编辑状态,添加 id |
| | | if (this.form.id) { |
| | | submitData.id = this.form.id; |
| | | } |
| | | |
| | | // 获取文件对象 |
| | | let file = null; |
| | | if (this.form.photos && this.form.photos.length > 0) { |
| | | file = this.form.photos[0].raw; |
| | | } |
| | | |
| | | const response = await createTicket(submitData, file); |
| | | if (response.data.code === 0) { |
| | | this.$message.success('工单创建成功'); |
| | | this.dialogVisible = false; |
| | | this.fetchTableData(); |
| | | } else { |
| | | throw new Error(response.data.msg || '创建失败'); |
| | | } |
| | | } catch (error) { |
| | | console.error('提交失败:', error); |
| | | this.$message.error(error.message || '工单创建失败,请稍后重试'); |
| | | } |
| | | try { |
| | | // 提交时需要完整验证 |
| | | await this.$refs.form.validate(); |
| | | |
| | | // 验证位置信息 |
| | | if (!this.form.location || this.form.location.length < 2) { |
| | | this.$message.warning('请在地图上选择位置'); |
| | | return; |
| | | } |
| | | }); |
| | | |
| | | // 验证图片上传 |
| | | if (!this.form.photos || this.form.photos.length === 0) { |
| | | this.$message.warning('请上传工单图片'); |
| | | return; |
| | | } |
| | | |
| | | const submitData = { |
| | | eventName: this.form.name, |
| | | content: this.form.content, |
| | | workType: "1", |
| | | longitude: String(this.form.location[0]), |
| | | latitude: String(this.form.location[1]), |
| | | address: this.form.address, |
| | | eventDictKey: this.form.type, |
| | | aiType: this.form.algorithm, |
| | | updateUser: this.form.handler, |
| | | createDept: this.form.department, |
| | | isDraft: 0 |
| | | }; |
| | | |
| | | if (this.form.id) { |
| | | submitData.id = this.form.id; |
| | | } |
| | | |
| | | // 获取文件对象 |
| | | const file = this.form.photos[0].raw; |
| | | if (!file) { |
| | | this.$message.warning('图片文件无效,请重新上传'); |
| | | return; |
| | | } |
| | | |
| | | const response = await createTicket(submitData, file); |
| | | if (response.data.code === 0) { |
| | | this.$message.success('工单创建成功'); |
| | | this.dialogVisible = false; |
| | | this.fetchTableData(); |
| | | } else { |
| | | throw new Error(response.data.msg || '创建失败'); |
| | | } |
| | | } catch (error) { |
| | | console.error('提交失败:', error); |
| | | if (error.message.includes('验证未通过')) { |
| | | this.$message.warning('请填写完整的工单信息'); |
| | | } else { |
| | | this.$message.error(error.message || '工单创建失败,请稍后重试'); |
| | | } |
| | | } |
| | | }, |
| | | |
| | | async saveDraft() { |
| | |
| | | const submitData = { |
| | | id: this.form.id, |
| | | eventName: this.form.name || undefined, |
| | | content: this.form.content || undefined, // 传递工单内容 |
| | | content: this.form.content || undefined, |
| | | workType: "1", |
| | | longitude: this.form.location?.[0] ? String(this.form.location[0]) : undefined, |
| | | latitude: this.form.location?.[1] ? String(this.form.location[1]) : undefined, |
| | |
| | | isDraft: 1 |
| | | }; |
| | | |
| | | // 确保即使为空字符串也会传入 content |
| | | if (this.form.content !== undefined) { |
| | | submitData.content = this.form.content; |
| | | } |
| | | |
| | | // 如果是编辑状态,添加 id |
| | | if (this.form.id) { |
| | | submitData.id = this.form.id; |
| | | // 草稿时也至少需要工单名称 |
| | | if (!submitData.eventName) { |
| | | this.$message.warning('请至少输入工单名称'); |
| | | return; |
| | | } |
| | | |
| | | // 过滤掉所有 undefined 的字段 |
| | |
| | | submitData[key] === undefined && delete submitData[key] |
| | | ); |
| | | |
| | | // 获取文件对象 |
| | | let file = null; |
| | | if (this.form.photos && this.form.photos.length > 0) { |
| | | file = this.form.photos[0].raw; |
| | | } |
| | | |
| | | const response = await createTicket(submitData, file); // 使用 createTicket 而不是 flowEvent |
| | | const response = await createTicket(submitData, file); |
| | | if (response.data.code === 0) { |
| | | this.$message.success('草稿保存成功'); |
| | | this.dialogVisible = false; |
| | |
| | | if (this.currentDetail.status !== 3) { |
| | | this.$message.warning('只有处理中状态的工单可以提交处理详情'); |
| | | return; |
| | | |
| | | } |
| | | |
| | | try { |
| | |
| | | |
| | | // 添加编辑方法 |
| | | handleEdit(row) { |
| | | // 从原始数据中获取 id 和 dept_id |
| | | const originalDeptId = row.department?.value || row.dept_id; |
| | | console.log('编辑原始数据:', row); |
| | | |
| | | // 尝试从row.dept_id或通过部门名称查找对应的部门ID |
| | | let deptId = row.dept_id; // 优先使用原始数据中的dept_id |
| | | |
| | | // 如果没有dept_id,则通过部门名称查找 |
| | | if (!deptId) { |
| | | // 输出所有可用的部门数据用于调试 |
| | | console.log('可用部门列表:', this.departments); |
| | | console.log('当前部门名称:', row.department); |
| | | |
| | | const deptInfo = this.departments.find(dept => |
| | | dept.label && dept.label.trim() === row.department.trim() |
| | | ); |
| | | deptId = deptInfo?.value; |
| | | } |
| | | |
| | | // 获取工单类型值 - 从types中找到匹配的值 |
| | | const typeValue = this.types.find(t => t.label === row.type)?.value || row.event_dict_key; |
| | | |
| | | // 获取处理人ID - 使用userNameToIdMap映射 |
| | | const handlerId = this.userNameToIdMap[row.handler] || row.handler; |
| | | |
| | | console.log('数据映射:', { |
| | | 部门名称: row.department, |
| | | 找到的部门ID: deptId, |
| | | 原始处理人: row.handler, |
| | | 处理人ID: handlerId, |
| | | 原始工单类型: row.type, |
| | | 映射后类型值: typeValue |
| | | }); |
| | | |
| | | this.form = { |
| | | id: row.id, // 保存原始 id |
| | | id: row.id, |
| | | name: row.orderName, |
| | | type: row.event_dict_key, // 使用原始 key 值 |
| | | department: originalDeptId, |
| | | handler: row.handler, |
| | | algorithm: row.content, |
| | | type: typeValue, |
| | | department: deptId, |
| | | handler: handlerId, // 使用映射后的处理人ID |
| | | algorithm: row.aiType, |
| | | location: row.location, |
| | | address: row.address, |
| | | content: row.content, |
| | | photos: [], |
| | | content: row.content, |
| | | }; |
| | | |
| | | // 如果有图片,添加到表单中 |
| | | if (row.photo_url) { |
| | | this.form.photos = [{ |
| | |
| | | url: row.photo_url |
| | | }]; |
| | | } |
| | | |
| | | this.dialogVisible = true; |
| | | }, |
| | | |
| | | // 添加删除方法 |
| | | handleDelete(row) { |
| | | this.$confirm('确认删除该工单?', '提示', { |
| | |
| | | } |
| | | |
| | | .create-ticket-form { |
| | | padding: 10px; |
| | | padding: 20px 10px; |
| | | |
| | | .form-section { |
| | | margin-bottom: 15px; |
| | | background: #f8f9fa; |
| | | background-color: #fff; |
| | | border-radius: 4px; |
| | | padding: 12px; |
| | | |
| | | .section-title { |
| | | font-size: 14px; |
| | | font-weight: 500; |
| | | color: #303133; |
| | | margin-bottom: 12px; |
| | | padding-left: 8px; |
| | | border-left: 3px solid #409EFF; |
| | | } |
| | | .el-row { |
| | | margin-bottom: 16px; |
| | | |
| | | .el-form-item { |
| | | margin-bottom: 12px; |
| | | &:last-child { |
| | | margin-bottom: 0; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .map-select { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 10px; |
| | | .location-wrapper { |
| | | .map-button { |
| | | width: 100%; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | |
| | | i { |
| | | margin-right: 4px; |
| | | } |
| | | } |
| | | |
| | | .location-text { |
| | | margin-top: 8px; |
| | | padding: 8px 12px; |
| | | background-color: #f5f7fa; |
| | | border-radius: 4px; |
| | | color: #606266; |
| | | font-size: 13px; |
| | | line-height: 1.4; |
| | | } |
| | | } |
| | | |
| | | .upload-item { |
| | | margin-bottom: 0; |
| | | } |
| | | .upload-wrapper { |
| | | .uploader { |
| | | :deep(.el-upload--picture-card) { |
| | | width: 120px; |
| | | height: 120px; |
| | | line-height: 128px; |
| | | } |
| | | |
| | | .ticket-upload { |
| | | :deep(.el-upload--picture-card) { |
| | | width: 100px; |
| | | height: 100px; |
| | | line-height: 100px; |
| | | margin: 0 8px 8px 0; |
| | | :deep(.el-upload-list__item) { |
| | | width: 120px; |
| | | height: 120px; |
| | | } |
| | | } |
| | | |
| | | :deep(.el-upload-list--picture-card .el-upload-list__item) { |
| | | width: 100px; |
| | | height: 100px; |
| | | margin: 0 8px 8px 0; |
| | | .upload-tip { |
| | | font-size: 12px; |
| | | color: #909399; |
| | | line-height: 1.4; |
| | | margin-top: 8px; |
| | | } |
| | | } |
| | | |
| | | .el-upload__tip { |
| | | font-size: 12px; |
| | | color: #909399; |
| | | line-height: 1.2; |
| | | margin-top: 5px; |
| | | .el-form-item { |
| | | margin-bottom: 18px; |
| | | |
| | | &:last-child { |
| | | margin-bottom: 0; |
| | | } |
| | | } |
| | | |
| | | :deep(.el-form-item__label) { |
| | | font-weight: 500; |
| | | color: #606266; |
| | | } |
| | | |
| | | :deep(.el-input__inner) { |
| | | height: 36px; |
| | | line-height: 36px; |
| | | } |
| | | |
| | | :deep(.el-textarea__inner) { |
| | | padding: 8px 12px; |
| | | } |
| | | } |
| | | |
| | | .dialog-footer { |
| | | text-align: right; |
| | | padding-top: 10px; |
| | | padding-top: 16px; |
| | | border-top: 1px solid #ebeef5; |
| | | |
| | | .el-button + .el-button { |
| | | margin-left: 12px; |
| | | } |
| | | |
| | | .el-button { |
| | | padding: 9px 20px; |
| | | |
| | | &:last-child { |
| | | margin-left: 12px; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .map-container { |