Przeglądaj źródła

feat(photography): 重构摄影详情页界面布局与组件结构

- 将原有的服务内容卡片替换为横向服务标签页设计
- 新增未开发功能的占位标签(详情页模板自定义、白底图导出、产品图册)
- 调整主图LOGO与详情模板选择区域的左右布局结构
- 优化详情资料准备区域,支持Excel上传和系统对接两种方式
- 修改一键上架平台选择器样式并调整国内外平台分类标签
- 更新底部"开始生成"按钮样式并固定在页面底部
- 引入新的蓝色头部组件BlueHeaderBar替代原有headerBar
- 样式全面升级,增强视觉层次和用户体验一致性
- 修复部分元素间距及交互逻辑问题
panqiuyao 1 tydzień temu
rodzic
commit
f4a838391a

BIN
frontend/src/assets/images/detail/logo.png


+ 160 - 0
frontend/src/components/header-bar/blue-header.vue

@@ -0,0 +1,160 @@
+<template>
+  <div class="blue-header-bar">
+    <div class="blue-header-bar__left">
+      <img src="@/assets/images/detail/logo.png" class="blue-header-bar__logo" alt="logo" />
+      <span class="blue-header-bar__title">智惠映AI自动拍照机 <span class="blue-header-bar__version">{{ currentVersion }}</span></span>
+    </div>
+    <div class="blue-header-bar__right">
+      <div class="blue-header-bar__user">
+        <span class="blue-header-bar__user-label">昵称:</span>
+        <span class="blue-header-bar__user-name">
+          {{ useUserInfoStore.userInfo.nick_name
+            || useUserInfoStore.userInfo.account_name
+            || useUserInfoStore.userInfo.real_name
+            || useUserInfoStore.userInfo.login_name
+            || '未登录' }}
+        </span>
+      </div>
+    </div>
+  </div>
+  <div class="blue-header-bar_blank"></div>
+</template>
+
+<script setup lang="ts">
+import {defineProps, reactive, onMounted, onUnmounted, ref} from 'vue'
+import useUserInfo from '@/stores/modules/user'
+import tokenInfo from '@/stores/modules/token';
+import packageJson from '@/../../package.json'
+import client from '@/stores/modules/client'
+
+const useUserInfoStore = useUserInfo()
+const tokenInfoStore = tokenInfo();
+const currentVersion = ref(packageJson.version)
+const clientStore = client()
+
+onMounted(async ()=>{
+
+  if (tokenInfoStore.getToken /* 已登录 */) {
+    if(!useUserInfoStore.userInfo.id){
+      await useUserInfoStore.getInfo()
+    }
+  }
+})
+const minimizeWindow = () => {
+  // 窗口控制功能,如果需要可以后续添加
+  console.log('minimize window')
+}
+
+const maximizeWindow = () => {
+  // 窗口控制功能,如果需要可以后续添加
+  console.log('maximize window')
+}
+
+const closeWindow = () => {
+  // 窗口控制功能,如果需要可以后续添加
+  console.log('close window')
+}
+</script>
+
+<style lang="scss" scoped>
+.blue-header-bar_blank {
+  width: 100%;
+  height: 50px;
+}
+
+.blue-header-bar {
+  position: fixed;
+  top: 0;
+  left: 0;
+  right: 0;
+  height: 50px;
+  background: linear-gradient(135deg, #2957FF 0%, #1E3A8A 100%);
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  padding: 0 20px;
+  z-index: 100000;
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+  app-drag: drag;
+  -webkit-app-region: drag;
+
+  &__left {
+    display: flex;
+    align-items: center;
+    gap: 12px;
+  }
+
+  &__logo {
+    width: 36px;
+    height: 36px;
+  }
+
+  &__title {
+    color: #fff;
+    font-size: 16px;
+    font-weight: 600;
+  }
+  &__version {
+    background: rgba(255,255,255,0.2);
+    border-radius: 12px;
+    font-size: 12px;
+    padding: 3px 10px;
+    font-weight: normal;
+    margin-left: 5px;
+  }
+
+  &__right {
+    display: flex;
+    align-items: center;
+    gap: 20px;
+    app-drag: no-drag;
+    -webkit-app-region: no-drag;
+  }
+
+  &__user {
+    display: flex;
+    align-items: center;
+    color: #fff;
+    font-size: 14px;
+
+    &-label {
+      opacity: 0.8;
+      margin-right: 4px;
+    }
+
+    &-name {
+      font-weight: 500;
+    }
+  }
+
+  &__controls {
+    display: flex;
+    gap: 2px;
+  }
+
+  .control-btn {
+    width: 30px;
+    height: 30px;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    cursor: pointer;
+    color: #fff;
+    font-size: 18px;
+    transition: background-color 0.2s;
+
+    &:hover {
+      background-color: rgba(255, 255, 255, 0.2);
+    }
+
+    &--close:hover {
+      background-color: rgba(255, 0, 0, 0.3);
+    }
+
+    span {
+      line-height: 1;
+    }
+  }
+}
+</style>
+

+ 3 - 0
frontend/src/components/header-bar/index.vue

@@ -81,6 +81,9 @@
       <div class="header-bar__button header-bar__button__user" v-if="showUser">
         <el-dropdown>
           <span class="el-dropdown-link">
+            {{useUserInfoStore.userInfo.account_name}}--
+            {{useUserInfoStore.userInfo.real_name}}--
+            {{useUserInfoStore.userInfo.login_name}}--
             {{ useUserInfoStore.userInfo.account_name || useUserInfoStore.userInfo.real_name || useUserInfoStore.userInfo.login_name }}
           </span>
           <template #dropdown>

+ 379 - 144
frontend/src/views/Photography/detail.vue

@@ -1,57 +1,90 @@
 <template>
+  <BlueHeaderBar />
+
+  <!-- 服务标签页 -->
+  <div class="service-tabs">
+    <div
+      class="service-tab"
+      :class="{
+        'active': form.services.includes('is_product_scene'),
+        'disabled': false
+      }"
+      @click="toggleService('is_product_scene')"
+      v-log="{ describe: { action: '点击服务标签', service: '场景图生成' } }"
+    >
+      <el-checkbox
+        :model-value="form.services.includes('is_product_scene')"
+        @change="toggleService('is_product_scene')"
+        @click.stop
+        class="tab-checkbox"
+      />
+      <img src="@/assets/images/Photography/cj.png" alt="场景图生成" class="tab-icon" />
+      <span class="tab-name">场景图生成</span>
+      <div class="tab-edit-btn" @click.stop="openScenePromptDialog" v-if="form.services.includes('is_product_scene')" v-log="{ describe: { action: '点击编辑场景图', service: '场景图生成-弹窗' } }">
+        <el-icon><EditPen /></el-icon>
+      </div>
+    </div>
+
+    <div
+      class="service-tab"
+      :class="{
+        'active': form.services.includes('is_upper_footer'),
+        'disabled': false
+      }"
+      @click="toggleService('is_upper_footer')"
+      v-log="{ describe: { action: '点击服务标签', service: '模特图生成' } }"
+    >
+      <el-checkbox
+        :model-value="form.services.includes('is_upper_footer')"
+        @change="toggleService('is_upper_footer')"
+        @click.stop
+        class="tab-checkbox"
+      />
+      <img src="@/assets/images/Photography/mt.png" alt="模特图生成" class="tab-icon" />
+      <span class="tab-name">模特图生成</span>
+      <div class="tab-edit-btn" @click.stop="openModelDialog" v-if="form.services.includes('is_upper_footer')" v-log="{ describe: { action: '点击编辑模特图', service: '模特图生成-弹窗' } }">
+        <el-icon><EditPen /></el-icon>
+      </div>
+    </div>
 
