web工单处理

This commit is contained in:
2025-12-28 13:18:28 +08:00
parent 1148e3368d
commit 7de30b1b36
12 changed files with 1209 additions and 95 deletions

View File

@@ -0,0 +1 @@
export { default as WorkcaseAssign } from './workcase/WorkcaseAssign.vue'

View File

@@ -0,0 +1,245 @@
<template>
<ElDialog
v-model="visible"
:title="dialogTitle"
width="500px"
@close="handleClose"
>
<div class="assign-form">
<div class="form-item">
<label class="form-label">选择工程师 <span class="required">*</span></label>
<ElSelect
v-model="form.processor"
placeholder="请选择工程师"
style="width: 100%;"
filterable
:loading="loadingEngineers"
>
<ElOption
v-for="engineer in availableEngineers"
:key="engineer.userId"
:label="`${engineer.username} (${engineer.statusName || '未知状态'})`"
:value="engineer.userId"
>
<div style="display: flex; justify-content: space-between; align-items: center;">
<span>{{ engineer.username }}</span>
<span style="color: #999; font-size: 12px;">
{{ engineer.statusName }} | 当前{{ engineer.currentWorkload || 0 }}
</span>
</div>
</ElOption>
</ElSelect>
</div>
<div class="form-item" style="margin-top: 16px;">
<label class="form-label">备注说明可选</label>
<ElInput
v-model="form.message"
type="textarea"
:rows="3"
placeholder="请输入指派/转派说明..."
maxlength="200"
/>
</div>
<div class="form-item" style="margin-top: 16px;">
<label class="form-label">附件可选</label>
<FileUpload
ref="fileUploadRef"
mode="content"
:max-count="5"
:max-size="10 * 1024 * 1024"
accept="image/*,.pdf,.doc,.docx,.xls,.xlsx"
:auto-upload="false"
:custom-upload="handleFilesUpload"
@upload-error="handleUploadError"
/>
</div>
</div>
<template #footer>
<ElButton @click="handleClose">取消</ElButton>
<ElButton type="primary" @click="handleSubmit" :loading="submitting">确定</ElButton>
</template>
</ElDialog>
</template>
<script setup lang="ts">
import { ref, computed, watch } from 'vue'
import { ElDialog, ElButton, ElInput, ElSelect, ElOption, ElMessage } from 'element-plus'
import { FileUpload } from 'shared/components'
import { fileAPI } from 'shared/api'
import { workcaseAPI } from '@/api/workcase'
import { workcaseChatAPI } from '@/api/workcase/workcaseChat'
import type { TbWorkcaseProcessDTO } from '@/types/workcase/workcase'
import type { CustomerServiceVO } from '@/types/workcase/customer'
import type { TbSysFileDTO } from 'shared/types'
interface Props {
/** 是否显示弹窗 */
modelValue: boolean
/** 工单ID */
workcaseId: string
/** 当前处理人ID转派时排除 */
currentProcessor?: string
/** 操作类型assign-指派redeploy-转派 */
action?: 'assign' | 'redeploy'
}
const props = withDefaults(defineProps<Props>(), {
currentProcessor: '',
action: 'assign'
})
const emit = defineEmits<{
'update:modelValue': [value: boolean]
'success': []
}>()
// 弹窗显示状态
const visible = computed({
get: () => props.modelValue,
set: (val) => emit('update:modelValue', val)
})
// 弹窗标题
const dialogTitle = computed(() => {
return props.action === 'assign' ? '指派工程师' : '转派工程师'
})
// 表单数据
const form = ref({
processor: '',
message: '',
fileIds: [] as string[]
})
// 状态
const submitting = ref(false)
const loadingEngineers = ref(false)
const availableEngineers = ref<CustomerServiceVO[]>([])
const fileUploadRef = ref<InstanceType<typeof FileUpload>>()
// 监听弹窗打开,加载工程师列表
watch(visible, async (val) => {
if (val) {
// 重置表单
form.value = { processor: '', message: '', fileIds: [] }
if (fileUploadRef.value) {
fileUploadRef.value.clearFiles()
}
// 加载工程师列表
await loadAvailableEngineers()
}
})
// 加载可用工程师列表
const loadAvailableEngineers = async () => {
loadingEngineers.value = true
try {
const res = await workcaseChatAPI.getCustomerServicePage({
filter: {},
pageParam: { page: 1, pageSize: 100 }
})
if (res.success && res.dataList) {
// 排除当前处理人
availableEngineers.value = res.dataList.filter(
(engineer: CustomerServiceVO) => engineer.userId !== props.currentProcessor
)
}
} catch (error) {
console.error('加载工程师列表失败:', error)
ElMessage.error('加载工程师列表失败')
} finally {
loadingEngineers.value = false
}
}
// 自定义文件上传
const handleFilesUpload = async (files: File[]): Promise<TbSysFileDTO[]> => {
const uploadedFiles: TbSysFileDTO[] = []
for (const file of files) {
const result = await fileAPI.uploadFile({
file,
module: 'workcase',
optsn: props.workcaseId
})
if (result.success && result.data) {
uploadedFiles.push(result.data)
if (result.data.fileId) {
form.value.fileIds.push(result.data.fileId)
}
} else {
throw new Error(`文件 ${file.name} 上传失败`)
}
}
return uploadedFiles
}
// 上传错误处理
const handleUploadError = (error: string) => {
ElMessage.error(error)
}
// 提交
const handleSubmit = async () => {
if (!form.value.processor) {
ElMessage.warning('请选择工程师')
return
}
if (!props.workcaseId) {
ElMessage.error('工单ID不存在')
return
}
submitting.value = true
try {
// 如果有待上传的文件,先上传
if (fileUploadRef.value?.selectedFiles?.length > 0) {
await fileUploadRef.value.uploadFiles()
}
const params: TbWorkcaseProcessDTO = {
workcaseId: props.workcaseId,
action: props.action,
processor: form.value.processor,
message: form.value.message || (props.action === 'assign' ? '工单指派' : '工单转派'),
files: form.value.fileIds.length > 0 ? form.value.fileIds : undefined
}
const res = await workcaseAPI.createWorkcaseProcess(params)
if (res.success) {
ElMessage.success(props.action === 'assign' ? '指派成功' : '转派成功')
visible.value = false
emit('success')
} else {
ElMessage.error(res.message || '操作失败')
}
} catch (error) {
console.error('指派/转派失败:', error)
ElMessage.error('操作失败')
} finally {
submitting.value = false
}
}
// 关闭弹窗
const handleClose = () => {
visible.value = false
}
</script>
<style scoped lang="scss">
.assign-form {
.form-item {
.form-label {
display: block;
margin-bottom: 8px;
font-size: 14px;
color: #333;
.required {
color: #f56c6c;
}
}
}
}
</style>