|
|
@@ -18,6 +18,11 @@
|
|
|
<a-collapse v-model:activeKey="activeKeys" class="props-collapse">
|
|
|
<a-collapse-panel key="introBlockDescs" header="简介块描述项 (introBlockDescs)">
|
|
|
<div v-for="(item, i) in introBlockDescsList" :key="`desc-${i}`" class="nested-item">
|
|
|
+ <div class="item-actions">
|
|
|
+ <ArrowUpOutlined title="上移" @click.stop="moveIntroBlockDescUp(i)" />
|
|
|
+ <ArrowDownOutlined title="下移" @click.stop="moveIntroBlockDescDown(i)" />
|
|
|
+ <CopyOutlined title="复制" @click.stop="copyIntroBlockDesc(i)" />
|
|
|
+ </div>
|
|
|
<a-form :labelCol="{ span: 6 }" size="small">
|
|
|
<a-form-item label="label">
|
|
|
<a-input v-model:value="item.label" placeholder="显示标签" />
|
|
|
@@ -33,11 +38,19 @@
|
|
|
</a-popconfirm>
|
|
|
</a-form>
|
|
|
</div>
|
|
|
- <a-button type="dashed" block size="small" @click="addIntroBlockDesc">+ 添加描述项</a-button>
|
|
|
+ <div class="section-footer">
|
|
|
+ <a-button style="flex: 4;" type="dashed" block size="small" @click="addIntroBlockDesc">+ 添加描述项</a-button>
|
|
|
+ <a-button style="flex: 2;" type="dashed" block size="small" @click="pasteIntroBlockDesc">粘贴</a-button>
|
|
|
+ </div>
|
|
|
</a-collapse-panel>
|
|
|
|
|
|
<a-collapse-panel key="introBlocks" header="简介下方块 (introBlocks)">
|
|
|
<div v-for="(item, i) in introBlocksList" :key="`block-${i}`" class="nested-item">
|
|
|
+ <div class="item-actions">
|
|
|
+ <ArrowUpOutlined title="上移" @click.stop="moveIntroBlockUp(i)" />
|
|
|
+ <ArrowDownOutlined title="下移" @click.stop="moveIntroBlockDown(i)" />
|
|
|
+ <CopyOutlined title="复制" @click.stop="copyIntroBlock(i)" />
|
|
|
+ </div>
|
|
|
<a-form :labelCol="{ span: 6 }" size="small">
|
|
|
<a-form-item label="类型">
|
|
|
<a-input v-model:value="item.type" placeholder="块类型" />
|
|
|
@@ -50,13 +63,23 @@
|
|
|
</a-popconfirm>
|
|
|
</a-form>
|
|
|
</div>
|
|
|
- <a-button type="dashed" block size="small" @click="addIntroBlock">+ 添加块</a-button>
|
|
|
+ <div class="section-footer">
|
|
|
+ <a-button style="flex: 4;" type="dashed" block size="small" @click="addIntroBlock">+ 添加块</a-button>
|
|
|
+ <a-button style="flex: 2;" type="dashed" block size="small" @click="pasteIntroBlock">粘贴</a-button>
|
|
|
+ </div>
|
|
|
</a-collapse-panel>
|
|
|
|
|
|
<a-collapse-panel key="tabs" header="详情 Tab (tabs)">
|
|
|
<div v-for="(tab, i) in tabItems" :key="tabKey(tab, i)" class="nested-item tab-item">
|
|
|
<a-collapse>
|
|
|
<a-collapse-panel :key="i" :header="tabHeader(tab)">
|
|
|
+ <template #extra>
|
|
|
+ <div class="item-actions">
|
|
|
+ <ArrowUpOutlined title="上移" @click.stop="moveTabUp(i)" />
|
|
|
+ <ArrowDownOutlined title="下移" @click.stop="moveTabDown(i)" />
|
|
|
+ <CopyOutlined title="复制" @click.stop="copyTab(i)" />
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
<a-form :labelCol="{ span: 4 }" size="small">
|
|
|
<a-form-item label="文本">
|
|
|
<a-input v-model:value="tab.text" />
|
|
|
@@ -104,7 +127,10 @@
|
|
|
</a-collapse-panel>
|
|
|
</a-collapse>
|
|
|
</div>
|
|
|
- <a-button type="dashed" block size="small" @click="addTab">+ 添加 Tab</a-button>
|
|
|
+ <div class="section-footer">
|
|
|
+ <a-button style="flex: 4;" type="dashed" block size="small" @click="addTab">+ 添加 Tab</a-button>
|
|
|
+ <a-button style="flex: 2;" type="dashed" block size="small" @click="pasteTab">粘贴</a-button>
|
|
|
+ </div>
|
|
|
</a-collapse-panel>
|
|
|
</a-collapse>
|
|
|
</div>
|
|
|
@@ -122,6 +148,8 @@ import type { IHomeCommonCategoryListDefine } from '../../CommonCategoryDefine';
|
|
|
import CommonListPropsEditor from './CommonListPropsEditor.vue';
|
|
|
import NestCategoryEditor from '../subpart/NestCategoryEditor.vue';
|
|
|
import KeyValueEditor from '../components/KeyValueEditor.vue';
|
|
|
+import { ArrowUpOutlined, ArrowDownOutlined, CopyOutlined } from '@ant-design/icons-vue';
|
|
|
+import { ArrayUtils } from '@imengyu/imengyu-utils';
|
|
|
|
|
|
const props = defineProps<{
|
|
|
props: IHomeCommonCategoryDetailDefine['props'];
|
|
|
@@ -155,21 +183,72 @@ function tabHeader(tab: IHomeCommonCategoryDetailTabItemDefine) {
|
|
|
return `${tab.text || 'Tab'} (${tab.type || '?'})`;
|
|
|
}
|
|
|
|
|
|
+function getIntroBlockDescs() {
|
|
|
+ if (!props.props.introBlockDescs) props.props.introBlockDescs = [];
|
|
|
+ return props.props.introBlockDescs;
|
|
|
+}
|
|
|
function addIntroBlockDesc() {
|
|
|
- props.props.introBlockDescs = props.props.introBlockDescs ?? [];
|
|
|
- props.props.introBlockDescs.push({ label: '', key: '' });
|
|
|
+ getIntroBlockDescs().push({ label: '', key: '' });
|
|
|
}
|
|
|
function removeIntroBlockDesc(i: number) {
|
|
|
- if (!props.props.introBlockDescs) return;
|
|
|
- props.props.introBlockDescs.splice(i, 1);
|
|
|
+ getIntroBlockDescs().splice(i, 1);
|
|
|
+}
|
|
|
+function moveIntroBlockDescUp(i: number) {
|
|
|
+ ArrayUtils.upData(getIntroBlockDescs(), i);
|
|
|
+}
|
|
|
+function moveIntroBlockDescDown(i: number) {
|
|
|
+ ArrayUtils.downData(getIntroBlockDescs(), i);
|
|
|
+}
|
|
|
+function copyIntroBlockDesc(i: number) {
|
|
|
+ const item = getIntroBlockDescs()[i];
|
|
|
+ uni.setClipboardData({
|
|
|
+ data: JSON.stringify({ type: 'Copy:DetailIntroBlockDesc', data: item }),
|
|
|
+ });
|
|
|
+ uni.showToast({ title: '复制成功', icon: 'success' });
|
|
|
+}
|
|
|
+async function pasteIntroBlockDesc() {
|
|
|
+ const res = await uni.getClipboardData();
|
|
|
+ const data = (res as { data?: string })?.data;
|
|
|
+ if (typeof data === 'string') {
|
|
|
+ try {
|
|
|
+ const json = JSON.parse(data);
|
|
|
+ if (json.type === 'Copy:DetailIntroBlockDesc') getIntroBlockDescs().push(json.data);
|
|
|
+ } catch (_) {}
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+function getIntroBlocks() {
|
|
|
+ if (!props.props.introBlocks) props.props.introBlocks = [];
|
|
|
+ return props.props.introBlocks;
|
|
|
}
|
|
|
function addIntroBlock() {
|
|
|
- props.props.introBlocks = props.props.introBlocks ?? [];
|
|
|
- props.props.introBlocks.push({ type: '', props: {} });
|
|
|
+ getIntroBlocks().push({ type: '', props: {} });
|
|
|
}
|
|
|
function removeIntroBlock(i: number) {
|
|
|
- if (!props.props.introBlocks) return;
|
|
|
- props.props.introBlocks.splice(i, 1);
|
|
|
+ getIntroBlocks().splice(i, 1);
|
|
|
+}
|
|
|
+function moveIntroBlockUp(i: number) {
|
|
|
+ ArrayUtils.upData(getIntroBlocks(), i);
|
|
|
+}
|
|
|
+function moveIntroBlockDown(i: number) {
|
|
|
+ ArrayUtils.downData(getIntroBlocks(), i);
|
|
|
+}
|
|
|
+function copyIntroBlock(i: number) {
|
|
|
+ const item = getIntroBlocks()[i];
|
|
|
+ uni.setClipboardData({
|
|
|
+ data: JSON.stringify({ type: 'Copy:DetailIntroBlock', data: item }),
|
|
|
+ });
|
|
|
+ uni.showToast({ title: '复制成功', icon: 'success' });
|
|
|
+}
|
|
|
+async function pasteIntroBlock() {
|
|
|
+ const res = await uni.getClipboardData();
|
|
|
+ const data = (res as { data?: string })?.data;
|
|
|
+ if (typeof data === 'string') {
|
|
|
+ try {
|
|
|
+ const json = JSON.parse(data);
|
|
|
+ if (json.type === 'Copy:DetailIntroBlock') getIntroBlocks().push(json.data);
|
|
|
+ } catch (_) {}
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
/** 创建默认列表定义 */
|
|
|
@@ -203,18 +282,44 @@ function onTabTypeChange(tab: IHomeCommonCategoryDetailTabItemDefine, newType: s
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+function getTabs() {
|
|
|
+ if (!props.props.tabs) props.props.tabs = [];
|
|
|
+ return props.props.tabs;
|
|
|
+}
|
|
|
function addTab() {
|
|
|
- props.props.tabs = props.props.tabs ?? [];
|
|
|
const newTab: IHomeCommonCategoryDetailTabItemDefine = {
|
|
|
text: '新 Tab',
|
|
|
key: '',
|
|
|
type: 'intro',
|
|
|
visible: true,
|
|
|
};
|
|
|
- props.props.tabs.push(newTab);
|
|
|
+ getTabs().push(newTab);
|
|
|
}
|
|
|
function removeTab(i: number) {
|
|
|
- props.props.tabs?.splice(i, 1);
|
|
|
+ getTabs().splice(i, 1);
|
|
|
+}
|
|
|
+function moveTabUp(i: number) {
|
|
|
+ ArrayUtils.upData(getTabs(), i);
|
|
|
+}
|
|
|
+function moveTabDown(i: number) {
|
|
|
+ ArrayUtils.downData(getTabs(), i);
|
|
|
+}
|
|
|
+function copyTab(i: number) {
|
|
|
+ const tab = getTabs()[i];
|
|
|
+ uni.setClipboardData({
|
|
|
+ data: JSON.stringify({ type: 'Copy:DetailTabItem', data: tab }),
|
|
|
+ });
|
|
|
+ uni.showToast({ title: '复制成功', icon: 'success' });
|
|
|
+}
|
|
|
+async function pasteTab() {
|
|
|
+ const res = await uni.getClipboardData();
|
|
|
+ const data = (res as { data?: string })?.data;
|
|
|
+ if (typeof data === 'string') {
|
|
|
+ try {
|
|
|
+ const json = JSON.parse(data);
|
|
|
+ if (json.type === 'Copy:DetailTabItem') getTabs().push(json.data);
|
|
|
+ } catch (_) {}
|
|
|
+ }
|
|
|
}
|
|
|
</script>
|
|
|
|
|
|
@@ -231,6 +336,19 @@ function removeTab(i: number) {
|
|
|
background: #fafafa;
|
|
|
border-radius: 4px;
|
|
|
}
|
|
|
+.item-actions {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: row;
|
|
|
+ gap: 8px;
|
|
|
+ margin-bottom: 8px;
|
|
|
+}
|
|
|
+.section-footer {
|
|
|
+ display: flex;
|
|
|
+ gap: 8px;
|
|
|
+}
|
|
|
+.section-footer .ant-btn:first-child {
|
|
|
+ flex: 1;
|
|
|
+}
|
|
|
.tab-item :deep(.ant-collapse) {
|
|
|
border: none;
|
|
|
background: transparent;
|