Parcourir la source

Merge remote-tracking branch 'origin/dev' into dev

jason il y a 1 an
Parent
commit
fcc748dc63

+ 10 - 0
src/api/mall/promotion/diy/page.ts

@@ -33,3 +33,13 @@ export const updateDiyPage = async (data: DiyPageVO) => {
 export const deleteDiyPage = async (id: number) => {
   return await request.delete({ url: `/promotion/diy-page/delete?id=` + id })
 }
+
+// 获得装修页面属性
+export const getDiyPageProperty = async (id: number) => {
+  return await request.get({ url: `/promotion/diy-page/get-property?id=` + id })
+}
+
+// 更新装修页面属性
+export const updateDiyPageProperty = async (data: DiyPageVO) => {
+  return await request.put({ url: `/promotion/diy-page/update-property`, data })
+}

+ 17 - 0
src/api/mall/promotion/diy/template.ts

@@ -1,4 +1,5 @@
 import request from '@/config/axios'
+import { DiyPageVO } from '@/api/mall/promotion/diy/page'
 
 export interface DiyTemplateVO {
   id?: number
@@ -10,6 +11,10 @@ export interface DiyTemplateVO {
   property: string
 }
 
+export interface DiyTemplatePropertyVO extends DiyTemplateVO {
+  pages: DiyPageVO[]
+}
+
 // 查询装修模板列表
 export const getDiyTemplatePage = async (params: any) => {
   return await request.get({ url: `/promotion/diy-template/page`, params })
@@ -39,3 +44,15 @@ export const deleteDiyTemplate = async (id: number) => {
 export const useDiyTemplate = async (id: number) => {
   return await request.put({ url: `/promotion/diy-template/use?id=` + id })
 }
+
+// 获得装修模板属性
+export const getDiyTemplateProperty = async (id: number) => {
+  return await request.get<DiyTemplatePropertyVO>({
+    url: `/promotion/diy-template/get-property?id=` + id
+  })
+}
+
+// 更新装修模板属性
+export const updateDiyTemplateProperty = async (data: DiyTemplateVO) => {
+  return await request.put({ url: `/promotion/diy-template/update-property`, data })
+}

+ 0 - 1
src/components/DiyEditor/components/mobile/NavigationBar/index.vue

@@ -40,7 +40,6 @@ defineProps<{ property: NavigationBarProperty }>()
   display: flex;
   justify-content: space-between;
   align-items: center;
-  cursor: pointer;
   /* 左边 */
   .left {
     margin-left: 8px;

+ 1 - 1
src/components/DiyEditor/components/mobile/NoticeBar/index.vue

@@ -1,6 +1,6 @@
 <template>
   <div
-    class="flex items-center text-12px"
+    class="flex items-center p-y-4px text-12px"
     :style="{ backgroundColor: property.backgroundColor, color: property.textColor }"
   >
     <el-image :src="property.iconUrl" class="h-18px" />

+ 214 - 158
src/components/DiyEditor/index.vue

@@ -45,6 +45,7 @@
               @click="handleNavigationBarSelected"
               :class="[
                 'component',
+                'cursor-pointer!',
                 { active: selectedComponent?.id === navigationBarComponent.id }
               ]"
             />
@@ -59,7 +60,7 @@
               }"
             >
               <draggable
-                class="drag-area page-prop-area"
+                class="page-prop-area drag-area"
                 v-model="pageComponents"
                 item-key="index"
                 :animation="200"
@@ -70,7 +71,7 @@
                 @change="handleComponentChange"
               >
                 <template #item="{ element, index }">
-                  <div class="component-box" @click="handleComponentSelected(element, index)">
+                  <div class="component-container" @click="handleComponentSelected(element, index)">
                     <!-- 左侧组件名 -->
                     <div
                       :class="['component-name', { active: selectedComponentIndex === index }]"
@@ -79,12 +80,13 @@
                       {{ element.name }}
                     </div>
                     <!-- 组件内容区 -->
-                    <component
-                      :is="element.id"
-                      :property="element.property"
-                      :class="['component', { active: selectedComponentIndex === index }]"
-                      :data-type="element.id"
-                    />
+                    <div :class="['component', { active: selectedComponentIndex === index }]">
+                      <component
+                        :is="element.id"
+                        :property="element.property"
+                        :data-type="element.id"
+                      />
+                    </div>
                     <!-- 左侧:组件操作工具栏 -->
                     <div
                       class="component-toolbar"
@@ -130,6 +132,7 @@
             :class="[
               'editor-design-bottom',
               'component',
+              'cursor-pointer!',
               { active: selectedComponent?.id === tabBarComponent.id }
             ]"
           >
@@ -200,18 +203,25 @@ const navigationBarComponent = ref<DiyComponent<any>>(cloneDeep(NAVIGATION_BAR_C
 const tabBarComponent = ref<DiyComponent<any>>(cloneDeep(TAB_BAR_COMPONENT))
 
 // 选中的组件,默认选中顶部导航栏
-const selectedComponent = ref<DiyComponent<any>>(unref(pageConfigComponent))
+const selectedComponent = ref<DiyComponent<any>>()
 // 选中的组件索引
 const selectedComponentIndex = ref<number>(-1)
 // 组件列表
 const pageComponents = ref<DiyComponent<any>[]>([])
 // 定义属性
 const props = defineProps<{
+  // 页面配置,支持Json字符串
   modelValue: string | PageConfig
+  // 标题
   title: string
-  libs: DiyComponentLibrary[] // 组件库
+  // 组件库
+  libs: DiyComponentLibrary[]
+  // 是否显示顶部导航栏
   showNavigationBar: boolean
+  // 是否显示底部导航菜单
   showTabBar: boolean
+  // 是否显示页面配置
+  showPageConfig: boolean
 }>()
 
 // 监听传入的页面配置
@@ -255,6 +265,8 @@ const handleSave = () => {
 
 // 处理页面选中:显示属性表单
 const handlePageSelected = (event: any) => {
+  if (!props.showPageConfig) return
+
   // 配置了样式 page-prop-area 的元素,才显示页面设置
   if (includes(event?.target?.classList, 'page-prop-area')) {
     handleComponentSelected(unref(pageConfigComponent))
@@ -351,183 +363,227 @@ const handlePreview = () => {
   message.warning('开发中~')
   emits('preview')
 }
+
+// 设置默认选中的组件
+const setDefaultSelectedComponent = () => {
+  if (props.showPageConfig) {
+    selectedComponent.value = unref(pageConfigComponent)
+  } else if (props.showNavigationBar) {
+    selectedComponent.value = unref(navigationBarComponent)
+  } else if (props.showTabBar) {
+    selectedComponent.value = unref(tabBarComponent)
+  }
+}
+watch(
+  () => [props.showPageConfig, props.showNavigationBar, props.showTabBar],
+  () => setDefaultSelectedComponent()
+)
+onMounted(() => setDefaultSelectedComponent())
 </script>
 <style lang="scss" scoped>
+/* 手机宽度 */
+$phone-width: 375px;
+/* 根节点样式 */
 .editor {
   height: 100%;
   margin: calc(0px - var(--app-content-padding));
   display: flex;
   flex-direction: column;
-}
-.editor-header {
-  display: flex;
-  align-items: center;
-  justify-content: space-between;
-  height: auto;
-  padding: 0;
-  border-bottom: solid 1px var(--el-border-color);
-  background-color: var(--el-bg-color);
-
-  .header-right {
-    height: 100%;
-    .el-button {
+  /* 顶部:工具栏 */
+  .editor-header {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    height: auto;
+    padding: 0;
+    border-bottom: solid 1px var(--el-border-color);
+    background-color: var(--el-bg-color);
+    /* 工具栏:右侧按钮 */
+    .header-right {
       height: 100%;
+      .el-button {
+        height: 100%;
+      }
     }
-  }
-
-  :deep(.el-radio-button__inner),
-  :deep(.el-button) {
-    border-top: none !important;
-    border-bottom: none !important;
-    border-radius: 0 !important;
-  }
-}
-.editor-container {
-  height: calc(
-    100vh - var(--top-tool-height) - var(--tags-view-height) - var(--app-footer-height) - 42px
-  );
-  /* 右侧属性面板 */
-  .editor-right {
-    flex-shrink: 0;
-    box-shadow: -8px 0 8px -8px rgba(0, 0, 0, 0.12);
-
-    :deep(.el-card__header) {
-      padding: 8px 16px;
+    /* 隐藏工具栏按钮的边框 */
+    :deep(.el-radio-button__inner),
+    :deep(.el-button) {
+      border-top: none !important;
+      border-bottom: none !important;
+      border-radius: 0 !important;
     }
-
-    .property-group {
-      /* 属性分组 */
+  }
+  /* 中心操作区 */
+  .editor-container {
+    height: calc(
+      100vh - var(--top-tool-height) - var(--tags-view-height) - var(--app-footer-height) - 42px
+    );
+    /* 右侧属性面板 */
+    .editor-right {
+      flex-shrink: 0;
+      box-shadow: -8px 0 8px -8px rgba(0, 0, 0, 0.12);
+      /* 属性面板顶部:减少内边距 */
       :deep(.el-card__header) {
-        border: none;
-        background: var(--el-bg-color-page);
+        padding: 8px 16px;
+      }
+      /* 属性面板分组 */
+      .property-group {
+        /* 属性分组 */
+        :deep(.el-card__header) {
+          border: none;
+          background: var(--el-bg-color-page);
+        }
       }
     }
-  }
-
-  /* 中心 */
-  .editor-center {
-    flex: 1 1 0;
-    padding: 16px 0;
-    background-color: var(--app-content-bg-color);
-    display: flex;
-    justify-content: center;
 
-    .editor-design {
-      position: relative;
-      height: 100%;
-      width: 100%;
+    /* 中心区域 */
+    .editor-center {
+      flex: 1 1 0;
+      padding: 16px 0;
+      background-color: var(--app-content-bg-color);
       display: flex;
-      flex-direction: column;
-      align-items: center;
-      overflow: hidden;
-
-      /* 组件 */
-      .component {
-        border: 1px solid #fff;
-        width: 375px !important;
+      justify-content: center;
+      /* 中心设计区域 */
+      .editor-design {
+        position: relative;
+        height: 100%;
+        width: 100%;
+        display: flex;
+        flex-direction: column;
+        align-items: center;
+        overflow: hidden;
 
-        &:hover {
-          border: 1px dashed #155bd4;
+        /* 组件 */
+        .component {
+          border: 1px solid #fff;
+          width: $phone-width;
+          cursor: move;
+          /* 鼠标放到组件上时 */
+          &:hover {
+            border: 1px dashed var(--el-color-primary);
+          }
         }
-      }
-      .component.active {
-        border: 2px solid #155bd4 !important;
-      }
-
-      .editor-design-top {
-        width: 379px;
-
-        .status-bar {
-          height: 20px;
-          width: 100%;
-          background-color: #fff;
+        /* 组件选中 */
+        .component.active {
+          border: 2px solid var(--el-color-primary);
         }
-
-        .navigation-bar {
-          width: 100%;
+        /* 手机顶部 */
+        .editor-design-top {
+          width: $phone-width;
+          /* 手机顶部状态栏 */
+          .status-bar {
+            height: 20px;
+            width: $phone-width;
+            background-color: #fff;
+          }
         }
-      }
-
-      .editor-design-bottom {
-        width: 379px;
-      }
-
-      .editor-design-center {
-        width: 100%;
-        flex: 1 1 0;
-
-        :deep(.el-scrollbar__view) {
-          height: 100%;
+        /* 手机底部导航 */
+        .editor-design-bottom {
+          width: $phone-width;
         }
+        /* 手机页面编辑区域 */
+        .editor-design-center {
+          width: 100%;
+          flex: 1 1 0;
 
-        /* 主体内容 */
-        .phone-container {
-          height: 100%;
-          box-sizing: border-box;
-          cursor: move;
-          position: relative;
-          background-repeat: no-repeat;
-          background-size: 100% 100%;
-          width: 379px;
-          margin: 0 auto;
-
-          .drag-area {
+          :deep(.el-scrollbar__view) {
             height: 100%;
           }
 
-          /* 组件容器 */
-          .component-box {
-            width: 100%;
+          /* 主体内容 */
+          .phone-container {
+            height: 100%;
+            box-sizing: border-box;
             position: relative;
-            /* 组件名称 */
-            .component-name {
-              position: absolute;
-              width: 80px;
-              text-align: center;
-              line-height: 25px;
-              height: 25px;
-              background: #fff;
-              font-size: 12px;
-              left: -80px;
-              top: 0;
-              box-shadow:
-                0 0 4px #00000014,
-                0 2px 6px #0000000f,
-                0 4px 8px 2px #0000000a;
-            }
-            .component-name.active {
-              background: #2d8cf0;
-              color: #fff;
+            background-repeat: no-repeat;
+            background-size: 100% 100%;
+            width: $phone-width;
+            margin: 0 auto;
+            .drag-area {
+              height: 100%;
             }
-            /* 组件操作按钮 */
-            .component-toolbar {
-              position: absolute;
-              top: 0;
-              right: -50px;
 
-              .el-button-group {
-                display: inline-flex;
-                flex-direction: column;
+            /* 组件容器(左侧:组件名称,中间:组件,右侧:操作工具栏) */
+            .component-container {
+              width: 100%;
+              position: relative;
+              /* 左侧:组件名称 */
+              .component-name {
+                position: absolute;
+                width: 80px;
+                text-align: center;
+                line-height: 25px;
+                height: 25px;
+                background: #fff;
+                font-size: 12px;
+                left: -85px;
+                top: 0;
+                box-shadow:
+                  0 0 4px #00000014,
+                  0 2px 6px #0000000f,
+                  0 4px 8px 2px #0000000a;
+                /* 右侧小三角 */
+                &:after {
+                  position: absolute;
+                  top: 7.5px;
+                  right: -10px;
+                  content: ' ';
+                  height: 0;
+                  width: 0;
+                  border: 5px solid transparent;
+                  border-left-color: #fff;
+                }
               }
-              .el-button-group > .el-button:first-child {
-                border-bottom-left-radius: 0;
-                border-bottom-right-radius: 0;
-                border-top-right-radius: var(--el-border-radius-base);
-                border-bottom-color: var(--el-button-divide-border-color);
+              /* 组件选中按钮 */
+              .component-name.active {
+                background: var(--el-color-primary);
+                color: #fff;
+                &:after {
+                  border-left-color: var(--el-color-primary);
+                }
               }
-              .el-button-group > .el-button:last-child {
-                border-top-left-radius: 0;
-                border-top-right-radius: 0;
-                border-bottom-left-radius: var(--el-border-radius-base);
-                border-top-color: var(--el-button-divide-border-color);
-              }
-              .el-button-group .el-button--primary:not(:first-child):not(:last-child) {
-                border-top-color: var(--el-button-divide-border-color);
-                border-bottom-color: var(--el-button-divide-border-color);
-              }
-              .el-button-group > .el-button:not(:last-child) {
-                margin-bottom: -1px;
-                margin-right: 0;
+              /* 右侧:组件操作工具栏 */
+              .component-toolbar {
+                position: absolute;
+                top: 0;
+                right: -57px;
+                /* 左侧小三角 */
+                &:before {
+                  position: absolute;
+                  top: 10px;
+                  left: -10px;
+                  content: ' ';
+                  height: 0;
+                  width: 0;
+                  border: 5px solid transparent;
+                  border-right-color: #2d8cf0;
+                }
+
+                /* 重写 Element 按钮组的样式(官方只支持水平显示,增加垂直显示的样式) */
+                .el-button-group {
+                  display: inline-flex;
+                  flex-direction: column;
+                }
+                .el-button-group > .el-button:first-child {
+                  border-bottom-left-radius: 0;
+                  border-bottom-right-radius: 0;
+                  border-top-right-radius: var(--el-border-radius-base);
+                  border-bottom-color: var(--el-button-divide-border-color);
+                }
+                .el-button-group > .el-button:last-child {
+                  border-top-left-radius: 0;
+                  border-top-right-radius: 0;
+                  border-bottom-left-radius: var(--el-border-radius-base);
+                  border-top-color: var(--el-button-divide-border-color);
+                }
+                .el-button-group .el-button--primary:not(:first-child):not(:last-child) {
+                  border-top-color: var(--el-button-divide-border-color);
+                  border-bottom-color: var(--el-button-divide-border-color);
+                }
+                .el-button-group > .el-button:not(:last-child) {
+                  margin-bottom: -1px;
+                  margin-right: 0;
+                }
               }
             }
           }

+ 3 - 2
src/views/mall/promotion/diy/page/decorate.vue

@@ -4,6 +4,7 @@
     v-model="formData.property"
     :title="formData.name"
     :libs="componentLibs"
+    :show-page-config="true"
     :show-navigation-bar="true"
     :show-tab-bar="false"
     @save="submitForm"
@@ -51,7 +52,7 @@ const formRef = ref() // 表单 Ref
 const getPageDetail = async (id: any) => {
   formLoading.value = true
   try {
-    formData.value = await DiyPageApi.getDiyPage(id)
+    formData.value = await DiyPageApi.getDiyPageProperty(id)
   } finally {
     formLoading.value = false
   }
@@ -63,7 +64,7 @@ const submitForm = async () => {
   // 提交请求
   formLoading.value = true
   try {
-    await DiyPageApi.updateDiyPage(unref(formData)!)
+    await DiyPageApi.updateDiyPageProperty(unref(formData)!)
     message.success('保存成功')
   } finally {
     formLoading.value = false

+ 30 - 9
src/views/mall/promotion/diy/template/decorate.vue

@@ -1,11 +1,12 @@
 <template>
   <DiyEditor
     v-if="formData && !formLoading"
-    v-model="formData.property"
+    v-model="currentFormData!.property"
     :title="templateItems[selectedTemplateItem].name"
     :libs="libs"
+    :show-page-config="selectedTemplateItem !== 0"
     :show-tab-bar="selectedTemplateItem === 0"
-    :show-navigation-bar="selectedTemplateItem > 0"
+    :show-navigation-bar="selectedTemplateItem !== 0"
     @save="submitForm"
   >
     <template #toolBarLeft>
@@ -25,6 +26,7 @@
 </template>
 <script setup lang="ts">
 import * as DiyTemplateApi from '@/api/mall/promotion/diy/template'
+import * as DiyPageApi from '@/api/mall/promotion/diy/page'
 import { useTagsViewStore } from '@/store/modules/tagsView'
 import { DiyComponentLibrary } from '@/components/DiyEditor/util'
 
@@ -42,14 +44,17 @@ const templateItems = reactive([
 const message = useMessage() // 消息弹窗
 
 const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
-const formData = ref<DiyTemplateApi.DiyTemplateVO>()
+const formData = ref<DiyTemplateApi.DiyTemplatePropertyVO>()
 const formRef = ref() // 表单 Ref
+// 当前编辑的属性
+const currentFormData = ref<DiyTemplateApi.DiyTemplatePropertyVO | DiyPageApi.DiyPageVO>()
 
 // 获取详情
 const getPageDetail = async (id: any) => {
   formLoading.value = true
   try {
-    formData.value = await DiyTemplateApi.getDiyTemplate(id)
+    formData.value = await DiyTemplateApi.getDiyTemplateProperty(id)
+    currentFormData.value = formData.value
   } finally {
     formLoading.value = false
   }
@@ -82,12 +87,21 @@ const pageLibs = [
 ] as DiyComponentLibrary[]
 // 当前组件库
 const libs = ref<DiyComponentLibrary[]>(templateLibs)
+// 模板选项切换
 const handleTemplateItemChange = () => {
+  // 编辑模板
   if (selectedTemplateItem.value === 0) {
     libs.value = templateLibs
-  } else {
-    libs.value = pageLibs
+    currentFormData.value = formData.value
+    return
   }
+
+  // 编辑页面
+  libs.value = pageLibs
+  currentFormData.value = formData.value!.pages.find(
+    (page: DiyPageApi.DiyPageVO) => page.name === templateItems[selectedTemplateItem.value].name
+  )
+  console.log(currentFormData.value)
 }
 
 // 提交表单
@@ -97,7 +111,13 @@ const submitForm = async () => {
   // 提交请求
   formLoading.value = true
   try {
-    await DiyTemplateApi.updateDiyTemplate(unref(formData)!)
+    if (selectedTemplateItem.value === 0) {
+      // 提交模板属性
+      await DiyTemplateApi.updateDiyTemplateProperty(unref(formData)!)
+    } else {
+      // 提交页面属性
+      await DiyPageApi.updateDiyPageProperty(unref(currentFormData)!)
+    }
     message.success('保存成功')
   } finally {
     formLoading.value = false
@@ -113,8 +133,9 @@ const resetForm = () => {
     usedTime: undefined,
     remark: '',
     previewImageUrls: [],
-    property: ''
-  } as DiyTemplateApi.DiyTemplateVO
+    property: '',
+    pages: []
+  } as DiyTemplateApi.DiyTemplatePropertyVO
   formRef.value?.resetFields()
 }