优化pagetype默认详情页,更新为支持直接复制详情页工具栏到新的pagetype里面进行修改覆盖默认模板
This commit is contained in:
parent
2760d25b70
commit
67119d3265
@ -17,9 +17,25 @@
|
||||
canEdit,
|
||||
loading,
|
||||
save: handleSave,
|
||||
refresh: handleRefresh,
|
||||
delete: handleDelete,
|
||||
goBack: goBack,
|
||||
toggleSidebarPosition: toggleSidebarPosition,
|
||||
sidebarPosition,
|
||||
router,
|
||||
t
|
||||
}"
|
||||
:entity="entity"
|
||||
:id="id"
|
||||
:record="record"
|
||||
:can-edit="canEdit"
|
||||
:loading="loading"
|
||||
:sidebar-position="sidebarPosition"
|
||||
@toggle-sidebar-position="toggleSidebarPosition"
|
||||
@refresh="handleRefresh"
|
||||
@delete="handleDelete"
|
||||
@go-back="goBack"
|
||||
@save="handleSave"
|
||||
/>
|
||||
<DefaultToolbar
|
||||
v-else
|
||||
|
||||
@ -1,5 +1,53 @@
|
||||
<template>
|
||||
<n-space :size="8">
|
||||
<n-space align="center">
|
||||
<!-- 侧边栏位置切换按钮 -->
|
||||
<n-button
|
||||
type="default"
|
||||
size="medium"
|
||||
@click="$emit('toggle-sidebar-position')"
|
||||
:title="sidebarPosition === 'left' ? '切换到右侧' : '切换到左侧'"
|
||||
class="header-action-btn"
|
||||
>
|
||||
<template #icon>
|
||||
<n-icon>
|
||||
<Icon :icon="sidebarPosition === 'left' ? 'fluent:panel-right-16-regular' : 'fluent:panel-left-16-regular'" />
|
||||
</n-icon>
|
||||
</template>
|
||||
</n-button>
|
||||
|
||||
<!-- 刷新按钮 -->
|
||||
<n-button
|
||||
type="default"
|
||||
size="medium"
|
||||
@click="$emit('refresh')"
|
||||
:disabled="loading || isNew"
|
||||
:title="t('Refresh')"
|
||||
class="header-action-btn"
|
||||
>
|
||||
<template #icon>
|
||||
<n-icon>
|
||||
<Icon icon="tabler:refresh" />
|
||||
</n-icon>
|
||||
</template>
|
||||
</n-button>
|
||||
|
||||
<!-- 删除按钮 -->
|
||||
<n-button
|
||||
type="default"
|
||||
size="medium"
|
||||
@click="$emit('delete')"
|
||||
:disabled="loading || isNew"
|
||||
:title="t('Delete')"
|
||||
class="header-action-btn delete-btn"
|
||||
>
|
||||
<template #icon>
|
||||
<n-icon>
|
||||
<Icon icon="tabler:trash" />
|
||||
</n-icon>
|
||||
</template>
|
||||
</n-button>
|
||||
|
||||
<!-- Edit Schema 按钮 -->
|
||||
<n-button
|
||||
type="default"
|
||||
size="medium"
|
||||
@ -22,33 +70,79 @@
|
||||
:on-save="handleSchemaSave"
|
||||
@close="showSchemaEditor = false"
|
||||
/>
|
||||
|
||||
<!-- 返回按钮 -->
|
||||
<n-button type="default" size="medium" @click="$emit('go-back')" :disabled="loading">
|
||||
<template #icon>
|
||||
<n-icon><Icon icon="tabler:arrow-left" /></n-icon>
|
||||
</template>
|
||||
{{ t('Back') }}
|
||||
</n-button>
|
||||
|
||||
<!-- 保存按钮 -->
|
||||
<n-button
|
||||
type="primary"
|
||||
size="medium"
|
||||
:disabled="loading"
|
||||
@click="$emit('save')"
|
||||
v-if="canEdit"
|
||||
class="save-btn-brand"
|
||||
>
|
||||
<template #icon>
|
||||
<n-icon>
|
||||
<Icon icon="tabler:check" />
|
||||
</n-icon>
|
||||
</template>
|
||||
{{ t('Save') }}
|
||||
</n-button>
|
||||
</n-space>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } from 'vue'
|
||||
import { NSpace, NButton, NIcon, useMessage } from 'naive-ui'
|
||||
import { ref, computed, watch, onMounted } from 'vue'
|
||||
import { NButton, NSpace, NIcon, useMessage } from 'naive-ui'
|
||||
import { Icon } from '@iconify/vue'
|
||||
import { t } from '@/shared/i18n'
|
||||
import SchemaEditorModal from '@/core/components/SchemaEditorModal.vue'
|
||||
import axios from 'axios'
|
||||
import { get_session_api_headers } from '@/shared/api/auth'
|
||||
|
||||
const props = defineProps<{ context: {
|
||||
entity: any
|
||||
id: any
|
||||
record: any
|
||||
canEdit: any
|
||||
loading: any
|
||||
save: () => Promise<void> | void
|
||||
router: any
|
||||
t: (k: string) => string
|
||||
} }>()
|
||||
interface Props {
|
||||
entity: string
|
||||
id: string
|
||||
record?: any
|
||||
canEdit: boolean
|
||||
loading: boolean
|
||||
sidebarPosition?: 'left' | 'right'
|
||||
}
|
||||
|
||||
interface Emits {
|
||||
(e: 'toggle-sidebar-position'): void
|
||||
(e: 'refresh'): void
|
||||
(e: 'delete'): void
|
||||
(e: 'go-back'): void
|
||||
(e: 'save'): void
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
sidebarPosition: 'left',
|
||||
record: () => ({})
|
||||
})
|
||||
|
||||
const emit = defineEmits<Emits>()
|
||||
|
||||
const message = useMessage()
|
||||
const t = props.context.t
|
||||
const isNew = computed(() => String(props.context.id?.value || props.context.id) === 'new')
|
||||
const showSchemaEditor = ref(false)
|
||||
|
||||
const nodeName = computed(() => String(props.context.id?.value || props.context.id))
|
||||
const nodeRecord = computed(() => props.context.record?.value || props.context.record || {})
|
||||
// 计算是否为新建记录
|
||||
const isNew = computed(() => {
|
||||
const idValue = props.id
|
||||
return idValue === 'new' || idValue.startsWith('new-')
|
||||
})
|
||||
|
||||
// Schema 编辑器相关
|
||||
const showSchemaEditor = ref(false)
|
||||
const nodeName = computed(() => String(props.id))
|
||||
const nodeRecord = computed(() => props.record || {})
|
||||
|
||||
function openSchemaEditor() {
|
||||
if (isNew.value) return
|
||||
@ -63,28 +157,150 @@ function openSchemaEditor() {
|
||||
|
||||
async function handleSchemaSave(schemaData: any) {
|
||||
try {
|
||||
// 更新本地记录
|
||||
if (props.context.record?.value) {
|
||||
props.context.record.value.node_schema = schemaData
|
||||
} else if (props.context.record) {
|
||||
props.context.record.node_schema = schemaData
|
||||
}
|
||||
// 保存 Schema 数据到后端
|
||||
const response = await axios.put(
|
||||
`/api/data/${encodeURIComponent(props.entity)}/${encodeURIComponent(nodeName.value)}`,
|
||||
{
|
||||
node_schema: schemaData
|
||||
},
|
||||
{
|
||||
headers: get_session_api_headers(),
|
||||
withCredentials: true
|
||||
}
|
||||
)
|
||||
|
||||
// 调用父组件的保存方法(不传参数,让父组件处理)
|
||||
if (props.context.save) {
|
||||
await props.context.save()
|
||||
if (response.data?.success !== false) {
|
||||
// 更新本地记录
|
||||
if (props.record) {
|
||||
props.record.node_schema = schemaData
|
||||
}
|
||||
message.success(t('Schema saved successfully'))
|
||||
showSchemaEditor.value = false
|
||||
} else {
|
||||
throw new Error('No save method provided')
|
||||
throw new Error(response.data?.message || t('Save failed'))
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
throw e // 让组件处理错误显示
|
||||
} catch (error: any) {
|
||||
console.error('保存 Schema 失败:', error)
|
||||
message.error(error?.response?.data?.message || error?.message || t('Save failed, please check permission and server logs'))
|
||||
throw error // 让组件处理错误显示
|
||||
}
|
||||
}
|
||||
|
||||
// 从 localStorage 读取侧边栏位置(如果父组件没有传入)
|
||||
const sidebarPosition = ref<'left' | 'right'>(props.sidebarPosition || 'left')
|
||||
|
||||
// 监听 props 变化
|
||||
watch(() => props.sidebarPosition, (newPos) => {
|
||||
if (newPos) {
|
||||
sidebarPosition.value = newPos
|
||||
}
|
||||
}, { immediate: true })
|
||||
|
||||
onMounted(() => {
|
||||
// 如果没有传入 sidebarPosition,尝试从 localStorage 读取
|
||||
if (!props.sidebarPosition) {
|
||||
const savedPosition = localStorage.getItem('jingrow-sidebar-position')
|
||||
if (savedPosition === 'left' || savedPosition === 'right') {
|
||||
sidebarPosition.value = savedPosition
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 头部操作按钮统一样式 */
|
||||
.header-action-btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
/* 删除按钮悬浮时使用红色 */
|
||||
.header-action-btn.delete-btn:hover:not(:disabled) {
|
||||
background: #ef4444 !important;
|
||||
border-color: #ef4444 !important;
|
||||
color: white !important;
|
||||
}
|
||||
|
||||
.header-action-btn.delete-btn:hover:not(:disabled) :deep(.n-button__border),
|
||||
.header-action-btn.delete-btn:hover:not(:disabled) :deep(.n-button__state-border) {
|
||||
border-color: #ef4444 !important;
|
||||
}
|
||||
|
||||
/* 保存按钮 - 使用柔和的品牌色系,与列表页创建按钮一致 */
|
||||
.save-btn-brand {
|
||||
background: #e6f8f0 !important;
|
||||
border: 1px solid #1fc76f !important;
|
||||
color: #0d684b !important;
|
||||
}
|
||||
|
||||
.save-btn-brand :deep(.n-button__border) {
|
||||
border: none !important;
|
||||
border-color: transparent !important;
|
||||
}
|
||||
|
||||
.save-btn-brand :deep(.n-button__state-border) {
|
||||
border: none !important;
|
||||
border-color: transparent !important;
|
||||
}
|
||||
|
||||
.save-btn-brand:hover {
|
||||
background: #dcfce7 !important;
|
||||
border-color: #1fc76f !important;
|
||||
border: 1px solid #1fc76f !important;
|
||||
color: #166534 !important;
|
||||
box-shadow: 0 2px 8px rgba(31, 199, 111, 0.15) !important;
|
||||
}
|
||||
|
||||
.save-btn-brand:hover :deep(.n-button__border),
|
||||
.save-btn-brand:hover :deep(.n-button__state-border) {
|
||||
border: none !important;
|
||||
border-color: transparent !important;
|
||||
}
|
||||
|
||||
.save-btn-brand:focus {
|
||||
background: #dcfce7 !important;
|
||||
border-color: #1fc76f !important;
|
||||
border: 1px solid #1fc76f !important;
|
||||
color: #166534 !important;
|
||||
box-shadow: 0 0 0 2px rgba(31, 199, 111, 0.2) !important;
|
||||
}
|
||||
|
||||
.save-btn-brand:focus :deep(.n-button__border),
|
||||
.save-btn-brand:focus :deep(.n-button__state-border) {
|
||||
border: none !important;
|
||||
border-color: transparent !important;
|
||||
}
|
||||
|
||||
.save-btn-brand:active {
|
||||
background: #1fc76f !important;
|
||||
border-color: #1fc76f !important;
|
||||
border: 1px solid #1fc76f !important;
|
||||
color: white !important;
|
||||
box-shadow: 0 1px 4px rgba(31, 199, 111, 0.2) !important;
|
||||
}
|
||||
|
||||
.save-btn-brand:active :deep(.n-button__border),
|
||||
.save-btn-brand:active :deep(.n-button__state-border) {
|
||||
border: none !important;
|
||||
border-color: transparent !important;
|
||||
}
|
||||
|
||||
.save-btn-brand:disabled {
|
||||
background: #f1f5f9 !important;
|
||||
border: 1px solid #e2e8f0 !important;
|
||||
border-color: #e2e8f0 !important;
|
||||
color: #94a3b8 !important;
|
||||
opacity: 0.6 !important;
|
||||
cursor: not-allowed !important;
|
||||
}
|
||||
|
||||
.save-btn-brand:disabled :deep(.n-button__border),
|
||||
.save-btn-brand:disabled :deep(.n-button__state-border) {
|
||||
border: none !important;
|
||||
border-color: transparent !important;
|
||||
}
|
||||
|
||||
/* 工具栏按钮基础样式 - 与返回按钮一致,使用badge配色方案 */
|
||||
.toolbar-btn {
|
||||
background: #f3f4f6 !important;
|
||||
@ -136,3 +352,4 @@ async function handleSchemaSave(schemaData: any) {
|
||||
border-color: transparent !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user