Selaa lähdekoodia

🎨 按要求调整栏目

快乐的梦鱼 4 päivää sitten
vanhempi
commit
e147c4aa05

+ 1 - 1
src/pages/article/data/DefaultCategory.json

@@ -1022,7 +1022,7 @@
                     "ich"
                   ],
                   "morePage": "/pages/article/data/list?pageConfigName=seminar&tab=0",
-                  "detailsPage": "/pages/inhert/seminar/details",
+                  "detailsPage": "/pages/article/data/details?pageConfigName=seminar-details",
                   "type": ""
                 },
                 {

+ 0 - 1
src/pages/article/data/editor/MiniProgramEditor.vue

@@ -273,7 +273,6 @@ function createDetailPageTemplate(): IHomeCommonCategoryDetailDefine {
     type: 'Details',
     props: {
       showHead: true,
-      hasInternalTabs: false,
       introBlockDescs: [],
       introBlocks: [],
       tabs: [],

+ 53 - 1
src/pages/article/data/editor/editors/CommonListPropsEditor.vue

@@ -23,6 +23,13 @@
         <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="list-tab-buttons">
+                  <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" />
@@ -91,7 +98,10 @@
             </a-collapse-panel>
           </a-collapse>
         </div>
-        <a-button type="dashed" block size="small" @click="addTab">+ 添加 Tab</a-button>
+        <div class="tabs-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>
@@ -107,6 +117,8 @@ import DataSolveEditor from '../components/DataSolveEditor.vue';
 import NestCategoryEditor from '../subpart/NestCategoryEditor.vue';
 import ItemTypeEditor from '../components/ItemTypeEditor.vue';
 import DropdownDefinesEditor from '../components/DropdownDefinesEditor.vue';
+import { ArrowUpOutlined, ArrowDownOutlined, CopyOutlined } from '@ant-design/icons-vue';
+import { ArrayUtils } from '@imengyu/imengyu-utils';
 
 type TabItem = IHomeCommonCategoryListTabItemDefine;
 
@@ -140,6 +152,34 @@ function addTab() {
 function removeTab(i: number) {
   props.props!.tabs?.splice(i, 1);
 }
+
+function getTabs() {
+  if (!props.props!.tabs) props.props!.tabs = [];
+  return props.props!.tabs as TabItem[];
+}
+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:CommonListTabItem', 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:CommonListTabItem') getTabs().push(json.data);
+    } catch (_) {}
+  }
+}
 </script>
 
 <style scoped>
@@ -155,6 +195,18 @@ function removeTab(i: number) {
   background: #fafafa;
   border-radius: 4px;
 }
+.list-tab-buttons {
+  display: flex;
+  flex-direction: row;
+  gap: 8px;
+}
+.tabs-footer {
+  display: flex;
+  gap: 8px;
+}
+.tabs-footer .ant-btn:first-child {
+  flex: 1;
+}
 .tab-item :deep(.ant-collapse) {
   border: none;
   background: transparent;

+ 132 - 14
src/pages/article/data/editor/editors/DetailPropsEditor.vue

@@ -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;

+ 2 - 2
src/pages/article/data/editor/subpart/NestCategoryEditor.vue

@@ -75,8 +75,8 @@
       </a-collapse>
     </div>
     <div class="nest-category-editor-footer">
-      <a-button class="large" type="dashed" block size="small" @click="add">+ 添加子分类</a-button>
-      <a-button class="small" type="dashed" block size="small" @click="paste">粘贴</a-button>
+      <a-button style="flex: 4;" class="large" type="dashed" block size="small" @click="add">+ 添加子分类</a-button>
+      <a-button style="flex: 2;" class="small" type="dashed" block size="small" @click="paste">粘贴</a-button>
     </div>
   </div>
 </template>

+ 6 - 2
src/pages/blocks/StatsBlock.vue

@@ -17,7 +17,7 @@
 <script setup lang="ts">
 import { useSimpleDataLoader } from '@/common/composeabe/SimpleDataLoader';
 import { navTo } from '@/components/utils/PageAction';
-import { ref } from 'vue';
+import { ref, watch } from 'vue';
 import { navCommonList } from '../article/common/CommonContent';
 import type { StatsTextItem } from '../parts/StatsText.vue';
 import type { IHomeCommonCategoryBlockStatsProps } from '../article/data/CommonCategoryDefine';
@@ -89,7 +89,7 @@ const statsLoader = useSimpleDataLoader(async () => {
         onClick: () => {
           switch (item.title) {
             case '世界文化遗产':
-              navTo('/pages/article/data/details', { pageConfigName: 'artifact-details', modelId: 7518 });
+              navTo('/pages/article/data/details', { pageConfigName: 'artifact-details', id: 7518 });
               break;
             case '传统村落':
               navTo('/pages/inhert/village/list');
@@ -126,4 +126,8 @@ const statsLoader = useSimpleDataLoader(async () => {
   });
 });
 
+watch(() => props.statsNameConfig, (newVal) => {
+  statsLoader.loadData(undefined, true);
+}, { deep: true });
+
 </script>