-  <headerBar title="主图与详情生成" />
+    <div
+      class="service-tab"
+      :class="{
+        'active': form.services.includes('is_detail'),
+        'disabled': false
+      }"
+      @click="toggleService('is_detail')"
+      v-log="{ describe: { action: '点击服务标签', service: '详情页生成' } }"
+    >
+      <el-checkbox
+        :model-value="form.services.includes('is_detail')"
+        @change="toggleService('is_detail')"
+        @click.stop
+        class="tab-checkbox"
+      />
+      <img src="@/assets/images/Photography/xq.png" alt="详情页生成" class="tab-icon" />
+      <span class="tab-name">详情页生成</span>
+    </div>
+
+    <!-- 未开发的功能 -->
+    <div class="service-tab disabled" title="功能开发中">
+      <img src="@/assets/images/Photography/zhuangshi.png" alt="详情页模板自定义" class="tab-icon" />
+      <span class="tab-name">详情页模板自定义</span>
+    </div>
+
+    <div class="service-tab disabled" title="功能开发中">
+      <img src="@/assets/images/Photography/zhuangshi.png" alt="白底图批量导出" class="tab-icon" />
+      <span class="tab-name">白底图批量导出</span>
+    </div>
+
+    <div class="service-tab disabled" title="功能开发中">
+      <img src="@/assets/images/Photography/zhuangshi.png" alt="产品图册生成" class="tab-icon" />
+      <span class="tab-name">产品图册生成</span>
+    </div>
+  </div>
 
   <div class="detail-container">
-    <div>
-      <!-- 顶部:勾选服务内容(多选) -->
-      <div class="service-section flex between top">
-        <div class="section-title">
-          <img src="@/assets/images/Photography/zhuangshi.png" style="width: 32px; height: 32px;" />
-          勾选服务内容(多选):
-        </div>
-        <div class="service-cards">
-          <div class="service-card" :class="{ active: form.services.includes('is_product_scene') }" @click="toggleService('is_product_scene')" v-log="{ describe: { action: '点击服务卡片', service: '场景图生成' } }">
-            <div class="service-checkbox" @click.stop>
-                              <el-checkbox size="large" :model-value="form.services.includes('is_product_scene')" @change="toggleService('is_product_scene')" />
-            </div>
-            <div class="service-content">
-              <div class="service-image">
-                <img src="@/assets/images/Photography/cj.png" alt="场景图生成" />
-                <div class="service-icon" @click.stop="openScenePromptDialog" v-log="{ describe: { action: '点击服务卡片图标', service: '场景图生成-弹窗' } }">
-                  <el-icon><EditPen /></el-icon>
-                </div>
-              </div>
-              <div class="service-name">场景图生成</div>
-            </div>
-          </div>
-          <div class="service-card" :class="{ active: form.services.includes('is_upper_footer') }" @click="toggleService('is_upper_footer')" v-log="{ describe: { action: '点击服务卡片', service: '模特图生成' } }">
-            <div class="service-checkbox" @click.stop>
-                              <el-checkbox size="large" :model-value="form.services.includes('is_upper_footer')" @change="toggleService('is_upper_footer')" />
-            </div>
-            <div class="service-content">
-              <div class="service-image">
-                <img src="@/assets/images/Photography/mt.png" alt="模特图生成" />
-                <div class="service-icon" @click.stop="openModelDialog" v-log="{ describe: { action: '点击服务卡片图标', service: '模特图生成-弹窗' } }">
-                  <el-icon><EditPen /></el-icon>
-                </div>
-              </div>
-              <div class="service-name">模特图生成</div>
-            </div>
-          </div>
-          <div class="service-card" :class="{ active: form.services.includes('is_detail') }" @click="toggleService('is_detail')" v-log="{ describe: { action: '点击服务卡片', service: '详情页生成' } }">
-            <div class="service-checkbox" @click.stop>
-                              <el-checkbox size="large" :model-value="form.services.includes('is_detail')" @change="toggleService('is_detail')" />
-            </div>
-            <div class="service-content">
-              <div class="service-image">
-                <img src="@/assets/images/Photography/xq.png" alt="详情页生成" />
-              </div>
-              <div class="service-name">详情页生成</div>
-            </div>
-          </div>
-        </div>
-      </div>
+    <div class="detail-content">
 
       <!-- 主图LOGO部分 -->
 <!--
