<template>
|
<el-dialog
|
v-model="dialogVisible"
|
title="新建工单"
|
width="70%"
|
:close-on-click-modal="false"
|
@close="handleClose"
|
>
|
<el-form
|
:model="form"
|
:rules="rules"
|
ref="formRef"
|
label-width="90px"
|
class="create-ticket-form"
|
@submit.prevent
|
>
|
<div class="form-section">
|
<el-row :gutter="16">
|
<el-col :span="12">
|
<el-form-item label="工单名称" prop="name">
|
<el-input
|
v-model="form.name"
|
placeholder="请输入工单名称"
|
clearable
|
/>
|
</el-form-item>
|
</el-col>
|
<el-col :span="12">
|
<el-form-item label="事件位置" prop="location">
|
<div class="location-wrapper">
|
<avue-input-map
|
v-model="form.location"
|
:clearable="false"
|
: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>
|
</el-form-item>
|
</el-col>
|
|
</el-row>
|
|
<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"
|
clearable
|
>
|
<el-option
|
v-for="dept in departments"
|
:key="dept.value"
|
:label="dept.label"
|
:value="dept.value"
|
/>
|
</el-select>
|
</el-form-item>
|
</el-col>
|
<el-col :span="12">
|
<el-form-item label="处理人员" prop="handler">
|
<el-select
|
v-model="form.handler"
|
placeholder="请先选择处理人员"
|
:disabled="!form.department"
|
class="full-width"
|
clearable
|
>
|
<el-option
|
v-for="user in availableHandlers"
|
:key="user.id"
|
:label="user.name"
|
:value="user.id"
|
/>
|
</el-select>
|
</el-form-item>
|
</el-col>
|
</el-row>
|
|
<el-row :gutter="16">
|
<el-col :span="12">
|
<el-form-item label="工单类型" prop="type">
|
<el-select
|
@change="handleTypeChange"
|
v-model="form.type"
|
placeholder="请选择工单类型"
|
class="full-width"
|
clearable
|
>
|
<el-option
|
v-for="item in types"
|
:key="item.value"
|
:label="item.label"
|
:value="item.value"
|
/>
|
</el-select>
|
</el-form-item>
|
</el-col>
|
|
<el-col :span="12">
|
<el-form-item label="关联算法" prop="algorithm">
|
<el-select
|
v-model="form.algorithm"
|
multiple
|
placeholder="请选择关联算法"
|
class="full-width"
|
:disabled="!form.type"
|
clearable
|
>
|
<el-option
|
v-for="item in availableAlgorithms"
|
:key="item.value"
|
:label="item.label"
|
:value="item.value"
|
/>
|
</el-select>
|
</el-form-item>
|
</el-col>
|
|
</el-row>
|
|
<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-form-item>
|
</el-col>
|
<el-col :span="12">
|
<el-form-item label="事件图片" prop="photos" required class="upload-wrapper">
|
<el-upload
|
ref="uploadRef"
|
: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="create-upload"
|
>
|
<template v-if="form.photos.length < 1">
|
<div class="el-icon-plus">
|
<span>+</span>
|
</div>
|
</template>
|
</el-upload>
|
<div class="upload-tip">需上传含有地址信息的照片(jpg、jpeg、png),且不超过5M</div>
|
</el-form-item>
|
</el-col>
|
</el-row>
|
</div>
|
</el-form>
|
|
<template #footer>
|
<div class="dialog-footer-new">
|
<el-button
|
type="danger"
|
:loading="submitLoading"
|
@click="handleSubmit"
|
icon="el-icon-position"
|
>
|
发布
|
</el-button>
|
|
<el-button @click="handleClose" icon="el-icon-circle-close">取 消</el-button>
|
</div>
|
</template>
|
</el-dialog>
|
</template>
|
|
<script>
|
import { createTicket } from '@/api/tickets/ticket';
|
import { gcj02ToWgs84, wgs84ToGcj02 } from '@/utils/coordinateTransformation';
|
|
export default {
|
name: 'CreateTicketDialog',
|
props: {
|
modelValue: {
|
type: Boolean,
|
default: false
|
},
|
departments: {
|
type: Array,
|
default: () => []
|
},
|
departmentUsers: {
|
type: Object,
|
default: () => ({})
|
},
|
types: {
|
type: Array,
|
default: () => []
|
},
|
allAlgorithms: {
|
type: Array,
|
default: () => []
|
},
|
editData: {
|
type: Object,
|
default: null
|
},
|
mapCenter: {
|
type: Array,
|
default: () => [115.861365, 28.621311]
|
}
|
},
|
emits: ['update:modelValue', 'create-success', 'draft-success', 'create-error'],
|
data() {
|
return {
|
form: {
|
name: '',
|
type: '',
|
department: '',
|
handler: '',
|
algorithm: [],
|
location: [],
|
address: '',
|
content: '',
|
photos: []
|
},
|
rules: {
|
name: [{ required: true, message: '请输入工单名称', trigger: 'blur' }],
|
type: [{ required: true, message: '请选择工单类型', trigger: 'change' }],
|
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: [
|
{
|
required: true,
|
validator: (rule, value, callback) => {
|
if (!value || value.length < 2) {
|
callback(new Error('请选择位置信息'));
|
} else if (!value[0] || !value[1]) {
|
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',
|
},
|
],
|
},
|
availableAlgorithms: [],
|
mapParams: {
|
zoom: 15,
|
center: null
|
},
|
submitLoading: false,
|
draftLoading: false
|
};
|
},
|
computed: {
|
dialogVisible: {
|
get() {
|
return this.modelValue;
|
},
|
set(value) {
|
this.$emit('update:modelValue', value);
|
}
|
},
|
availableHandlers() {
|
return this.form.department ? this.departmentUsers[this.form.department] || [] : [];
|
},
|
isEditMode() {
|
return !!this.editData;
|
}
|
},
|
watch: {
|
modelValue(newVal) {
|
if (newVal) {
|
this.initForm();
|
this.mapParams.center = [...this.mapCenter];
|
}
|
}
|
},
|
methods: {
|
initForm() {
|
if (this.isEditMode && this.editData) {
|
// 编辑模式:填充表单数据
|
this.loadEditData();
|
} else {
|
// 新建模式:重置表单
|
this.resetForm();
|
}
|
},
|
|
loadEditData() {
|
const data = this.editData;
|
|
// 获取部门ID
|
let deptId = data.dept_id;
|
if (!deptId) {
|
const deptInfo = this.departments.find(
|
dept => dept.label && dept.label.trim() === data.department.trim()
|
);
|
deptId = deptInfo?.value;
|
}
|
|
// 获取工单类型值
|
const typeValue = this.types.find(t => t.value === data.type)?.value || data.work_order_type_dict_key;
|
|
// 处理算法数组
|
let algorithmArr = [];
|
if (Array.isArray(data.aiType)) {
|
algorithmArr = data.aiType;
|
} else if (typeof data.aiType === 'string' && data.aiType) {
|
algorithmArr = data.aiType.split(/[,、]/).map(item => item.trim());
|
}
|
|
// 确保算法值与选项匹配
|
algorithmArr = algorithmArr
|
.map(item => {
|
if (typeof item === 'string') {
|
const matchedAlgorithm = this.allAlgorithms.find(
|
algo => algo.dict_value === item || algo.dict_key === item
|
);
|
return matchedAlgorithm ? matchedAlgorithm.dict_key : item;
|
}
|
return item;
|
})
|
.filter(Boolean);
|
|
// 处理位置数据
|
let curLocation = [];
|
if (Array.isArray(data.location) && data.location.length >= 2) {
|
let [lng, lat] = this.disposeLocation(false, data);
|
curLocation = [lng, lat, data.location[2] || data.address || ''];
|
}
|
|
this.form = {
|
id: data.id,
|
name: data.orderName,
|
type: typeValue,
|
department: deptId,
|
handler: data.handler,
|
algorithm: algorithmArr,
|
location: curLocation,
|
address: curLocation[2] || '',
|
content: data.content,
|
photos: data.photo_url ? [{
|
name: 'existing-photo.jpg',
|
url: data.photo_url,
|
status: 'success',
|
raw: null,
|
existingUrl: data.photo_url,
|
}] : []
|
};
|
|
// 更新算法选项
|
this.handleTypeChange(typeValue);
|
},
|
|
resetForm() {
|
this.form = {
|
name: '',
|
type: '',
|
department: '',
|
handler: '',
|
algorithm: [],
|
location: [],
|
address: '',
|
content: '',
|
photos: []
|
};
|
this.availableAlgorithms = [];
|
|
if (this.$refs.formRef) {
|
this.$refs.formRef.clearValidate();
|
}
|
},
|
|
handleClose() {
|
this.dialogVisible = false;
|
this.resetForm();
|
},
|
handleTypeChange(typeValue) {
|
this.form.algorithm = [];
|
if (!typeValue) {
|
this.availableAlgorithms = [];
|
return;
|
}
|
const matchedCategory = this.allAlgorithms.find(category => category.dict_key === typeValue);
|
if (!matchedCategory || !matchedCategory.algorithms || matchedCategory.algorithms.length === 0) {
|
this.availableAlgorithms = [];
|
this.$message.info('该工单类型暂无关联算法');
|
return;
|
}
|
|
this.availableAlgorithms = matchedCategory.algorithms.map(algo => ({
|
label: algo.dict_value,
|
value: algo.dict_key,
|
dict_key: algo.dict_key,
|
dict_value: algo.dict_value,
|
}));
|
},
|
|
handleDepartmentChange() {
|
this.form.handler = '';
|
},
|
|
handleLocationChange(val) {
|
let locationValue = val.value;
|
if (locationValue && locationValue.length >= 2) {
|
const [lng, lat] = gcj02ToWgs84(locationValue[0], locationValue[1]);
|
this.form.location = [Number(lng), Number(lat), locationValue[2] || ''];
|
this.form.address = locationValue[2] || '';
|
} else {
|
this.form.location = [];
|
this.form.address = '';
|
}
|
},
|
|
handleFileChange(file, fileList) {
|
this.form.photos = fileList.map(item => ({
|
...item,
|
existingUrl: item.url && !item.raw ? item.url : null,
|
}));
|
},
|
|
handleUploadRemove(file, fileList) {
|
this.form.photos = fileList;
|
},
|
|
beforeUpload(file) {
|
const isImage = file.type.includes('image');
|
const isLt5M = file.size / 1024 / 1024 < 5;
|
|
if (!isImage) {
|
this.$message.error('只能上传图片文件!');
|
return false;
|
}
|
if (!isLt5M) {
|
this.$message.error('图片大小不能超过5MB!');
|
return false;
|
}
|
return true;
|
},
|
|
async handleSubmit() {
|
if (this.submitLoading) return;
|
|
try {
|
await this.$refs.formRef.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;
|
}
|
|
this.submitLoading = true;
|
|
let [lng, lat] = this.disposeLocation(true, this.form);
|
|
const submitData = {
|
eventName: this.form.name,
|
content: this.form.content,
|
workType: '1',
|
longitude: lng,
|
latitude: lat,
|
address: this.form.address,
|
workOrderTypeDictKey: this.form.type,
|
aiType: Array.isArray(this.form.algorithm) ? this.form.algorithm : [this.form.algorithm],
|
updateUser: this.form.handler,
|
createDept: this.form.department,
|
isDraft: 0,
|
};
|
|
if (this.form.id) {
|
submitData.id = this.form.id;
|
}
|
|
// 获取文件
|
let file = null;
|
const photoInfo = this.form.photos[0];
|
|
if (photoInfo.raw) {
|
file = photoInfo.raw;
|
} else if (photoInfo.existingUrl) {
|
submitData.photoUrl = photoInfo.existingUrl;
|
} else {
|
this.$message.warning('图片文件无效,请重新上传');
|
return;
|
}
|
|
const response = await createTicket(submitData, file);
|
if (response.data.code === 0) {
|
this.$message.success(this.isEditMode ? '工单编辑成功' : '工单创建成功');
|
this.$emit('create-success');
|
this.handleClose();
|
} else {
|
throw new Error(response.data.msg || '操作失败');
|
}
|
} catch (error) {
|
if (error.message.includes('验证未通过')) {
|
this.$message.warning('请填写完整的工单信息');
|
} else {
|
this.$message.error(error.message || '操作失败,请稍后重试');
|
this.$emit('create-error', error);
|
}
|
} finally {
|
this.submitLoading = false;
|
}
|
},
|
|
|
disposeLocation(goWgs84 = false, data) {
|
let lng = '', lat = '';
|
|
if (Array.isArray(data.location) && data.location.length > 0) {
|
if (goWgs84) {
|
lng = data.location?.[0] ? String(data.location[0]) : undefined;
|
lat = data.location?.[1] ? String(data.location[1]) : undefined;
|
|
if (lng && lat) {
|
[lng, lat] = gcj02ToWgs84(Number(lng), Number(lat));
|
}
|
} else {
|
lng = Number(data.location[0]);
|
lat = Number(data.location[1]);
|
|
if (lng && lat) {
|
[lng, lat] = this.wgs84ToGcj02(Number(lng), Number(lat));
|
}
|
}
|
}
|
|
return [String(lng), String(lat)];
|
},
|
|
|
}
|
};
|
</script>
|
|
<style lang="scss" scoped>
|
.full-width {
|
width: 100%;
|
}
|
|
.location-wrapper {
|
width: 100%;
|
height: auto;
|
}
|
|
.map-button {
|
width: 100%;
|
height: 36px;
|
display: flex;
|
align-items: center;
|
justify-content: center;
|
}
|
|
.create-ticket-form {
|
padding: 20px 10px;
|
|
.form-section {
|
background-color: #fff;
|
border-radius: 4px;
|
|
.el-row {
|
margin-bottom: 16px;
|
|
&:last-child {
|
margin-bottom: 0;
|
}
|
}
|
}
|
|
.location-wrapper {
|
width: 100%; // 修改为100%以适应父容器
|
height: auto; // 修改为auto以自适应内容
|
|
.map-button {
|
width: 100%; // 让按钮填满容器宽度
|
height: 36px; // 与其他输入框保持一致的高度
|
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-wrapper {
|
.uploader {
|
:deep(.el-upload--picture-card) {
|
width: 120px;
|
height: 100px;
|
line-height: 128px;
|
}
|
|
:deep(.el-upload-list__item) {
|
width: 120px;
|
height: 120px;
|
}
|
}
|
|
.upload-tip {
|
font-size: 12px;
|
color: #909399;
|
line-height: 1.4;
|
margin-top: 8px;
|
}
|
}
|
|
.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;
|
}
|
|
// :deep(.el-input__inner),
|
// :deep(.el-select),
|
// :deep(.el-select .el-input) {
|
// width: 100%; // 确保所有输入框和选择框宽度一致
|
// }
|
|
.full-width {
|
width: 100%;
|
}
|
}
|
|
:deep(.el-dialog__body) {
|
border-top: 0.1rem solid #f0f0f0;
|
}
|
/* 新建工单和处理工单的上传组件样式 */
|
.create-upload,
|
.detail-upload {
|
:deep(.el-upload--picture-card) {
|
width: 120px;
|
height: 100px;
|
line-height: 100px;
|
}
|
|
/* 隐藏额外的上传按钮 */
|
:deep(.el-upload.el-upload--picture-card) {
|
display: none;
|
}
|
|
/* 当没有图片时显示上传按钮 */
|
:deep(.el-upload.el-upload--picture-card:first-child) {
|
display: flex;
|
}
|
|
/* 上传组件的预览图片样式 */
|
:deep(.el-upload-list--picture-card .el-upload-list__item) {
|
width: 120px;
|
height: 100px;
|
}
|
}
|
</style>
|