思维导图详情页增加重复项和重命名菜单
This commit is contained in:
parent
eac89e8f0f
commit
3979ba7ba7
@ -70,6 +70,28 @@
|
||||
{{ t('Back') }}
|
||||
</n-button>
|
||||
|
||||
<!-- 三点菜单 -->
|
||||
<n-dropdown
|
||||
v-if="menuOptions.length > 0"
|
||||
:options="menuOptions"
|
||||
trigger="click"
|
||||
:render-icon="renderMenuIcon"
|
||||
@select="handleMenuSelect"
|
||||
>
|
||||
<n-button
|
||||
type="default"
|
||||
size="medium"
|
||||
:title="t('More')"
|
||||
class="header-action-btn"
|
||||
>
|
||||
<template #icon>
|
||||
<n-icon>
|
||||
<Icon icon="tabler:dots" />
|
||||
</n-icon>
|
||||
</template>
|
||||
</n-button>
|
||||
</n-dropdown>
|
||||
|
||||
<!-- 保存按钮 -->
|
||||
<n-button
|
||||
type="primary"
|
||||
@ -86,16 +108,42 @@
|
||||
</template>
|
||||
{{ t('Save') }}
|
||||
</n-button>
|
||||
|
||||
<!-- 重命名对话框 -->
|
||||
<n-modal
|
||||
v-model:show="showRenameDialog"
|
||||
preset="dialog"
|
||||
:title="t('Rename')"
|
||||
:positive-text="t('OK')"
|
||||
:negative-text="t('Cancel')"
|
||||
:positive-button-props="{ type: 'primary' }"
|
||||
:loading="renaming"
|
||||
@positive-click="handleRenameConfirm"
|
||||
style="width: 450px;"
|
||||
>
|
||||
<n-form ref="renameFormRef" :model="renameForm" :rules="renameRules" label-placement="top">
|
||||
<n-form-item :label="t('New Name')" path="newName">
|
||||
<n-input v-model:value="renameForm.newName" :placeholder="t('Enter new name')" />
|
||||
</n-form-item>
|
||||
<n-form-item :label="t('Merge with existing')" path="merge">
|
||||
<n-checkbox v-model:checked="renameForm.merge">
|
||||
<span>{{ t('Merge with existing') }}</span>
|
||||
<n-text depth="3" style="margin-left: 4px;">({{ t('This cannot be undone') }})</n-text>
|
||||
</n-checkbox>
|
||||
</n-form-item>
|
||||
</n-form>
|
||||
</n-modal>
|
||||
</n-space>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, watch, onMounted } from 'vue'
|
||||
import { NButton, NSpace, NIcon } from 'naive-ui'
|
||||
import { ref, computed, watch, onMounted, h } from 'vue'
|
||||
import { NButton, NSpace, NIcon, NDropdown, NModal, NForm, NFormItem, NInput, NCheckbox, NText, useMessage, type FormInst, type FormRules, type DropdownOption } from 'naive-ui'
|
||||
import { Icon } from '@iconify/vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { t } from '@/shared/i18n'
|
||||
import { pageTypeToSlug } from '@/shared/utils/slug'
|
||||
import { renameRecord, duplicateRecord } from '@/shared/api/common'
|
||||
|
||||
interface Props {
|
||||
entity: string
|
||||
@ -104,6 +152,7 @@ interface Props {
|
||||
canEdit: boolean
|
||||
loading: boolean
|
||||
sidebarPosition?: 'left' | 'right'
|
||||
pageMeta?: any
|
||||
}
|
||||
|
||||
interface Emits {
|
||||
@ -112,14 +161,17 @@ interface Emits {
|
||||
(e: 'delete'): void
|
||||
(e: 'go-back'): void
|
||||
(e: 'save'): void
|
||||
(e: 'rename', newName: string): void
|
||||
(e: 'duplicate', newName: string): void
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
sidebarPosition: 'left',
|
||||
record: () => ({})
|
||||
record: () => ({}),
|
||||
pageMeta: () => ({})
|
||||
})
|
||||
|
||||
defineEmits<Emits>()
|
||||
const emit = defineEmits<Emits>()
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
@ -155,6 +207,123 @@ function handleMindMap() {
|
||||
const pagetype = pageTypeToSlug(props.entity)
|
||||
router.push({ name: 'MindMapEditor', query: { pagetype, id: props.id } })
|
||||
}
|
||||
|
||||
// ===== 重命名功能 =====
|
||||
const canRename = computed(() => {
|
||||
if (isNew.value) return false
|
||||
if (!props.canEdit) return false
|
||||
return !!(props.pageMeta?.allow_rename)
|
||||
})
|
||||
|
||||
// ===== 三点菜单 =====
|
||||
const menuOptions = computed<DropdownOption[]>(() => {
|
||||
const options: DropdownOption[] = []
|
||||
if (!isNew.value && props.canEdit && !props.pageMeta?.allow_copy) {
|
||||
options.push({
|
||||
label: t('Duplicate'),
|
||||
key: 'duplicate',
|
||||
icon: 'tabler:copy'
|
||||
})
|
||||
}
|
||||
if (canRename.value) {
|
||||
options.push({
|
||||
label: t('Rename'),
|
||||
key: 'rename',
|
||||
icon: 'tabler:edit'
|
||||
})
|
||||
}
|
||||
return options
|
||||
})
|
||||
|
||||
function renderMenuIcon(option: DropdownOption) {
|
||||
const iconName = option.icon as string
|
||||
if (!iconName) return null
|
||||
return h(NIcon, null, { default: () => h(Icon, { icon: iconName }) })
|
||||
}
|
||||
|
||||
function handleMenuSelect(key: string) {
|
||||
if (key === 'rename') {
|
||||
openRenameDialog()
|
||||
} else if (key === 'duplicate') {
|
||||
handleDuplicate()
|
||||
}
|
||||
}
|
||||
|
||||
// ===== 重复功能 =====
|
||||
const duplicating = ref(false)
|
||||
const message = useMessage()
|
||||
|
||||
async function handleDuplicate() {
|
||||
if (duplicating.value || isNew.value) return
|
||||
duplicating.value = true
|
||||
try {
|
||||
const result = await duplicateRecord(props.entity, props.id)
|
||||
if (result.success && result.data) {
|
||||
const storageKey = `duplicate:${props.entity}`
|
||||
try {
|
||||
sessionStorage.setItem(storageKey, JSON.stringify(result.data))
|
||||
} catch {}
|
||||
emit('duplicate', '')
|
||||
} else {
|
||||
message.error(result.message || t('Failed to duplicate record'))
|
||||
}
|
||||
} catch (err: any) {
|
||||
message.error(err?.message || t('Failed to duplicate record'))
|
||||
} finally {
|
||||
duplicating.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// ===== 重命名对话框 =====
|
||||
const showRenameDialog = ref(false)
|
||||
const renaming = ref(false)
|
||||
const renameFormRef = ref<FormInst | null>(null)
|
||||
const renameForm = ref({
|
||||
newName: '',
|
||||
merge: false
|
||||
})
|
||||
const renameRules: FormRules = {
|
||||
newName: [
|
||||
{ required: true, message: t('New name is required'), trigger: 'blur' }
|
||||
]
|
||||
}
|
||||
|
||||
function openRenameDialog() {
|
||||
renameForm.value.newName = props.id
|
||||
renameForm.value.merge = false
|
||||
showRenameDialog.value = true
|
||||
}
|
||||
|
||||
async function handleRenameConfirm() {
|
||||
try {
|
||||
await renameFormRef.value?.validate()
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
|
||||
const { newName, merge } = renameForm.value
|
||||
if (!newName || newName === props.id) {
|
||||
message.info(t('No changes made'))
|
||||
return false
|
||||
}
|
||||
|
||||
renaming.value = true
|
||||
try {
|
||||
const result = await renameRecord(props.entity, props.id, newName, merge)
|
||||
if (result.success) {
|
||||
showRenameDialog.value = false
|
||||
emit('rename', result.data || newName)
|
||||
} else {
|
||||
message.error(result.message || t('Failed to rename document'))
|
||||
return false
|
||||
}
|
||||
} catch (err: any) {
|
||||
message.error(err?.message || t('Failed to rename document'))
|
||||
return false
|
||||
} finally {
|
||||
renaming.value = false
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user