优化copy/clone操作
This commit is contained in:
parent
02ead7b7e3
commit
b47266d532
|
|
@ -174,7 +174,7 @@ const treeDataSource = [
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Main Tree",
|
name: "Standards",
|
||||||
children: [
|
children: [
|
||||||
{ name: "Product Scenario", children: [] },
|
{ name: "Product Scenario", children: [] },
|
||||||
{
|
{
|
||||||
|
|
@ -638,35 +638,22 @@ const operationDialog = ref({
|
||||||
show: false,
|
show: false,
|
||||||
mode: 'copy' as 'copy' | 'clone' | 'branch',
|
mode: 'copy' as 'copy' | 'clone' | 'branch',
|
||||||
sourceNodeId: '',
|
sourceNodeId: '',
|
||||||
selectedSourceIds: [] as string[]
|
selectedSourceIds: [] as string[],
|
||||||
|
owner: 'admin' // default owner
|
||||||
})
|
})
|
||||||
|
|
||||||
const toggleSourceSelection = (node: TreeNode) => {
|
const toggleSourceSelection = (node: TreeNode) => {
|
||||||
|
// Only allow terminal nodes
|
||||||
|
if (!isTerminalNode(node)) return
|
||||||
|
|
||||||
const ids = operationDialog.value.selectedSourceIds
|
const ids = operationDialog.value.selectedSourceIds
|
||||||
|
|
||||||
// Collect ALL terminal descendant IDs (exclude folders from selection list)
|
if (ids.includes(node.id)) {
|
||||||
let terminalsToToggle: string[] = []
|
// Toggle off if already selected
|
||||||
|
operationDialog.value.selectedSourceIds = []
|
||||||
if (isTerminalNode(node)) {
|
|
||||||
terminalsToToggle = [node.id]
|
|
||||||
} else {
|
} else {
|
||||||
terminalsToToggle = getAllTerminalDescendants(node)
|
// Single select: replace any existing selection
|
||||||
}
|
operationDialog.value.selectedSourceIds = [node.id]
|
||||||
|
|
||||||
// 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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -916,11 +903,15 @@ const closeContextMenu = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const openOperationDialog = (mode: 'copy' | 'clone' | 'branch') => {
|
const openOperationDialog = (mode: 'copy' | 'clone' | 'branch') => {
|
||||||
|
const currentSourceNodeId = contextMenu.value?.nodeId || ''
|
||||||
|
const currentNode = findNode(mockTreeData.value, currentSourceNodeId)
|
||||||
|
|
||||||
operationDialog.value = {
|
operationDialog.value = {
|
||||||
show: true,
|
show: true,
|
||||||
mode,
|
mode,
|
||||||
sourceNodeId: contextMenu.value?.nodeId || '',
|
sourceNodeId: currentSourceNodeId,
|
||||||
selectedSourceIds: []
|
selectedSourceIds: [],
|
||||||
|
owner: currentNode?.owner || 'admin'
|
||||||
}
|
}
|
||||||
dialogExpandedIds.value = new Set(expandedIds.value)
|
dialogExpandedIds.value = new Set(expandedIds.value)
|
||||||
closeContextMenu()
|
closeContextMenu()
|
||||||
|
|
@ -1199,6 +1190,7 @@ const executeOperation = () => {
|
||||||
if (mode === 'copy') {
|
if (mode === 'copy') {
|
||||||
const newNode = cloneNode(sourceNode, `${destinationNode.id}-${Date.now()}-${processedCount}`)
|
const newNode = cloneNode(sourceNode, `${destinationNode.id}-${Date.now()}-${processedCount}`)
|
||||||
newNode.label = `${newNode.label} (Copy)`
|
newNode.label = `${newNode.label} (Copy)`
|
||||||
|
newNode.owner = operationDialog.value.owner
|
||||||
addChildNode(targetParent, newNode)
|
addChildNode(targetParent, newNode)
|
||||||
} else if (mode === 'clone') {
|
} else if (mode === 'clone') {
|
||||||
const newNode: TreeNode = {
|
const newNode: TreeNode = {
|
||||||
|
|
@ -1206,13 +1198,15 @@ const executeOperation = () => {
|
||||||
id: `${destinationNode.id}-${Date.now()}-${processedCount}`,
|
id: `${destinationNode.id}-${Date.now()}-${processedCount}`,
|
||||||
label: `${sourceNode.label} (Clone)`,
|
label: `${sourceNode.label} (Clone)`,
|
||||||
isClone: true,
|
isClone: true,
|
||||||
cloneSourceId: sourceNode.id
|
cloneSourceId: sourceNode.id,
|
||||||
|
owner: operationDialog.value.owner
|
||||||
}
|
}
|
||||||
addChildNode(targetParent, newNode)
|
addChildNode(targetParent, newNode)
|
||||||
} else if (mode === 'branch') {
|
} else if (mode === 'branch') {
|
||||||
// Even if source is terminal, "Branch" op might function like copy
|
// Even if source is terminal, "Branch" op might function like copy
|
||||||
const newNode = cloneNode(sourceNode, `${destinationNode.id}-${Date.now()}-${processedCount}`)
|
const newNode = cloneNode(sourceNode, `${destinationNode.id}-${Date.now()}-${processedCount}`)
|
||||||
newNode.label = `${newNode.label} (Branch)`
|
newNode.label = `${newNode.label} (Branch)`
|
||||||
|
newNode.owner = operationDialog.value.owner
|
||||||
addChildNode(targetParent, newNode)
|
addChildNode(targetParent, newNode)
|
||||||
}
|
}
|
||||||
processedCount++
|
processedCount++
|
||||||
|
|
@ -1511,6 +1505,7 @@ defineExpose({
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
:checked="isIdsSelected(node)"
|
:checked="isIdsSelected(node)"
|
||||||
:indeterminate.prop="isIdsIndeterminate(node)"
|
:indeterminate.prop="isIdsIndeterminate(node)"
|
||||||
|
:disabled="!isTerminalNode(node)"
|
||||||
@change="toggleSourceSelection(node)"
|
@change="toggleSourceSelection(node)"
|
||||||
style="margin-right: 6px;"
|
style="margin-right: 6px;"
|
||||||
/>
|
/>
|
||||||
|
|
@ -1528,6 +1523,11 @@ defineExpose({
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="dialog-footer">
|
<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-secondary" @click="operationDialog.show = false">Cancel</button>
|
||||||
<button class="btn btn-primary" @click="executeOperation" :disabled="operationDialog.selectedSourceIds.length === 0">
|
<button class="btn btn-primary" @click="executeOperation" :disabled="operationDialog.selectedSourceIds.length === 0">
|
||||||
{{ operationDialog.mode === 'copy' ? 'Copy' : operationDialog.mode === 'clone' ? 'Clone' : 'Branch' }}
|
{{ operationDialog.mode === 'copy' ? 'Copy' : operationDialog.mode === 'clone' ? 'Clone' : 'Branch' }}
|
||||||
|
|
@ -1536,6 +1536,7 @@ defineExpose({
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|
@ -1973,12 +1974,43 @@ defineExpose({
|
||||||
|
|
||||||
.dialog-footer {
|
.dialog-footer {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: flex-end;
|
justify-content: space-between;
|
||||||
gap: 12px;
|
align-items: center;
|
||||||
padding: 16px 20px;
|
padding: 16px 20px;
|
||||||
border-top: 1px solid #e0e0e0;
|
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 {
|
.btn {
|
||||||
padding: 8px 16px;
|
padding: 8px 16px;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ const findNodeByName = (nodes: TreeNode[], name: string): TreeNode | undefined =
|
||||||
return nodes.find(n => n.label === name)
|
return nodes.find(n => n.label === name)
|
||||||
}
|
}
|
||||||
|
|
||||||
const mainTreeNode = computed(() => findNodeByName(props.treeData, 'Main Tree'))
|
const mainTreeNode = computed(() => findNodeByName(props.treeData, 'Standards'))
|
||||||
|
|
||||||
const attributesNodes = computed(() => {
|
const attributesNodes = computed(() => {
|
||||||
if (!mainTreeNode.value || !mainTreeNode.value.children) return []
|
if (!mainTreeNode.value || !mainTreeNode.value.children) return []
|
||||||
|
|
|
||||||
|
|
@ -69,7 +69,7 @@ const initReviewHistory = () => {
|
||||||
// 获取可release的版本详情(包含审核信息)
|
// 获取可release的版本详情(包含审核信息)
|
||||||
const releasableVersionsDetails = computed(() => {
|
const releasableVersionsDetails = computed(() => {
|
||||||
return reviewHistory.value
|
return reviewHistory.value
|
||||||
.filter((item: any) => item.status === 'approved')
|
.filter((item: any) => item.status === 'accepted')
|
||||||
.map((item: any) => {
|
.map((item: any) => {
|
||||||
// 尝试从节点历史版本中获取真实内容
|
// 尝试从节点历史版本中获取真实内容
|
||||||
const versionData = props.selectedNode?.versions?.find((v: any) => v.version === item.version)
|
const versionData = props.selectedNode?.versions?.find((v: any) => v.version === item.version)
|
||||||
|
|
@ -150,7 +150,7 @@ const nodeForModal = computed(() => {
|
||||||
return {
|
return {
|
||||||
...props.selectedNode,
|
...props.selectedNode,
|
||||||
version: selectedVersionDetail.value.version,
|
version: selectedVersionDetail.value.version,
|
||||||
status: 'Approved',
|
status: 'Accepted',
|
||||||
lastChangedBy: selectedVersionDetail.value.reviewer,
|
lastChangedBy: selectedVersionDetail.value.reviewer,
|
||||||
lastChangedDate: selectedVersionDetail.value.approvedDate,
|
lastChangedDate: selectedVersionDetail.value.approvedDate,
|
||||||
comment: selectedVersionDetail.value.comment,
|
comment: selectedVersionDetail.value.comment,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue