<template>
<plugin-setting
v-if="isShow"
:fixed-name="PLUGIN_NAME.AppManage"
:align="align"
:title="state.title"
class="page-plugin-setting"
>
<template #header>
<button-group>
<tiny-button type="primary" @click="savePageSetting">保存</tiny-button>
<svg-button
v-if="!pageSettingState.isNew"
name="text-copy-page"
placement="bottom"
tips="复制页面"
@click="copyPage"
></svg-button>
<svg-button v-if="!pageSettingState.isNew" name="delete" tips="删除页面" @click="deletePage"></svg-button>
<svg-button name="close" @click="cancelPageSetting"></svg-button>
</button-group>
</template>
<template #content>
<div class="page-setting-content">
<tiny-collapse v-model="state.activeName" class="page-setting-collapse">
<tiny-collapse-item title="基本设置" :name="PAGE_SETTING_SESSION.general">
<component :is="pageGeneral" ref="pageGeneralRef" :isFolder="isFolder"></component>
</tiny-collapse-item>
<tiny-collapse-item
class="base-setting"
v-if="pageSettingState.currentPageData.group !== 'public'"
title="输入输出"
:name="PAGE_SETTING_SESSION.inputOutput"
>
<page-input-output></page-input-output>
</tiny-collapse-item>
<tiny-collapse-item
class="input-output"
v-if="pageSettingState.currentPageData.group !== 'public'"
title="页面生命周期配置"
:name="PAGE_SETTING_SESSION.lifeCycles"
>
<div class="life-cycles-container">
<life-cycles
:bindLifeCycles="pageSettingState.currentPageData.page_content?.lifeCycles"
@updatePageLifeCycles="updatePageLifeCycles"
></life-cycles>
</div>
</tiny-collapse-item>
<tiny-collapse-item class="history-source" title="历史备份" :name="PAGE_SETTING_SESSION.history">
<page-history @restorePage="restorePage"></page-history>
</tiny-collapse-item>
</tiny-collapse>
</div>
</template>
</plugin-setting>
</template>
<script lang="jsx">
/* metaService: engine.plugins.appmanage.PageSetting */
import { reactive, ref, computed, onActivated, onDeactivated } from 'vue'
import { Button, Collapse, CollapseItem, Input } from '@opentiny/vue'
import { PluginSetting, ButtonGroup, SvgButton, LifeCycles } from '@opentiny/tiny-engine-common'
import {
useLayout,
usePage,
useCanvas,
useModal,
useNotify,
getMergeMeta,
getMetaApi,
META_SERVICE,
useMessage
} from '@opentiny/tiny-engine-meta-register'
import { extend, isEqual } from '@opentiny/vue-renderless/common/object'
import { constants } from '@opentiny/tiny-engine-utils'
import { isVsCodeEnv } from '@opentiny/tiny-engine-common/js/environments'
import { handlePageUpdate } from '@opentiny/tiny-engine-common/js/http'
import { generatePage } from '@opentiny/tiny-engine-common/js/vscodeGenerateFile'
import PageHistory from './PageHistory.vue'
import PageInputOutput from './PageInputOutput.vue'
import meta from '../meta'
import http from './http'
const { COMPONENT_NAME } = constants
const isShow = ref(false)
export const openPageSettingPanel = () => {
isShow.value = true
}
export const closePageSettingPanel = () => {
isShow.value = false
const { resetPageData } = usePage()
resetPageData()
}
const PAGE_SETTING_SESSION = {
general: 'general',
inputOutput: 'inputOutput',
lifeCycles: 'lifeCycles',
history: 'history'
}
export default {
components: {
TinyButton: Button,
TinyCollapse: Collapse,
TinyCollapseItem: CollapseItem,
PageInputOutput,
LifeCycles,
PageHistory,
PluginSetting,
SvgButton,
ButtonGroup
},
props: {
isFolder: {
type: Boolean,
default: false
}
},
emits: ['openNewPage'],
setup(props, { emit }) {
const { requestCreatePage, requestDeletePage } = http
const {
getDefaultPage,
pageSettingState,
changeTreeData,
isCurrentDataSame,
initCurrentPageData,
isTemporaryPage,
STATIC_PAGE_GROUP_ID,
updatePageSettingAfterSave
} = usePage()
const { pageState, initData } = useCanvas()
const { confirm } = useModal()
const registry = getMergeMeta(meta.id)
const pageGeneral = registry.components.PageGeneral
const beforeCreatePage = registry?.options?.beforeCreatePage
const pageGeneralRef = ref(null)
const { PLUGIN_NAME, getPluginByLayout } = useLayout()
const align = computed(() => getPluginByLayout(PLUGIN_NAME.AppManage))
const { subscribe, unsubscribe } = useMessage()
// 初始化订阅
let subscriber = null
// 组件激活时订阅
onActivated(() => {
subscriber = subscribe({
topic: 'page-saved',
callback: () => {
// 当收到页面保存事件时,更新页面设置状态
updatePageSettingAfterSave()
}
})
})
// 组件卸载或失活时取消订阅
onDeactivated(() => {
if (subscriber) {
unsubscribe(subscriber)
}
})
const state = reactive({
activeName: Object.values(PAGE_SETTING_SESSION),
title: '页面设置',
historyMessage: ''
})
const cancelPageSetting = () => {
if (isEqual(pageSettingState.currentPageData, pageSettingState.currentPageDataCopy)) {
closePageSettingPanel()
} else {
confirm({
title: '提示',
message: '您即将使用未保存的更改关闭此页。是否要在关闭之前放弃这些更改?',
exec: () => {
if (!pageSettingState.isNew) {
changeTreeData(pageSettingState.oldParentId, pageSettingState.currentPageData.parentId)
Object.assign(pageSettingState.currentPageData, pageSettingState.currentPageDataCopy)
}
closePageSettingPanel()
}
})
}
}
const createPage = async () => {
const { page_content, ...other } = getDefaultPage()
const { page_content: page_content_state, ...pageSettingStateOther } = pageSettingState.currentPageData
const createParams = {
...other,
...pageSettingStateOther,
page_content: {
...page_content,
...page_content_state,
fileName: pageSettingState.currentPageData.name
},
app: getMetaApi(META_SERVICE.GlobalService).getBaseInfo().id,
isPage: true
}
if (createParams.id) {
delete createParams.id
delete createParams._id
}
if (beforeCreatePage) {
await beforeCreatePage(createParams)
}
try {
const data = await requestCreatePage(createParams)
await pageSettingState.updateTreeData()
pageSettingState.isNew = false
isTemporaryPage.saved = false
emit('openNewPage', data)
closePageSettingPanel()
useLayout().closePlugin()
useNotify({
type: 'success',
message: '新建页面成功!'
})
if (isVsCodeEnv) {
generatePage(data)
}
} catch (err) {
useNotify({
type: 'error',
title: '新建页面失败',
message: JSON.stringify(err?.message || err)
})
}
}
const updatePage = (id, params, isUpdateTree = true) => {
const routerChange = pageSettingState.currentPageDataCopy.route !== pageSettingState.currentPageData.route
const isCurEditPage = pageState?.currentPage?.id === id
const updateParams = {
id,
params,
routerChange,
isCurEditPage,
isUpdateTree
}
return handlePageUpdate(updateParams)
}
const restorePage = (pageData) => {
const currentData = {
...pageData,
id: pageData.page
}
const unnecessaryFields = ['page', 'backupTime', 'backupTitle', 'time']
unnecessaryFields.forEach((key) => delete currentData[key])
const params = {
...pageSettingState.currentPageData,
...currentData,
message: '还原页面'
}
updatePage(currentData.id, params).then((data) => {
// 还原的页面是当前页,需要同步更新画布
if (pageState?.currentPage?.id === data?.id) {
initData(data.page_content, data)
}
initCurrentPageData(data)
})
}
const editPage = async () => {
// 更新页面
const { id, name, page_content } = pageSettingState.currentPageData
const params = {
...pageSettingState.currentPageData,
page_content: {
...page_content,
fileName: name
}
}
const res = await updatePage(id, params)
initCurrentPageData(res)
}
const updatePageLifeCycles = (val) => {
if (!val) {
return
}
const pageContent = pageSettingState.currentPageData.page_content
pageContent.lifeCycles = {
...(pageContent.lifeCycles || {}),
...val
}
}
const copyPageData = () => {
const data = pageSettingState.currentPageData
const copyData = extend(true, {}, data)
pageSettingState.isNew = true
copyData.name = `${copyData.name}Copy`
copyData.route = `${copyData.route}Copy`
pageSettingState.currentPageData = copyData
pageSettingState.currentPageDataCopy = extend(true, {}, copyData)
pageSettingState.defaultPage = null
}
const copyPage = () => {
if (!isCurrentDataSame()) {
confirm({
title: '提示',
message: '您即将复制的页面有更改未保存,是否确定跳过更改直接复制?',
exec: () => {
changeTreeData(pageSettingState.oldParentId, pageSettingState.currentPageData.parentId)
Object.assign(pageSettingState.currentPageData, pageSettingState.currentPageDataCopy)
copyPageData()
}
})
} else {
copyPageData()
}
}
const settingDefaultPage = async () => {
const params = { ...pageSettingState.defaultPage, isDefault: true }
updatePage(pageSettingState.defaultPage?.id, params, false).then((res) => {
if (res) {
editPage()
}
})
}
const createHistoryMessage = () => {
if (pageSettingState.isNew) {
pageSettingState.currentPageData.message = 'Page auto save'
createPage()
} else {
const title = '创建历史备份信息'
const messageRender = {
render: () => <Input placeholder="历史备份信息" v-model={state.historyMessage}></Input>
}
const exec = () => {
pageSettingState.currentPageData.message = state.historyMessage.trim() || 'Page auto save'
if (pageSettingState.defaultPage?.id) {
settingDefaultPage()
} else {
editPage()
}
state.historyMessage = ''
}
confirm({ title, message: messageRender, exec })
}
}
const savePageSetting = () => {
pageGeneralRef.value.validGeneralForm().then(createHistoryMessage)
}
const collectAllPage = (staticPageList = []) => {
if (!Array.isArray(staticPageList)) {
return []
}
const pageList = []
staticPageList.forEach((pageItem) => {
if (pageItem?.isPage) {
pageList.push(pageItem)
return
}
if (!pageItem?.isPage && pageItem?.children?.length) {
pageList.push(...collectAllPage(pageItem.children))
}
})
return pageList
}
const deletePage = () => {
if (pageSettingState.treeDataMapping[pageSettingState.currentPageData.id]?.children?.length) {
useNotify({
type: 'error',
message: '此页面存在子页面或子文件夹,不能删除!'
})
return
}
confirm({
title: '提示',
message: '您是否要删除页面?',
exec: () => {
const id = pageSettingState.currentPageData?.id || ''
requestDeletePage(id)
.then(() => {
pageSettingState.updateTreeData().then((pages) => {
if (pageState?.currentPage?.id !== id) {
return
}
const staticPageList = (pages || []).find(({ groupId }) => groupId === STATIC_PAGE_GROUP_ID)?.data || []
const pageList = collectAllPage(staticPageList)
const pageHome = pageList.find((page) => page.isHome)
const firstPage = pageList?.[0]
const defaultPage = {
componentName: COMPONENT_NAME.Page
}
emit('openNewPage', pageHome || firstPage || defaultPage)
})
closePageSettingPanel()
useNotify({ message: '删除页面成功!', type: 'success' })
})
.catch(() => {
useNotify({ message: '删除页面失败!', type: 'error' })
})
}
})
}
return {
align,
PLUGIN_NAME,
state,
isShow,
savePageSetting,
copyPage,
pageSettingState,
pageGeneral,
pageGeneralRef,
deletePage,
cancelPageSetting,
closePageSettingPanel,
updatePageLifeCycles,
restorePage,
PAGE_SETTING_SESSION
}
}
}
</script>
<style lang="less" scoped>
.block-add-content {
display: flex;
flex-direction: column;
height: calc(100% - 45px);
}
.page-plugin-setting {
:deep(.plugin-setting-header) {
border: 0;
}
:deep(.plugin-setting-content) {
padding: 0 0 16px 0;
}
:deep(.tiny-collapse) {
border-bottom: 0;
}
}
.page-setting-collapse {
:deep(.tiny-collapse-item__header) {
&,
&.is-active {
&::before {
border: none;
}
}
.svg-icon {
margin-right: 6px;
}
}
:deep(.tiny-collapse-item__content) {
padding: 0 12px 12px;
}
}
.input-output {
:deep(.tiny-collapse-item__content) {
margin-bottom: 4px;
}
}
</style>