|
@@ -74,10 +74,51 @@
|
|
|
</a-form>
|
|
</a-form>
|
|
|
</a-modal>
|
|
</a-modal>
|
|
|
|
|
|
|
|
|
|
+ <!-- 添加页面弹框 -->
|
|
|
|
|
+ <a-modal
|
|
|
|
|
+ v-model:open="addPageModalVisible"
|
|
|
|
|
+ title="添加页面"
|
|
|
|
|
+ ok-text="添加"
|
|
|
|
|
+ cancel-text="取消"
|
|
|
|
|
+ :confirm-loading="addPageLoading"
|
|
|
|
|
+ @ok="confirmAddPage"
|
|
|
|
|
+ >
|
|
|
|
|
+ <a-form layout="vertical" class="add-page-form">
|
|
|
|
|
+ <a-form-item label="key(页面唯一标识)" required>
|
|
|
|
|
+ <a-input
|
|
|
|
|
+ v-model:value="addPageKey"
|
|
|
|
|
+ placeholder="如 home、list_1"
|
|
|
|
|
+ allow-clear
|
|
|
|
|
+ />
|
|
|
|
|
+ </a-form-item>
|
|
|
|
|
+ <a-form-item label="title(页面标题)" required>
|
|
|
|
|
+ <a-input
|
|
|
|
|
+ v-model:value="addPageTitle"
|
|
|
|
|
+ placeholder="如 首页、列表页"
|
|
|
|
|
+ allow-clear
|
|
|
|
|
+ />
|
|
|
|
|
+ </a-form-item>
|
|
|
|
|
+ <a-form-item v-if="addPageTemplate" label="模板">
|
|
|
|
|
+ <span>{{ addPageTemplate === 'Home' ? 'Home' : 'CommonList' }}</span>
|
|
|
|
|
+ </a-form-item>
|
|
|
|
|
+ </a-form>
|
|
|
|
|
+ </a-modal>
|
|
|
|
|
+
|
|
|
<div class="editor-body">
|
|
<div class="editor-body">
|
|
|
<!-- 左一:页面列表 -->
|
|
<!-- 左一:页面列表 -->
|
|
|
<div class="panel panel-pages">
|
|
<div class="panel panel-pages">
|
|
|
- <div class="panel-title">页面列表</div>
|
|
|
|
|
|
|
+ <div class="panel-title panel-title-with-action">
|
|
|
|
|
+ <span>页面列表</span>
|
|
|
|
|
+ <a-dropdown size="small">
|
|
|
|
|
+ <PlusOutlined />
|
|
|
|
|
+ <template #overlay>
|
|
|
|
|
+ <a-menu @click="onAddPageMenuClick">
|
|
|
|
|
+ <a-menu-item key="Home">Home</a-menu-item>
|
|
|
|
|
+ <a-menu-item key="CommonList">CommonList</a-menu-item>
|
|
|
|
|
+ </a-menu>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </a-dropdown>
|
|
|
|
|
+ </div>
|
|
|
<a-list
|
|
<a-list
|
|
|
:data-source="currentEditorJson?.page ?? []"
|
|
:data-source="currentEditorJson?.page ?? []"
|
|
|
size="small"
|
|
size="small"
|
|
@@ -86,12 +127,22 @@
|
|
|
<template #renderItem="{ item }">
|
|
<template #renderItem="{ item }">
|
|
|
<a-list-item
|
|
<a-list-item
|
|
|
:class="{ 'page-item-active': selectedPage?.name === item.name }"
|
|
:class="{ 'page-item-active': selectedPage?.name === item.name }"
|
|
|
|
|
+ class="page-list-item"
|
|
|
@click="selectedPage = item"
|
|
@click="selectedPage = item"
|
|
|
>
|
|
>
|
|
|
<a-list-item-meta>
|
|
<a-list-item-meta>
|
|
|
<template #title>{{ item.title || item.name }}</template>
|
|
<template #title>{{ item.title || item.name }}</template>
|
|
|
<template #description>{{ item.name }} · {{ item.content?.type }}</template>
|
|
<template #description>{{ item.name }} · {{ item.content?.type }}</template>
|
|
|
</a-list-item-meta>
|
|
</a-list-item-meta>
|
|
|
|
|
+ <a-button
|
|
|
|
|
+ type="text"
|
|
|
|
|
+ danger
|
|
|
|
|
+ size="small"
|
|
|
|
|
+ class="page-item-delete"
|
|
|
|
|
+ @click.stop="confirmDeletePage(item)"
|
|
|
|
|
+ >
|
|
|
|
|
+ <DeleteOutlined />
|
|
|
|
|
+ </a-button>
|
|
|
</a-list-item>
|
|
</a-list-item>
|
|
|
</template>
|
|
</template>
|
|
|
</a-list>
|
|
</a-list>
|
|
@@ -99,7 +150,10 @@
|
|
|
|
|
|
|
|
<!-- 左二:属性编辑器 -->
|
|
<!-- 左二:属性编辑器 -->
|
|
|
<div class="panel panel-props">
|
|
<div class="panel panel-props">
|
|
|
- <div class="panel-title">属性编辑</div>
|
|
|
|
|
|
|
+ <div class="panel-title panel-title-with-action">
|
|
|
|
|
+ 属性编辑
|
|
|
|
|
+ <a-button type="primary" size="small" @click="($refs.previewRef as any)?.refresh()">刷新</a-button>
|
|
|
|
|
+ </div>
|
|
|
<div v-if="!selectedPage" class="panel-empty">请选择页面</div>
|
|
<div v-if="!selectedPage" class="panel-empty">请选择页面</div>
|
|
|
<PropsEditorTree
|
|
<PropsEditorTree
|
|
|
v-else
|
|
v-else
|
|
@@ -112,6 +166,7 @@
|
|
|
<div class="panel-title">小程序预览</div>
|
|
<div class="panel-title">小程序预览</div>
|
|
|
<div class="preview-wrap">
|
|
<div class="preview-wrap">
|
|
|
<EditorPreview
|
|
<EditorPreview
|
|
|
|
|
+ ref="previewRef"
|
|
|
:editor-json="currentEditorJson"
|
|
:editor-json="currentEditorJson"
|
|
|
:selected-page="selectedPage"
|
|
:selected-page="selectedPage"
|
|
|
/>
|
|
/>
|
|
@@ -125,9 +180,9 @@
|
|
|
<script setup lang="ts">
|
|
<script setup lang="ts">
|
|
|
import { computed, onMounted, provide, ref } from 'vue';
|
|
import { computed, onMounted, provide, ref } from 'vue';
|
|
|
import { ObjectUtils } from '@imengyu/imengyu-utils';
|
|
import { ObjectUtils } from '@imengyu/imengyu-utils';
|
|
|
-import { DownOutlined, DownloadOutlined, InfoCircleFilled } from '@ant-design/icons-vue';
|
|
|
|
|
|
|
+import { DownOutlined, DownloadOutlined, InfoCircleFilled, PlusOutlined, DeleteOutlined } from '@ant-design/icons-vue';
|
|
|
import { message, Modal } from 'ant-design-vue';
|
|
import { message, Modal } from 'ant-design-vue';
|
|
|
-import type { IHomeCommonCategoryDefine } from '../CommonCategoryDefine';
|
|
|
|
|
|
|
+import type { IHomeCommonCategoryDefine, IHomeCommonCategoryListDefine, IHomeCommonCategoryHomeDefine } from '../CommonCategoryDefine';
|
|
|
import DefaultEditorJson from '../DefaultCategory.json';
|
|
import DefaultEditorJson from '../DefaultCategory.json';
|
|
|
import PropsEditorTree from './subpart/PropsEditorTree.vue';
|
|
import PropsEditorTree from './subpart/PropsEditorTree.vue';
|
|
|
import EditorPreview from './subpart/EditorPreview.vue';
|
|
import EditorPreview from './subpart/EditorPreview.vue';
|
|
@@ -159,8 +214,117 @@ const saveAsModalVisible = ref(false);
|
|
|
const saveAsVersionName = ref('');
|
|
const saveAsVersionName = ref('');
|
|
|
const saveAsLoading = ref(false);
|
|
const saveAsLoading = ref(false);
|
|
|
|
|
|
|
|
|
|
+const addPageModalVisible = ref(false);
|
|
|
|
|
+const addPageTemplate = ref<'Home' | 'CommonList' | null>(null);
|
|
|
|
|
+const addPageKey = ref('');
|
|
|
|
|
+const addPageTitle = ref('');
|
|
|
|
|
+const addPageLoading = ref(false);
|
|
|
|
|
+
|
|
|
provide('pageList', pageList);
|
|
provide('pageList', pageList);
|
|
|
|
|
|
|
|
|
|
+/** 生成新页面唯一 name */
|
|
|
|
|
+function getUniquePageName(prefix: string): string {
|
|
|
|
|
+ const pages = currentEditorJson.value?.page ?? [];
|
|
|
|
|
+ const names = new Set(pages.map(p => p.name));
|
|
|
|
|
+ let name = prefix;
|
|
|
|
|
+ let n = 1;
|
|
|
|
|
+ while (names.has(name)) {
|
|
|
|
|
+ name = `${prefix}_${n}`;
|
|
|
|
|
+ n++;
|
|
|
|
|
+ }
|
|
|
|
|
+ return name;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/** Home 模板 */
|
|
|
|
|
+function createHomePageTemplate(): IHomeCommonCategoryHomeDefine {
|
|
|
|
|
+ return {
|
|
|
|
|
+ type: 'Home',
|
|
|
|
|
+ props: {
|
|
|
|
|
+ title: '首页',
|
|
|
|
|
+ subTitle: '',
|
|
|
|
|
+ homeBanner: '',
|
|
|
|
|
+ homeButtons: [],
|
|
|
|
|
+ categorys: [],
|
|
|
|
|
+ },
|
|
|
|
|
+ };
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/** CommonList 模板 */
|
|
|
|
|
+function createCommonListPageTemplate(): IHomeCommonCategoryListDefine {
|
|
|
|
|
+ return {
|
|
|
|
|
+ type: 'CommonList',
|
|
|
|
|
+ props: {
|
|
|
|
|
+ showTab: true,
|
|
|
|
|
+ showSearch: true,
|
|
|
|
|
+ showTotal: true,
|
|
|
|
|
+ tabs: [],
|
|
|
|
|
+ },
|
|
|
|
|
+ };
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function onAddPageMenuClick(e: { key: string }) {
|
|
|
|
|
+ const template = e.key as 'Home' | 'CommonList';
|
|
|
|
|
+ addPageTemplate.value = template;
|
|
|
|
|
+ const prefix = template === 'Home' ? 'home' : 'list';
|
|
|
|
|
+ addPageKey.value = getUniquePageName(prefix);
|
|
|
|
|
+ addPageTitle.value = template === 'Home' ? '首页' : '列表页';
|
|
|
|
|
+ addPageModalVisible.value = true;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function confirmAddPage(): Promise<void> | void {
|
|
|
|
|
+ const key = addPageKey.value?.trim();
|
|
|
|
|
+ const title = addPageTitle.value?.trim();
|
|
|
|
|
+ if (!key) {
|
|
|
|
|
+ message.warning('请输入 key');
|
|
|
|
|
+ return Promise.reject();
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!title) {
|
|
|
|
|
+ message.warning('请输入 title');
|
|
|
|
|
+ return Promise.reject();
|
|
|
|
|
+ }
|
|
|
|
|
+ const pages = currentEditorJson.value?.page ?? [];
|
|
|
|
|
+ const exists = pages.some(p => p.name === key);
|
|
|
|
|
+ if (exists) {
|
|
|
|
|
+ message.warning(`key「${key}」已存在,请使用其他 key`);
|
|
|
|
|
+ return Promise.reject();
|
|
|
|
|
+ }
|
|
|
|
|
+ const template = addPageTemplate.value;
|
|
|
|
|
+ if (!template) return Promise.reject();
|
|
|
|
|
+ addPageLoading.value = true;
|
|
|
|
|
+ const content = template === 'Home'
|
|
|
|
|
+ ? createHomePageTemplate()
|
|
|
|
|
+ : createCommonListPageTemplate();
|
|
|
|
|
+ currentEditorJson.value = {
|
|
|
|
|
+ ...currentEditorJson.value,
|
|
|
|
|
+ page: [...pages, { name: key, title, content }],
|
|
|
|
|
+ };
|
|
|
|
|
+ const added = currentEditorJson.value.page[currentEditorJson.value.page.length - 1];
|
|
|
|
|
+ selectedPage.value = added;
|
|
|
|
|
+ addPageModalVisible.value = false;
|
|
|
|
|
+ addPageLoading.value = false;
|
|
|
|
|
+ message.success(`已添加页面 ${title} (${key})`);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function confirmDeletePage(page: IHomeCommonCategoryDefine['page'][0]) {
|
|
|
|
|
+ Modal.confirm({
|
|
|
|
|
+ title: '确认删除',
|
|
|
|
|
+ content: `确定要删除页面「${page.title || page.name}」吗?删除后不可恢复。`,
|
|
|
|
|
+ okText: '删除',
|
|
|
|
|
+ okType: 'danger',
|
|
|
|
|
+ cancelText: '取消',
|
|
|
|
|
+ onOk() {
|
|
|
|
|
+ const pages = currentEditorJson.value?.page ?? [];
|
|
|
|
|
+ const index = pages.findIndex(p => p.name === page.name);
|
|
|
|
|
+ if (index === -1) return;
|
|
|
|
|
+ const next = pages.filter(p => p.name !== page.name);
|
|
|
|
|
+ currentEditorJson.value = { ...currentEditorJson.value, page: next };
|
|
|
|
|
+ if (selectedPage.value?.name === page.name)
|
|
|
|
|
+ selectedPage.value = next[0] ?? null;
|
|
|
|
|
+ message.success('已删除页面');
|
|
|
|
|
+ },
|
|
|
|
|
+ });
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
async function loadEditorJson(selectDefault = false) {
|
|
async function loadEditorJson(selectDefault = false) {
|
|
|
try {
|
|
try {
|
|
|
//加载基础配置
|
|
//加载基础配置
|
|
@@ -334,6 +498,12 @@ onMounted(async () => {
|
|
|
border-bottom: 1px solid #eee;
|
|
border-bottom: 1px solid #eee;
|
|
|
flex-shrink: 0;
|
|
flex-shrink: 0;
|
|
|
}
|
|
}
|
|
|
|
|
+.panel-title-with-action {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ justify-content: space-between;
|
|
|
|
|
+ gap: 8px;
|
|
|
|
|
+}
|
|
|
.panel-pages {
|
|
.panel-pages {
|
|
|
width: 220px;
|
|
width: 220px;
|
|
|
flex-shrink: 0;
|
|
flex-shrink: 0;
|
|
@@ -365,6 +535,19 @@ onMounted(async () => {
|
|
|
cursor: pointer;
|
|
cursor: pointer;
|
|
|
padding: 8px 12px;
|
|
padding: 8px 12px;
|
|
|
}
|
|
}
|
|
|
|
|
+.page-list-item :deep(.ant-list-item-meta) {
|
|
|
|
|
+ flex: 1;
|
|
|
|
|
+ min-width: 0;
|
|
|
|
|
+}
|
|
|
|
|
+.page-list-item {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ gap: 4px;
|
|
|
|
|
+}
|
|
|
|
|
+.page-item-delete {
|
|
|
|
|
+ flex-shrink: 0;
|
|
|
|
|
+ padding: 0 4px;
|
|
|
|
|
+}
|
|
|
.page-item-active {
|
|
.page-item-active {
|
|
|
background: #e6f7ff;
|
|
background: #e6f7ff;
|
|
|
}
|
|
}
|