Преглед изворни кода

⚙️ 修改编辑器细节

快乐的梦鱼 пре 1 месец
родитељ
комит
09a253bea8

+ 4 - 3
src/pages/article/data/editor/components/LinkPathEditor.vue

@@ -18,7 +18,7 @@
     <a-modal
       v-model:open="modalVisible"
       title="编辑跳转路径"
-      :width="520"
+      :width="720"
       @cancel="onModalCancel"
       @ok="onModalOk"
     >
@@ -68,11 +68,12 @@
             </a-select>
             
             <!-- 自定义输入 -->
-            <a-input
+            <a-textarea
               v-else-if="pathType === 'custom'"
               v-model:value="linkPath"
-              placeholder="请输入跳转路径"
+              placeholder="请输入跳转路径, 可用表达式:dynamic:开头,请参考源码设置表达式"
               style="flex: 1"
+              :auto-size="{ minRows: 2, maxRows: 5 }"
             />
           </div>
         </a-form-item>

+ 204 - 0
src/pages/article/data/editor/editors/CommonDetailPropsEditor.vue

@@ -0,0 +1,204 @@
+<template>
+  <div class="common-detail-props-editor">
+    <a-form :labelCol="{ span: 6 }" size="small">
+      <a-form-item label="测试内容ID">
+        <a-input-number
+          :value="testDetailId"
+          @update:value="emit('update:testDetailId', $event)"
+          style="width: 100%"
+        />
+      </a-form-item>
+      <a-collapse v-model:activeKey="activeKeys" class="props-collapse">
+        <a-collapse-panel key="articleBorrow" header="查询借阅功能 (articleBorrow)">
+          <a-form :labelCol="{ span: 6 }" size="small">
+            <a-form-item label="链接 URL">
+              <a-input v-model:value="articleBorrow.url" placeholder="借阅查询链接,支持 {title} 占位" />
+            </a-form-item>
+            <a-form-item label="按钮文本">
+              <a-input v-model:value="articleBorrow.text" placeholder="如:点击查询借阅" />
+            </a-form-item>
+            <a-form-item label="按钮图标">
+              <IconEditor v-model="articleBorrow.icon" />
+            </a-form-item>
+          </a-form>
+        </a-collapse-panel>
+
+        <a-collapse-panel key="topButtons" header="顶部按钮 (topButtons)">
+          <div v-for="(item, i) in topButtonsList" :key="`btn-${i}`" class="nested-item">
+            <div class="item-actions">
+              <ArrowUpOutlined title="上移" @click.stop="moveTopButtonUp(i)" />
+              <ArrowDownOutlined title="下移" @click.stop="moveTopButtonDown(i)" />
+              <CopyOutlined title="复制" @click.stop="copyTopButton(i)" />
+              <a-popconfirm title="确定删除?" @confirm="removeTopButton(i)">
+                <a-button type="link" danger size="small">删除</a-button>
+              </a-popconfirm>
+            </div>
+            <a-form :labelCol="{ span: 6 }" size="small">
+              <a-form-item label="可见性表达式">
+                <a-input v-model:value="item.visible" placeholder="动态可见判断表达式" />
+              </a-form-item>
+              <a-form-item label="按钮文本">
+                <a-input v-model:value="item.text" />
+              </a-form-item>
+              <a-form-item label="按钮图标">
+                <IconEditor v-model="item.icon" />
+              </a-form-item>
+              <a-form-item label="动作表达式">
+                <a-textarea v-model:value="item.expression" placeholder="点击执行的动作表达式" :auto-size="{ minRows: 1, maxRows: 4 }" />
+              </a-form-item>
+            </a-form>
+          </div>
+          <div class="section-footer">
+            <a-button type="dashed" block size="small" @click="addTopButton">+ 添加顶部按钮</a-button>
+          </div>
+        </a-collapse-panel>
+
+        <a-collapse-panel key="topShowInfos" header="顶部显示信息 (topShowInfos)">
+          <div v-for="(item, i) in topShowInfosList" :key="`info-${i}`" class="nested-item">
+            <div class="item-actions">
+              <ArrowUpOutlined title="上移" @click.stop="moveTopShowInfoUp(i)" />
+              <ArrowDownOutlined title="下移" @click.stop="moveTopShowInfoDown(i)" />
+              <CopyOutlined title="复制" @click.stop="copyTopShowInfo(i)" />
+              <a-popconfirm title="确定删除?" @confirm="removeTopShowInfo(i)">
+                <a-button type="link" danger size="small">删除</a-button>
+              </a-popconfirm>
+            </div>
+            <a-form :labelCol="{ span: 6 }" size="small">
+              <a-form-item label="数据键 key">
+                <a-input v-model:value="item.key" placeholder="显示信息数据键" />
+              </a-form-item>
+              <a-form-item label="前缀文字">
+                <a-input v-model:value="item.prefix" placeholder="如:发布时间" />
+              </a-form-item>
+              <a-form-item label="取值表达式">
+                <a-input v-model:value="item.expression" placeholder="表达式" />
+              </a-form-item>
+            </a-form>
+          </div>
+          <div class="section-footer">
+            <a-button type="dashed" block size="small" @click="addTopShowInfo">+ 添加显示信息</a-button>
+          </div>
+        </a-collapse-panel>
+      </a-collapse>
+    </a-form>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { ref, computed } from 'vue';
+import type { IHomeCommonArticleDetailDefine } from '../../defines/Details';
+import IconEditor from '../components/IconEditor.vue';
+import { ArrowUpOutlined, ArrowDownOutlined, CopyOutlined } from '@ant-design/icons-vue';
+import { ArrayUtils } from '@imengyu/imengyu-utils';
+import { message } from 'ant-design-vue';
+
+const props = defineProps<{
+  props: IHomeCommonArticleDetailDefine['props'];
+  testDetailId: number;
+}>();
+const emit = defineEmits<{
+  (e: 'update:testDetailId', value: number): void;
+}>();
+
+const activeKeys = ref<string[]>(['articleBorrow', 'topButtons', 'topShowInfos']);
+
+const articleBorrow = computed({
+  get() {
+    if (!props.props.articleBorrow) {
+      (props.props as Record<string, unknown>).articleBorrow = {
+        url: '',
+        text: '',
+        icon: '',
+      };
+    }
+    return props.props.articleBorrow!;
+  },
+  set(_) {
+    // 通过子字段 v-model 已直接写入 props.props.articleBorrow
+  },
+});
+
+const topButtonsList = computed(() => props.props?.topButtons ?? []);
+const topShowInfosList = computed(() => props.props?.topShowInfos ?? []);
+
+function getTopButtons() {
+  if (!props.props.topButtons) (props.props as Record<string, unknown>).topButtons = [];
+  return props.props.topButtons!;
+}
+function addTopButton() {
+  getTopButtons().push({
+    visible: '',
+    text: '',
+    icon: '',
+    expression: '',
+  });
+}
+function removeTopButton(i: number) {
+  getTopButtons().splice(i, 1);
+}
+function moveTopButtonUp(i: number) {
+  ArrayUtils.upData(getTopButtons(), i);
+}
+function moveTopButtonDown(i: number) {
+  ArrayUtils.downData(getTopButtons(), i);
+}
+function copyTopButton(i: number) {
+  const item = getTopButtons()[i];
+  uni.setClipboardData({
+    data: JSON.stringify({ type: 'Copy:CommonDetailTopButton', data: item }),
+  });
+  message.success('复制成功');
+}
+
+function getTopShowInfos() {
+  if (!props.props.topShowInfos) (props.props as Record<string, unknown>).topShowInfos = [];
+  return props.props.topShowInfos!;
+}
+function addTopShowInfo() {
+  getTopShowInfos().push({
+    key: '',
+    prefix: '',
+    expression: '',
+  });
+}
+function removeTopShowInfo(i: number) {
+  getTopShowInfos().splice(i, 1);
+}
+function moveTopShowInfoUp(i: number) {
+  ArrayUtils.upData(getTopShowInfos(), i);
+}
+function moveTopShowInfoDown(i: number) {
+  ArrayUtils.downData(getTopShowInfos(), i);
+}
+function copyTopShowInfo(i: number) {
+  const item = getTopShowInfos()[i];
+  uni.setClipboardData({
+    data: JSON.stringify({ type: 'Copy:CommonDetailTopShowInfo', data: item }),
+  });
+  message.success('复制成功');
+}
+</script>
+
+<style scoped>
+.common-detail-props-editor {
+  font-size: 12px;
+}
+.props-collapse {
+  margin-top: 8px;
+}
+.nested-item {
+  margin-bottom: 12px;
+  padding: 8px;
+  background: #fafafa;
+  border-radius: 4px;
+}
+.item-actions {
+  display: flex;
+  flex-direction: row;
+  gap: 8px;
+  margin-bottom: 8px;
+}
+.section-footer {
+  margin-top: 8px;
+}
+</style>

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