@@ -120,50 +153,38 @@
       </div>
       <el-divider />-->
 
-      <!-- 主图LOGO 与 选择详情模板:左右布局 -->
-      <div class="logo-template-row">
-        <!-- 左:主图LOGO -->
-        <div class="logo-col">
-          <div class="logo-section flex left top" >
-            <div class="section-title" style="margin-bottom: 0px;">
-              <img src="@/assets/images/Photography/zhuangshi.png" style="width: 32px; height: 32px;" />
-              主图LOGO:
-            </div>
-          </div>
-          <div class="logo-section flex left top multi-line">
-            <upload v-for="item,index in logoList"   :value="item" :key="item"
-                    v-show="item"
-                    @input="onRemove(index)"
-                    class="mar-right-10 upload-item"
-                    :class="{
-                        active: item === form.logo_path
-                    }"
-                    @click.native="selectLogo(item)"
-            ></upload>
-            <upload @input="onInput"></upload>
-          </div>
-        </div>
-
-        <!-- 右:选择详情模板 -->
-        <div class="template-col">
+      <!-- 左右布局 -->
+      <div class="main-layout">
+        <!-- 左侧:选择详情模板 -->
+        <div class="left-panel">
           <div :class="['template-section', { 'template-section--disabled': !isDetailServiceSelected }]">
-            <div class="flex between">
+            <div class="section-header">
               <div class="section-title">
                 <img src="@/assets/images/Photography/zhuangshi.png" style="width: 32px; height: 32px;" />
                 选择详情模版
               </div>
               <div class="template-pagination">
-                <el-pagination background layout="prev, pager, next" v-model:current-page="queryParams.current"
-                  v-model:page-size.sync="queryParams.size" :total="totalPage" @current-change="onCurrentChange"
-                  @size-change="onSizeChange" />
+                <el-pagination
+                  background
+                  layout="prev, pager, next"
+                  v-model:current-page="queryParams.current"
+                  v-model:page-size.sync="queryParams.size"
+                  :total="totalPage"
+                  @current-change="onCurrentChange"
+                  @size-change="onSizeChange"
+                />
               </div>
             </div>
 
             <div class="template-list">
-              <div v-for="(template, index) in visibleTemplates" :key="index" class="template-item"
-                @click="handleTemplateItemClick(template)" v-log="{ describe: { action: '点击选择详情模板', template_name: template.template_name } }">
-                <el-image :src="template.template_cover_image" fit="contain" class="cur-p"
-                  style="width: 100%; display: block;" />
+              <div
+                v-for="(template, index) in visibleTemplates"
+                :key="index"
+                class="template-item"
+                @click="handleTemplateItemClick(template)"
+                v-log="{ describe: { action: '点击选择详情模板', template_name: template.template_name } }"
+              >
+                <el-image :src="template.template_cover_image" fit="contain" class="cur-p" style="width: 100%; display: block;" />
                 <div class="select-warp" :class="form.selectTemplate?.id == template.id ? 'active' : ''">
                   <el-icon color="#FFFFFF">
                     <Select />
@@ -171,34 +192,93 @@
                 </div>
                 <div class="template-info">
                   <span class="mar-left-10 chaochu_1">{{ template.template_name }}</span>
-                  <div class="template-view" @click="viewTemplate(template)" v-log="{ describe: { action: '点击查看模板详情', template_name: template.template_name } }">查看</div>
+                  <div class="template-view" @click.stop="viewTemplate(template)" v-log="{ describe: { action: '点击查看模板详情', template_name: template.template_name } }">查看</div>
                 </div>
               </div>
             </div>
 
             <div class="template-tips c-333 fs-14 line-20 te-l mar-top-20 flex left">
               <el-icon><WarningFilled /></el-icon>
-              <span class="mar-left-10">该模版要求拍摄图片:{{form.selectTemplate?.template_image_order}}
-                <template v-if="form.selectTemplate?.template_image_order">,共{{form.selectTemplate?.template_image_order.split(',').length}}张。</template>
-              </span>
+              <span class="mar-left-10">该模版需提供{{form.selectTemplate?.template_image_order?.split(',').length || 5}}张标准视角的商品图:{{form.selectTemplate?.template_image_order || '俯视、侧视、后跟、鞋底、内里'}}。请确保图片清晰度高,背景干净。</span>
+            </div>
+          </div>
+        </div>
+
+        <!-- 右侧:LOGO、详情资料准备、一键上架 -->
+        <div class="right-panel">
+          <!-- 主图LOGO -->
+          <div class="right-section">
+            <div class="section-title">
+              <img src="@/assets/images/Photography/zhuangshi.png" style="width: 32px; height: 32px;" />
+              主图LOGO
+            </div>
+            <div class="logo-section flex left top multi-line">
+              <upload
+                v-for="item,index in logoList"
+                :value="item"
+                :key="item"
+                v-show="item"
+                @input="onRemove(index)"
+                class="mar-right-10 upload-item"
+                :class="{ active: item === form.logo_path }"
+                @click.native="selectLogo(item)"
+              ></upload>
+              <upload @input="onInput"></upload>
             </div>
+          </div>
 
-            <!-- 模板下:一键上架 和 电商平台(多选) -->
-            <div
-              class="publish-section flex left"
-              :class="{ 'publish-section--disabled': !canUsePublishSection }"
-              v-if="onlineStoreTempList.length || onlineStoreTempListForeign.length"
-            >
-              <div class="form-item flex left">
-                <div class="fw-b">一键上架:</div>
-               </div>
-              <div class="form-item flex left mar-top-10" style="margin-right: 10px !important;"  v-if="onlineStoreTempList.length">
+          <!-- 详情资料准备 -->
+          <div class="right-section">
+            <div class="section-title">
+              <img src="@/assets/images/Photography/zhuangshi.png" style="width: 32px; height: 32px;" />
+              详情资料准备
+            </div>
+            <div class="data-prep-content">
+              <el-radio-group v-model="form.dataType" class="ml-4">
+                <el-radio label="1" size="large">Excel上传</el-radio>
+                <el-radio label="2" size="large">系统对接</el-radio>
+              </el-radio-group>
+
+              <div v-if="form.dataType == '1'" class="excel-upload-section">
+                <el-button
+                  type="primary"
+                  class="select-file-btn"
+                  @click="selectExcel"
+                  v-log="{ describe: { action: '点击选择Excel文件' } }"
+                >
+                  <img src="@/assets/images/Photography/wenjian.png" style="width: 16px; margin-right: 4px;" />
+                  点击选择文件
+                </el-button>
+                <el-button
+                  type="text"
+                  class="download-link"
+                  @click="downloadExcel"
+                  v-log="{ describe: { action: '点击下载Excel模板' } }"
+                >
+                  下载商品基础资料模版
+                </el-button>
+              </div>
+            </div>
+          </div>
+
+          <!-- 一键上架平台 -->
+          <div
+            class="right-section"
+            :class="{ 'publish-section--disabled': !canUsePublishSection }"
+            v-if="onlineStoreTempList.length || onlineStoreTempListForeign.length"
+          >
+            <div class="section-title">
+              <img src="@/assets/images/Photography/zhuangshi.png" style="width: 32px; height: 32px;" />
+              一键上架平台
+            </div>
+            <div class="publish-content">
+              <div class="form-item" v-if="onlineStoreTempList.length">
                 <div class="label">国内电商平台:</div>
                 <el-select
                   v-model="domesticPlatforms"
                   multiple
-                  placeholder="请选择平台"
-                  style="min-width: 200px;"
+                  placeholder="请选择"
+                  style="width: 100%;"
                   :disabled="!canUsePublishSection"
                 >
                   <el-option
@@ -210,13 +290,13 @@
                   />
                 </el-select>
               </div>
-              <div class="form-item flex left mar-top-10" v-if="onlineStoreTempListForeign.length">
-                <div class="label">国外电商平台:</div>
+              <div class="form-item" v-if="onlineStoreTempListForeign.length">
+                <div class="label">跨境电商平台:</div>
                 <el-select
                   v-model="foreignPlatforms"
                   multiple
-                  placeholder="请选择平台"
-                  style="min-width: 200px;"
+                  placeholder="请选择"
+                  style="width: 100%;"
                   :disabled="!canUsePublishSection"
                 >
                   <el-option
@@ -232,7 +312,6 @@
           </div>
         </div>
       </div>
-      <el-divider />
 
       <!-- 详情高级配置 -->
 <!--      <div class="section">
@@ -271,41 +350,19 @@
         </div>
       </div>
       <el-divider />-->
-      <!-- 详情资料准备部分 -->
-      <div class="data-prep-section">
-        <div class="flex-item left">
-          <div class="section-title">
-            <img src="@/assets/images/Photography/zhuangshi.png" style="width: 32px; height: 32px;" />
-            详情资料准备 (2选1)
-
-            <el-button v-if="form.dataType == '1'" type="text" class="mar-left-10 fs-16"  @click="downloadExcel" v-log="{ describe: { action: '点击下载Excel模板' } }">下载商品基础资料模版</el-button>
-          </div>
-        </div>
-
-        <div class="flex-item left">
-          <el-radio-group v-model="form.dataType" class="ml-4">
-            <el-radio label="1" size="large">EXCEL文件选择</el-radio>
-            <el-radio label="2" size="large">系统对接(和业务员联系)</el-radio>
-          </el-radio-group>
-        </div>
-        <div v-if="form.dataType == '1'" class="excel-upload">
-          <div class="flex bottom between">
-            <div style="max-width: 160px;" class="mar-left-20">商品基础资料EXCEL文件选择:</div>
-            <div class="flex bottom mar-left-20" style="flex-grow: 1;">
-              <el-input type="textarea" v-model="form.excel_path" />
-            </div>
-            <el-button class="select-button button--primary1  mar-left-20" type="primary" @click="selectExcel" v-log="{ describe: { action: '点击选择Excel文件' } }">
-              <img src="@/assets/images/Photography/wenjian.png" style="width: 16px; margin-right: 4px;" />
-              选择</el-button>
-          </div>
-        </div>
-      </div>
     </div>
+
     <!-- 底部按钮 -->
     <div class="footer">
-      <!-- <el-button class="button--primary1 footer-button" type="primary" @click="saveConfig">保存配置</el-button> -->
-      <!--  <el-button class="button--primary1 footer-button" type="primary" @click="startProcess">开始处理</el-button>  -->
-      <el-button v-loading="requesting" class="button--primary1 footer-button" type="primary" @click="generate" v-log="{ describe: { action: '点击开始生成详情页' } }">开始生成</el-button>
+      <el-button
+        v-loading="requesting"
+        class="button--primary1 footer-button"
+        type="primary"
+        @click="generate"
+        v-log="{ describe: { action: '点击开始生成详情页' } }"
+      >
+        开始生成→
+      </el-button>
     </div>
   </div>
 
@@ -374,7 +431,7 @@ import { clickLog, setLogInfo } from '@/utils/log'
 
 import { ElMessage, ElMessageBox } from 'element-plus'
 
-import headerBar from '@/components/header-bar/index.vue'
+import BlueHeaderBar from '@/components/header-bar/blue-header.vue'
 import { ref, computed, reactive, onMounted, onBeforeUnmount, nextTick, watch } from 'vue';
 import { Select, EditPen } from '@element-plus/icons-vue'
 import upload from '@/components/upload'
