feat: 1.预览图支持粘贴图片 2.修复子任务拖拽排序功能
This commit is contained in:
parent
96f2521511
commit
7b6d7a3fe1
@ -71,16 +71,19 @@
|
||||
:key="item.id || index"
|
||||
class="task-type-item"
|
||||
:class="{ 'dragging': dragIndex === index, 'drag-over': dragOverIndex === index }"
|
||||
draggable="true"
|
||||
@dragstart="handleDragStart($event, index)"
|
||||
@dragend="handleDragEnd"
|
||||
@dragover.prevent="handleDragOver($event, index)"
|
||||
@dragleave="handleDragLeave"
|
||||
@drop="handleDrop($event, index)"
|
||||
>
|
||||
<div class="task-type-row">
|
||||
<!-- 拖拽手柄 -->
|
||||
<div class="drag-handle" title="拖拽调整顺序">
|
||||
<div
|
||||
class="drag-handle"
|
||||
title="拖拽调整顺序"
|
||||
draggable="true"
|
||||
@dragstart="handleDragStart($event, index)"
|
||||
@dragend="handleDragEnd"
|
||||
>
|
||||
<Icon name="menu" size="sm" />
|
||||
</div>
|
||||
<div class="task-type-select task-type-cascader">
|
||||
@ -397,7 +400,12 @@
|
||||
|
||||
<!-- 预览图上传 -->
|
||||
<a-form-item label="预览图" name="previewImage">
|
||||
<div class="preview-upload-area">
|
||||
<div
|
||||
class="preview-upload-area"
|
||||
@paste="handlePasteImage"
|
||||
tabindex="0"
|
||||
style="outline: none;"
|
||||
>
|
||||
<a-upload
|
||||
v-model:file-list="previewFileList"
|
||||
:before-upload="beforePreviewUpload"
|
||||
@ -410,13 +418,14 @@
|
||||
<div v-if="!formData.previewImage" class="upload-button">
|
||||
<Icon name="plus" size="lg" />
|
||||
<div class="upload-text">上传预览图</div>
|
||||
<div class="paste-hint">或按 Ctrl+V 粘贴图片</div>
|
||||
</div>
|
||||
</a-upload>
|
||||
<div class="preview-tip">
|
||||
<span class="tip-icon">
|
||||
<Icon name="image" size="sm" color="#31f08b" />
|
||||
</span>
|
||||
<span>建议上传正方形图片,系统将自动压缩处理,显示在任务卡片上</span>
|
||||
<span>建议上传正方形图片,系统将自动压缩处理,显示在任务卡片上。支持 Ctrl+V 粘贴图片</span>
|
||||
</div>
|
||||
</div>
|
||||
</a-form-item>
|
||||
@ -941,6 +950,20 @@ const handleDragStart = (e: DragEvent, index: number) => {
|
||||
if (e.dataTransfer) {
|
||||
e.dataTransfer.effectAllowed = 'move'
|
||||
e.dataTransfer.setData('text/plain', String(index))
|
||||
e.dataTransfer.setData('application/json', JSON.stringify({ index }))
|
||||
// 设置拖拽图像为整个 item
|
||||
const target = e.target as HTMLElement
|
||||
const itemElement = target.closest('.task-type-item') as HTMLElement
|
||||
if (itemElement) {
|
||||
// 创建一个临时元素作为拖拽图像
|
||||
const dragImage = itemElement.cloneNode(true) as HTMLElement
|
||||
dragImage.style.opacity = '0.8'
|
||||
dragImage.style.position = 'absolute'
|
||||
dragImage.style.top = '-1000px'
|
||||
document.body.appendChild(dragImage)
|
||||
e.dataTransfer.setDragImage(dragImage, e.offsetX, e.offsetY)
|
||||
setTimeout(() => document.body.removeChild(dragImage), 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -966,19 +989,37 @@ const handleDragLeave = () => {
|
||||
// 放置
|
||||
const handleDrop = (e: DragEvent, targetIndex: number) => {
|
||||
e.preventDefault()
|
||||
if (dragIndex.value === null || dragIndex.value === targetIndex) {
|
||||
e.stopPropagation()
|
||||
|
||||
// 从拖拽数据中获取源索引
|
||||
let sourceIndex = dragIndex.value
|
||||
if (sourceIndex === null) {
|
||||
// 尝试从 dataTransfer 中读取
|
||||
const data = e.dataTransfer?.getData('text/plain')
|
||||
if (data) {
|
||||
sourceIndex = parseInt(data, 10)
|
||||
} else {
|
||||
handleDragEnd()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if (sourceIndex === null || sourceIndex === targetIndex || sourceIndex < 0 || targetIndex < 0) {
|
||||
handleDragEnd()
|
||||
return
|
||||
}
|
||||
|
||||
console.log('拖拽排序:', sourceIndex, '->', targetIndex)
|
||||
|
||||
// 交换位置 - 使用 splice 原地修改数组以确保响应式更新
|
||||
const sourceIndex = dragIndex.value
|
||||
const [removed] = formData.taskTypes.splice(sourceIndex, 1)
|
||||
formData.taskTypes.splice(targetIndex, 0, removed)
|
||||
|
||||
// 重新计算积分(pipelineGroups 是 computed,会自动更新)
|
||||
calculateScore()
|
||||
|
||||
message.success('任务顺序已调整')
|
||||
|
||||
handleDragEnd()
|
||||
}
|
||||
|
||||
@ -1730,6 +1771,83 @@ const handlePreviewRemove = () => {
|
||||
return true
|
||||
}
|
||||
|
||||
// 处理粘贴图片
|
||||
const handlePasteImage = async (e: ClipboardEvent) => {
|
||||
e.preventDefault()
|
||||
|
||||
// 如果已经有预览图,不处理粘贴
|
||||
if (formData.previewImage) {
|
||||
return
|
||||
}
|
||||
|
||||
const items = e.clipboardData?.items
|
||||
if (!items) return
|
||||
|
||||
// 查找图片数据
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
const item = items[i]
|
||||
if (item.type.indexOf('image') !== -1) {
|
||||
const file = item.getAsFile()
|
||||
if (!file) continue
|
||||
|
||||
// 验证文件大小
|
||||
const isLt10M = file.size / 1024 / 1024 < 10
|
||||
if (!isLt10M) {
|
||||
message.error('图片大小不能超过 10MB')
|
||||
return
|
||||
}
|
||||
|
||||
// 创建 File 对象用于上传
|
||||
const uploadFile: UploadFile = {
|
||||
uid: `paste-${Date.now()}`,
|
||||
name: `粘贴图片-${Date.now()}.png`,
|
||||
status: 'uploading',
|
||||
originFileObj: file
|
||||
}
|
||||
|
||||
previewFileList.value = [uploadFile]
|
||||
|
||||
// 调用上传处理函数
|
||||
try {
|
||||
previewUploading.value = true
|
||||
const formDataObj = new FormData()
|
||||
formDataObj.append('file', file)
|
||||
|
||||
const response = await fetch('/api/file/upload-preview', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${localStorage.getItem('token')}`
|
||||
},
|
||||
body: formDataObj
|
||||
})
|
||||
|
||||
const result = await response.json()
|
||||
|
||||
if (result.code === 200 && result.data) {
|
||||
formData.previewImage = result.data
|
||||
previewFileList.value = [{
|
||||
uid: uploadFile.uid,
|
||||
name: uploadFile.name,
|
||||
status: 'done',
|
||||
url: result.data
|
||||
} as UploadFile]
|
||||
message.success('图片粘贴成功')
|
||||
} else {
|
||||
throw new Error(result.msg || '上传失败')
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error('粘贴图片上传失败:', error)
|
||||
previewFileList.value = []
|
||||
message.error('粘贴图片上传失败: ' + error.message)
|
||||
} finally {
|
||||
previewUploading.value = false
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 加载任务数据用于编辑
|
||||
const loadTaskForEdit = async (taskId: number) => {
|
||||
try {
|
||||
@ -1908,8 +2026,19 @@ const loadReleaseTypeConfig = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
// 全局粘贴事件处理(当预览图区域可见时)
|
||||
const handleGlobalPaste = (e: ClipboardEvent) => {
|
||||
// 如果预览图区域可见且没有图片,处理粘贴
|
||||
if (!formData.previewImage) {
|
||||
handlePasteImage(e)
|
||||
}
|
||||
}
|
||||
|
||||
// 组件挂载
|
||||
onMounted(async () => {
|
||||
// 添加全局粘贴事件监听
|
||||
document.addEventListener('paste', handleGlobalPaste as EventListener)
|
||||
|
||||
// 先刷新用户信息,确保部门信息最新
|
||||
try {
|
||||
await authStore.fetchUserInfo()
|
||||
@ -1941,6 +2070,7 @@ onMounted(async () => {
|
||||
// 组件卸载
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('configUpdated', handleConfigUpdate as EventListener)
|
||||
document.removeEventListener('paste', handleGlobalPaste as EventListener)
|
||||
})
|
||||
</script>
|
||||
|
||||
@ -2678,6 +2808,21 @@ onUnmounted(() => {
|
||||
margin-top: 8px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.paste-hint {
|
||||
margin-top: 4px;
|
||||
font-size: 11px;
|
||||
color: #666;
|
||||
opacity: 0.7;
|
||||
}
|
||||
}
|
||||
|
||||
// 当区域获得焦点时显示提示
|
||||
&:focus-within {
|
||||
.paste-hint {
|
||||
opacity: 1;
|
||||
color: $brand-primary;
|
||||
}
|
||||
}
|
||||
|
||||
.preview-tip {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user