@@ -37,7 +37,7 @@
               <a-input v-model:value="item.label" placeholder="显示标签" />
             </a-form-item>
             <a-form-item label="key">
-              <a-input v-model:value="item.key" placeholder="数据键名" />
+              <a-input v-model:value="item.key" placeholder="数据键名,可用表达式:expression:开头,请参考源码设置表达式" />
             </a-form-item>
             <a-form-item label="映射关系">
               <KeyValueEditor v-model:value="item.map" />

+ 3 - 0
src/pages/article/data/editor/subpart/EditorPreview.vue

@@ -15,6 +15,7 @@ import { provide, computed, toRef, markRaw, type Component, ref, watch } from 'v
 import { COMMON_CATEGORY_KEY } from '../../CommonCategoryGlobalLoader';
 import type { IHomeCommonCategoryDefine } from '../../CommonCategoryDefine';
 import HomePage from '@/pages/home/index.vue';
+import CommonArticleDetail from '@/pages/article/details.vue';
 import CommonCategoryList from '@/pages/article/data/CommonCategoryList.vue';
 import CommonCategoryDetail from '@/pages/article/data/CommonCategoryDetail.vue';
 import { ObjectUtils, waitTimeOut } from '@imengyu/imengyu-utils';
@@ -47,6 +48,8 @@ const previewComponent = computed<Component | null>(() => {
       return markRaw(CommonCategoryList);
     case 'Details':
       return markRaw(CommonCategoryDetail);
+    case 'CommonDetails':
+      return markRaw(CommonArticleDetail);
     default:
       console.error('未知页面类型:', type);
       return null;

+ 8 - 0
src/pages/article/data/editor/subpart/PropsEditorTree.vue

@@ -24,6 +24,12 @@
         :test-detail-id="testDetailId"
         @update:testDetailId="emit('update:testDetailId', $event)"
       />
+      <CommonDetailPropsEditor
+        v-else-if="contentType === 'CommonDetails'"
+        :props="(props.page?.content?.props as IHomeCommonArticleDetailDefine['props'])"
+        :test-detail-id="testDetailId"
+        @update:testDetailId="emit('update:testDetailId', $event)"
+      />
       <div v-else class="unknown-type">未知模板类型: {{ contentType }}</div>
     </div>
   </div>
@@ -34,12 +40,14 @@ import { computed } from 'vue';
 import type {
   IHomeCommonCategoryDefine,
   IHomeCommonCategoryDetailDefine,
+  IHomeCommonArticleDetailDefine,
   IHomeCommonCategoryHomeDefine,
   IHomeCommonCategoryListDefine,
 } from '../../CommonCategoryDefine';
 import HomePropsEditor from '../editors/HomePropsEditor.vue';
 import CommonListPropsEditor from '../editors/CommonListPropsEditor.vue';
 import DetailPropsEditor from '../editors/DetailPropsEditor.vue';
+import CommonDetailPropsEditor from '../editors/CommonDetailPropsEditor.vue';
 
 const props = defineProps<{
   page: IHomeCommonCategoryDefine['page'][0];

+ 1 - 1
src/pages/article/details.vue

@@ -398,7 +398,7 @@ function goDetails(id: number) {
 }
 function goBorrow(title: string) {
   navTo('/pages/article/web/ewebview', { 
-    url: FormatUtils.formatString(currentCommonCategoryContentDefine.value?.props.articleBorrow?.url || '', { title })
+    url: FormatUtils.formatString(currentCommonCategoryContentDefine.value?.props.articleBorrow?.url || '', title)
   });
 }
 function getPageShareData() {