@@ -1540,13 +1597,96 @@ const selectFolder = () => {
 </script>
 
 <style lang="scss" scoped>
+// 服务标签页样式
+.service-tabs {
+  display: flex;
+  gap: 10px;
+  padding: 20px;
+  background: #fff;
+  border-bottom: 1px solid #e8e8e8;
+  margin-top: 50px;
+
+  .service-tab {
+    display: flex;
+    align-items: center;
+    gap: 8px;
+    padding: 10px 20px;
+    border: 2px solid #e8e8e8;
+    border-radius: 8px;
+    cursor: pointer;
+    transition: all 0.3s;
+    position: relative;
+    background: #fff;
+
+    .tab-checkbox {
+      margin-right: 0;
+    }
+
+    .tab-icon {
+      width: 24px;
+      height: 24px;
+    }
+
+    .tab-name {
+      font-size: 14px;
+      color: #333;
+    }
+
+    .tab-edit-btn {
+      position: absolute;
+      right: 8px;
+      top: 50%;
+      transform: translateY(-50%);
+      width: 24px;
+      height: 24px;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      background: rgba(41, 87, 255, 0.1);
+      border-radius: 4px;
+      opacity: 0;
+      transition: opacity 0.3s;
+      cursor: pointer;
+
+      .el-icon {
+        color: #2957FF;
+        font-size: 16px;
+      }
+    }
+
+    &:hover {
+      border-color: #2957FF;
+
+      .tab-edit-btn {
+        opacity: 1;
+      }
+    }
+
+    &.active {
+      border-color: #2957FF;
+      background: #F0F5FF;
+    }
+
+    &.disabled {
+      opacity: 0.5;
+      cursor: not-allowed;
+      background: #f5f5f5;
+    }
+  }
+}
+
 .detail-container {
-  background-color: #EAECED;
+  background: #F5F6F7;
+  min-height: calc(100vh - 130px);
+  padding: 20px;
+
+  .detail-content {
+    max-width: 100%;
+  }
   width: 100%;
   min-width: 600px;
-  padding: 20px;
   overflow: hidden;
-  min-height: calc(100vh - 30px);
+  padding-bottom: 100px; // 为底部按钮留出空间
 
 }
 
@@ -1662,6 +1802,20 @@ const selectFolder = () => {
   margin-bottom: 20px;
 }
 
+.template-section {
+  background: #fff;
+  padding: 20px;
+  border-radius: 8px;
+  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
+
+  .section-header {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    margin-bottom: 20px;
+  }
+}
+
 .template-section--disabled {
   opacity: 0.8;
 
@@ -1675,6 +1829,78 @@ const selectFolder = () => {
   opacity: 0.6;
 }
 
+// 主布局:左右分栏
+.main-layout {
+  display: flex;
+  gap: 20px;
+  align-items: flex-start;
+
+  .left-panel {
+    flex: 1;
+    min-width: 0;
+  }
+
+  .right-panel {
+    width: 400px;
+    flex-shrink: 0;
+    display: flex;
+    flex-direction: column;
+    gap: 20px;
+  }
+}
+
+.right-section {
+  background: #fff;
+  padding: 20px;
+  border-radius: 8px;
+  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
+
+  .section-title {
+    margin-bottom: 16px;
+  }
+
+  .data-prep-content {
+    display: flex;
+    flex-direction: column;
+    gap: 15px;
+
+    .excel-upload-section {
+      display: flex;
+      flex-direction: column;
+      gap: 10px;
+
+      .select-file-btn {
+        width: 100%;
+      }
+
+      .download-link {
+        color: #2957FF;
+        text-decoration: underline;
+        padding: 0;
+      }
+    }
+  }
+
+  .publish-content {
+    display: flex;
+    flex-direction: column;
+    gap: 15px;
+
+    .form-item {
+      display: flex;
+      flex-direction: column;
+      gap: 8px;
+
+      .label {
+        min-width: auto;
+        margin-right: 0;
+        font-size: 14px;
+        color: #666;
+      }
+    }
+  }
+}
+
 .logo-template-row {
   display: flex;
   gap: 24px;
@@ -1936,11 +2162,20 @@ const selectFolder = () => {
 
 .footer {
   display: flex;
-  justify-content: center;
-  margin-top: 24px;
+  justify-content: flex-end;
+  padding: 20px;
+  background: #fff;
+  border-top: 1px solid #e8e8e8;
+  position: fixed;
+  bottom: 0;
+  left: 0;
+  right: 0;
+  z-index: 100;
 
   .footer-button {
-    padding: 10px 20px;
+    padding: 12px 40px;
+    font-size: 16px;
+    border-radius: 8px;
   }
 }