Compare commits

..

2 Commits

Author SHA1 Message Date
DKJ 394cb3cc18 Merge branch 'master' of http://106.15.36.190:3000/DKJ/MID 2026-02-12 22:22:28 +08:00
DKJ b47266d532 优化copy/clone操作 2026-02-12 22:22:03 +08:00
2 changed files with 65 additions and 33 deletions

View File

@ -638,35 +638,22 @@ const operationDialog = ref({
show: false,
mode: 'copy' as 'copy' | 'clone' | 'branch',
sourceNodeId: '',
selectedSourceIds: [] as string[]
selectedSourceIds: [] as string[],
owner: 'admin' // default owner
})
const toggleSourceSelection = (node: TreeNode) => {
// Only allow terminal nodes
if (!isTerminalNode(node)) return
const ids = operationDialog.value.selectedSourceIds
// Collect ALL terminal descendant IDs (exclude folders from selection list)
let terminalsToToggle: string[] = []
if (isTerminalNode(node)) {
terminalsToToggle = [node.id]
if (ids.includes(node.id)) {
// Toggle off if already selected
operationDialog.value.selectedSourceIds = []
} else {
terminalsToToggle = getAllTerminalDescendants(node)
}
// If node is a folder with no terminal children, do nothing or just ignore
if (terminalsToToggle.length === 0) return
const allSelected = terminalsToToggle.every(id => ids.includes(id))
if (allSelected) {
operationDialog.value.selectedSourceIds = ids.filter(id => !terminalsToToggle.includes(id))
} else {
// Add missing
const newIds = [...ids]
terminalsToToggle.forEach(id => {
if (!newIds.includes(id)) newIds.push(id)
})
operationDialog.value.selectedSourceIds = newIds
// Single select: replace any existing selection
operationDialog.value.selectedSourceIds = [node.id]
}
}
@ -916,11 +903,15 @@ const closeContextMenu = () => {
}
const openOperationDialog = (mode: 'copy' | 'clone' | 'branch') => {
const currentSourceNodeId = contextMenu.value?.nodeId || ''
const currentNode = findNode(mockTreeData.value, currentSourceNodeId)
operationDialog.value = {
show: true,
mode,
sourceNodeId: contextMenu.value?.nodeId || '',
selectedSourceIds: []
sourceNodeId: currentSourceNodeId,
selectedSourceIds: [],
owner: currentNode?.owner || 'admin'
}
dialogExpandedIds.value = new Set(expandedIds.value)
closeContextMenu()
@ -1199,6 +1190,7 @@ const executeOperation = () => {
if (mode === 'copy') {
const newNode = cloneNode(sourceNode, `${destinationNode.id}-${Date.now()}-${processedCount}`)
newNode.label = `${newNode.label} (Copy)`
newNode.owner = operationDialog.value.owner
addChildNode(targetParent, newNode)
} else if (mode === 'clone') {
const newNode: TreeNode = {
@ -1206,13 +1198,15 @@ const executeOperation = () => {
id: `${destinationNode.id}-${Date.now()}-${processedCount}`,
label: `${sourceNode.label} (Clone)`,
isClone: true,
cloneSourceId: sourceNode.id
cloneSourceId: sourceNode.id,
owner: operationDialog.value.owner
}
addChildNode(targetParent, newNode)
} else if (mode === 'branch') {
// Even if source is terminal, "Branch" op might function like copy
const newNode = cloneNode(sourceNode, `${destinationNode.id}-${Date.now()}-${processedCount}`)
newNode.label = `${newNode.label} (Branch)`
newNode.owner = operationDialog.value.owner
addChildNode(targetParent, newNode)
}
processedCount++
@ -1511,6 +1505,7 @@ defineExpose({
type="checkbox"
:checked="isIdsSelected(node)"
:indeterminate.prop="isIdsIndeterminate(node)"
:disabled="!isTerminalNode(node)"
@change="toggleSourceSelection(node)"
style="margin-right: 6px;"
/>
@ -1528,6 +1523,11 @@ defineExpose({
</div>
<div class="dialog-footer">
<div class="owner-input-group">
<label>Owner:</label>
<input type="text" v-model="operationDialog.owner" class="dialog-input" />
</div>
<div class="dialog-actions">
<button class="btn btn-secondary" @click="operationDialog.show = false">Cancel</button>
<button class="btn btn-primary" @click="executeOperation" :disabled="operationDialog.selectedSourceIds.length === 0">
{{ operationDialog.mode === 'copy' ? 'Copy' : operationDialog.mode === 'clone' ? 'Clone' : 'Branch' }}
@ -1536,6 +1536,7 @@ defineExpose({
</div>
</div>
</div>
</div>
</template>
<style scoped>
@ -1973,12 +1974,43 @@ defineExpose({
.dialog-footer {
display: flex;
justify-content: flex-end;
gap: 12px;
justify-content: space-between;
align-items: center;
padding: 16px 20px;
border-top: 1px solid #e0e0e0;
}
.owner-input-group {
display: flex;
align-items: center;
gap: 8px;
}
.owner-input-group label {
font-size: 13px;
font-weight: 500;
color: #333;
}
.dialog-input {
padding: 6px 8px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 13px;
width: 150px;
outline: none;
transition: border-color 0.2s;
}
.dialog-input:focus {
border-color: #0078d4;
}
.dialog-actions {
display: flex;
gap: 12px;
}
.btn {
padding: 8px 16px;
border-radius: 4px;

View File

@ -150,7 +150,7 @@ const nodeForModal = computed(() => {
return {
...props.selectedNode,
version: selectedVersionDetail.value.version,
status: 'Approved',
status: 'Accepted',
lastChangedBy: selectedVersionDetail.value.reviewer,
lastChangedDate: selectedVersionDetail.value.approvedDate,
comment: selectedVersionDetail.value.comment,