<template>
  <div class="page-assembly">
    <!-- ==================== 列表视图 ==================== -->
    <template v-if="viewState === 'list'">
      <el-card>
        <template #header>
          <div class="search-section">
          <div class="search-row">
            <el-form :model="searchForm" label-width="80px" inline>
              <el-form-item label="页面ID:">
                <el-input v-model="searchForm.id" placeholder="请输入" clearable style="width:160px" />
              </el-form-item>
              <el-form-item label="页面名称:">
                <el-input v-model="searchForm.name" placeholder="请输入" clearable style="width:160px" />
              </el-form-item>
              <div class="button-group">
                <el-button type="primary" @click="handleSearch">查询</el-button>
                <el-button @click="handleReset">重置</el-button>
              </div>
            </el-form>
          </div>
          <div class="action-row">
            <el-button type="primary" @click="handleCreate">
              <el-icon><Plus /></el-icon>新建组装页
            </el-button>
          </div>
          </div>
        </template>

        <el-table :data="tableData" stripe border style="width:100%">
          <el-table-column label="操作" width="120" fixed="left" align="center">
            <template #default="scope">
              <div class="list-actions">
                <el-button type="text" size="small" @click="handleEdit(scope.row)">编辑</el-button>
                <el-button type="text" size="small" @click="showLinkDialog(scope.row.id)">链接</el-button>
              </div>
            </template>
          </el-table-column>
          <el-table-column prop="id" label="页面ID" width="120" align="center" />
          <el-table-column prop="name" label="组装页名称" min-width="200" show-overflow-tooltip />
          <el-table-column label="发布状态" width="100" align="center">
            <template #default="scope">
              <el-tag :type="scope.row.status === 'published' ? 'success' : 'info'" size="small">
                {{ scope.row.status === 'published' ? '已上架' : '草稿' }}
              </el-tag>
            </template>
          </el-table-column>
          <el-table-column prop="createTime" label="创建时间" width="180" align="center" />
          <el-table-column prop="updateTime" label="更新时间" width="180" align="center" />
        </el-table>
      </el-card>
    </template>

    <!-- ==================== 编辑视图 ==================== -->
    <template v-else>
      <!-- 操作栏 -->
      <div class="toolbar">
        <div class="toolbar-left">
          <el-button size="small" @click="backToList">
            <el-icon><ArrowLeft /></el-icon>返回
          </el-button>
          <h2 class="toolbar-title">{{ isNew ? '新建组装页' : '编辑组装页' }}</h2>
          <el-tag v-if="editData.status === 'published' && !isModified" type="success" size="small" effect="plain">正式线上环境</el-tag>
          <el-tag v-else-if="showModifiedStatus" type="warning" size="small" effect="plain">有未发布的修改</el-tag>
          <el-tag v-else size="info" effect="plain">草稿 (未发布)</el-tag>
        </div>
        <div class="toolbar-right">
          <el-button size="small" @click="showLinkDialog(editData.id)">
            <el-icon><Link /></el-icon>获取链接
          </el-button>
          <el-button size="small" type="primary" plain @click="handleSaveAssembly('draft')">保存草稿</el-button>
          <el-button v-if="canPublish" size="small" type="primary" @click="handleSaveAssembly('publish')">发布上线</el-button>
        </div>
      </div>

      <!-- 基本信息 -->
      <el-card class="section-card" shadow="hover">
        <div class="basic-info">
          <div class="info-item">
            <span class="info-label">页面 ID：</span>
            <el-tag size="small" effect="plain">{{ editData.id || '保存后生成' }}</el-tag>
          </div>
          <div class="info-item" style="flex:1">
            <span class="info-label">组装页名称 <span style="color:#F56C6C">*</span></span>
            <el-input v-model="editData.name" placeholder="请为该页面设置一个名称 (必填)" size="small" @input="isModified = true" />
          </div>
        </div>
      </el-card>

      <!-- 微信分享卡片配置 -->
      <div class="block-card">
        <div class="block-header">
          <span><el-icon><ChatDotRound /></el-icon> 微信分享卡片配置</span>
          <el-checkbox v-model="editData.shareConfig.enabled" @change="isModified = true" class="share-toggle-checkbox">自定义微信分享</el-checkbox>
        </div>
        <div class="block-body" v-if="editData.shareConfig.enabled">
          <div class="share-card-body">
            <div class="share-thumb-section">
              <div class="share-field-label">分享缩略图 <span class="required-star">*</span></div>
              <div class="share-thumb-upload" @click="triggerShareUpload">
                <img v-if="editData.shareConfig.thumbnail" :src="editData.shareConfig.thumbnail" class="share-thumb-img" />
                <div v-else class="upload-placeholder">
                  <el-icon><Picture /></el-icon>
                </div>
                <div class="upload-overlay">
                  <el-icon><Upload /></el-icon>
                  <span>点击上传</span>
                </div>
              </div>
              <div class="share-thumb-tip">建议 100x100</div>
              <input ref="shareUpload" type="file" accept="image/*" style="display:none" @change="onShareImageUpload" />
            </div>
            <div class="share-fields">
              <el-form label-position="top">
                <el-form-item>
                  <template #label>
                    分享标题 <span class="required-star">*</span>
                  </template>
                  <el-input
                    v-model="editData.shareConfig.title"
                    placeholder="请输入分享标题"
                    @input="isModified = true"
                  />
                </el-form-item>
                <el-form-item label="分享描述">
                  <el-input
                    v-model="editData.shareConfig.desc"
                    type="textarea"
                    :rows="3"
                    placeholder="请输入分享描述语"
                    @input="isModified = true"
                  />
                </el-form-item>
              </el-form>
            </div>
          </div>
        </div>
      </div>

      <!-- 模块列表 -->
      <div v-for="(block, bIdx) in editData.blocks" :key="block.id" class="block-card">
        <!-- 模块操作栏 -->
        <div class="block-actions">
          <el-popover placement="bottom" trigger="click" width="180">
            <div>
              <div style="margin-bottom:8px;font-size:13px;color:#666">移动到序号：</div>
              <div style="display:flex;gap:8px">
                <el-input-number v-model="sortTarget" :min="1" :max="editData.blocks.length" size="small" controls-position="right" style="width:100px" />
                <el-button size="small" type="primary" @click="moveBlock(bIdx, sortTarget - 1)">确定</el-button>
              </div>
            </div>
            <template #reference>
              <el-button size="small" @click="sortTarget = bIdx + 1">
                <el-icon><Sort /></el-icon>排序
              </el-button>
            </template>
          </el-popover>
          <el-button v-if="block.type !== 'privilege'" size="small" @click="duplicateBlock(bIdx)">
            <el-icon><CopyDocument /></el-icon>复制
          </el-button>
          <el-button size="small" type="danger" @click="removeBlock(bIdx)">
            <el-icon><Delete /></el-icon>删除
          </el-button>
        </div>

        <!-- 图片模块 -->
        <template v-if="block.type === 'image'">
          <div class="block-header"><el-icon><PictureRounded /></el-icon> 图片模块</div>
          <div class="block-body">
            <div class="image-upload-area">
              <div class="upload-box upload-box-wide" @click="triggerBlockUpload(bIdx)">
                <img v-if="block.data.imageUrl" :src="block.data.imageUrl" class="preview-img" />
                <div v-else class="upload-placeholder">
                  <el-icon><Picture /></el-icon><span>未上传图片</span>
                </div>
                <div class="upload-overlay"><el-icon><Upload /></el-icon><span>点击上传图片</span></div>
              </div>
              <div class="upload-tip">建议宽度：<b>750px</b><br/>高度不限，系统将按比例自适应展示</div>
              <input :ref="'blockUpload' + bIdx" type="file" accept="image/*" style="display:none" @change="onBlockImageUpload($event, bIdx)" />
            </div>
          </div>
        </template>

        <!-- 权益商品模块 -->
        <template v-else-if="block.type === 'privilege'">
          <div class="block-header"><el-icon><Grid /></el-icon> 权益商品模块 <span class="block-hint">(当前页面仅限添加一个)</span></div>
          <div class="block-body">
            <el-form label-width="100px" style="margin-bottom:16px;border-bottom:1px solid #ebeef5;padding-bottom:16px">
              <el-form-item label="可配置大标题">
                <el-input v-model="block.data.title" style="width:50%" @input="isModified = true" />
              </el-form-item>
            </el-form>
            <div v-for="(pItem, pIdx) in block.data.items" :key="pIdx" class="privilege-row">
              <el-tag size="small" effect="plain" class="privilege-no">NO. {{ pIdx + 1 }}</el-tag>
              <div class="privilege-icon"><el-icon><Document /></el-icon></div>
              <div class="privilege-info">
                <div class="privilege-title">{{ pItem.title }}</div>
                <div class="privilege-desc">{{ pItem.desc }}</div>
              </div>
              <el-popover placement="bottom" trigger="click" width="180">
                <div>
                  <div style="margin-bottom:8px;font-size:13px;color:#666">移动到序号：</div>
                  <div style="display:flex;gap:8px">
                    <el-input-number v-model="sortTarget" :min="1" :max="block.data.items.length" size="small" controls-position="right" style="width:100px" />
                    <el-button size="small" type="primary" @click="movePrivilegeItem(bIdx, pIdx, sortTarget - 1)">确定</el-button>
                  </div>
                </div>
                <template #reference>
                  <el-button size="small" circle @click="sortTarget = pIdx + 1">
                    <el-icon><Sort /></el-icon>
                  </el-button>
                </template>
              </el-popover>
            </div>
          </div>
        </template>

        <!-- 图文模块 (富文本) -->
        <template v-else-if="block.type === 'richtext'">
          <div class="block-header"><el-icon><Notebook /></el-icon> 图文模块</div>
          <div class="block-body">
            <quill-editor
              v-model:content="block.data.content"
              content-type="html"
              :options="quillOptions"
              @change="onQuillChange"
            />
          </div>
        </template>

        <!-- 按钮模块 -->
        <template v-else-if="block.type === 'buttonGroup'">
          <div class="block-header button-module-header">
            <span class="button-module-title"><el-icon><Pointer /></el-icon> 按钮模块</span>
            <el-button class="add-button-btn" type="primary" plain size="small" @click="addButton(bIdx)">
              <el-icon><CirclePlus /></el-icon>添加按钮
            </el-button>
          </div>
          <div class="block-body">
            <div v-if="!block.data.buttons || block.data.buttons.length === 0" class="empty-tip">暂无按钮</div>
            <div v-for="(btn, btnIdx) in (block.data.buttons || [])" :key="btnIdx" class="button-config-item">
              <div class="btn-index">BTN {{ btnIdx + 1 }}</div>
              <div class="btn-config-body">
                <el-form class="button-form" label-position="top">
                  <el-row :gutter="16">
                    <el-col :span="8">
                      <el-form-item label="按钮文案">
                        <el-input v-model="btn.text" size="small" @input="isModified = true" />
                      </el-form-item>
                    </el-col>
                    <el-col :span="8">
                      <el-form-item label="点击动作">
                        <el-select v-model="btn.actionType" size="small" @change="handleButtonActionChange(btn)">
                          <el-option label="跳转链接" value="link" />
                          <el-option label="弹窗提示" value="dialog" />
                        </el-select>
                      </el-form-item>
                    </el-col>
                    <el-col :span="8">
                      <el-form-item label="按钮样式">
                        <el-select v-model="btn.style" size="small" @change="isModified = true">
                          <el-option label="突出显示（红色）" value="primary" />
                          <el-option label="次要操作（白色）" value="secondary" />
                        </el-select>
                      </el-form-item>
                    </el-col>
                  </el-row>
                  <!-- 跳转链接 -->
                  <div v-if="btn.actionType === 'link'" class="jump-config-panel">
                    <div class="jump-config-title"><el-icon><Link /></el-icon> 跳转链接配置</div>
                    <el-form-item class="jump-type-item" label-width="0">
                      <el-radio-group v-model="btn.linkType" @change="handleButtonJumpTypeChange(btn)">
                        <el-radio label="h5">H5链接跳转</el-radio>
                        <el-radio label="miniProgram">小程序跳转</el-radio>
                        <el-radio label="taikoo">太古地产</el-radio>
                        <el-radio label="dragon">龙腾</el-radio>
                      </el-radio-group>
                    </el-form-item>
                    <el-form-item v-if="btn.linkType === 'h5'" label="配置链接">
                      <el-input v-model="btn.linkH5" size="small" placeholder="例如：https://hsbc.com" @input="handleButtonJumpFieldChange(btn)" />
                    </el-form-item>
                    <el-row v-if="btn.linkType === 'miniProgram'" :gutter="16">
                      <el-col :span="12">
                        <el-form-item label="AppID">
                          <el-input v-model="btn.miniAppId" size="small" placeholder="例如：wx1234567890abcdef" @input="handleButtonJumpFieldChange(btn)" />
                        </el-form-item>
                      </el-col>
                      <el-col :span="12">
                        <el-form-item label="页面路径">
                          <el-input v-model="btn.miniAppPath" size="small" placeholder="例如：pages/index/index" @input="handleButtonJumpFieldChange(btn)" />
                        </el-form-item>
                      </el-col>
                    </el-row>
                  </div>
                  <!-- 弹窗配置 -->
                  <div v-if="btn.actionType === 'dialog' && btn.dialog" class="dialog-config">
                    <div class="dialog-config-title"><el-icon><Document /></el-icon> 弹窗内容配置</div>
                    <el-form-item label="弹窗标题">
                      <el-input v-model="btn.dialog.title" size="small" placeholder="弹窗标题" @input="isModified = true" />
                    </el-form-item>
                    <el-form-item label="弹窗描述">
                      <div class="dialog-desc-editor">
                        <quill-editor
                          v-model:content="btn.dialog.desc"
                          content-type="html"
                          :options="quillOptions"
                          @change="isModified = true"
                        />
                      </div>
                    </el-form-item>
                    <div class="dialog-button-config">
                      <div class="confirm-config-header">
                        <span>左侧取消按钮配置</span>
                      </div>
                      <el-input v-model="btn.dialog.cancelText" size="small" placeholder="取消" @input="isModified = true" />
                    </div>
                    <div class="dialog-button-config confirm-button-config">
                      <div class="confirm-config-header">
                        <span>右侧确认按钮配置</span>
                        <el-checkbox v-model="btn.dialog.showConfirm" @change="isModified = true">展示</el-checkbox>
                      </div>
                      <el-input v-if="btn.dialog.showConfirm" v-model="btn.dialog.confirmText" size="small" placeholder="确认" @input="isModified = true" />
                    </div>
                    <div v-if="btn.dialog.showConfirm" class="jump-config-panel dialog-jump-config">
                      <div class="jump-config-title"><el-icon><Link /></el-icon> 确认按钮跳转链接配置</div>
                      <el-form-item class="jump-type-item" label-width="0">
                        <el-radio-group v-model="btn.dialog.confirmLinkType" @change="handleDialogJumpTypeChange(btn.dialog)">
                          <el-radio label="h5">H5链接跳转</el-radio>
                          <el-radio label="miniProgram">小程序跳转</el-radio>
                          <el-radio label="taikoo">太古地产</el-radio>
                          <el-radio label="dragon">龙腾</el-radio>
                        </el-radio-group>
                      </el-form-item>
                      <el-form-item v-if="btn.dialog.confirmLinkType === 'h5'" label="配置链接">
                        <el-input v-model="btn.dialog.confirmLinkH5" size="small" placeholder="例如：https://hsbc.com" @input="handleDialogJumpFieldChange(btn.dialog)" />
                      </el-form-item>
                      <el-row v-if="btn.dialog.confirmLinkType === 'miniProgram'" :gutter="16">
                        <el-col :span="12">
                          <el-form-item label="AppID">
                            <el-input v-model="btn.dialog.confirmMiniAppId" size="small" placeholder="例如：wx1234567890abcdef" @input="handleDialogJumpFieldChange(btn.dialog)" />
                          </el-form-item>
                        </el-col>
                        <el-col :span="12">
                          <el-form-item label="页面路径">
                            <el-input v-model="btn.dialog.confirmMiniAppPath" size="small" placeholder="例如：pages/index/index" @input="handleDialogJumpFieldChange(btn.dialog)" />
                          </el-form-item>
                        </el-col>
                      </el-row>
                    </div>
                  </div>
                </el-form>
              </div>
              <div class="btn-config-actions">
                <el-popover placement="bottom" trigger="click" width="180">
                  <div>
                    <div style="margin-bottom:8px;font-size:13px;color:#666">移动到序号：</div>
                    <div style="display:flex;gap:8px">
                      <el-input-number v-model="sortTarget" :min="1" :max="block.data.buttons.length" size="small" controls-position="right" style="width:100px" />
                      <el-button size="small" type="primary" @click="moveButton(bIdx, btnIdx, sortTarget - 1)">确定</el-button>
                    </div>
                  </div>
                  <template #reference>
                    <el-button size="small" circle @click="sortTarget = btnIdx + 1">
                      <el-icon><Sort /></el-icon>
                    </el-button>
                  </template>
                </el-popover>
                <el-button size="small" type="danger" circle @click="removeButton(bIdx, btnIdx)">
                  <el-icon><Delete /></el-icon>
                </el-button>
              </div>
            </div>
          </div>
        </template>
      </div>

      <!-- 添加模块按钮 -->
      <div class="add-block-bar">
        <el-button type="text" @click="addBlock('image')">
          <el-icon><PictureRounded /></el-icon>图片模块
        </el-button>
        <el-button v-if="!hasPrivilegeBlock" type="text" @click="addBlock('privilege')">
          <el-icon><Grid /></el-icon>权益商品模块
        </el-button>
        <el-button type="text" @click="addBlock('richtext')">
          <el-icon><Notebook /></el-icon>图文模块
        </el-button>
        <el-button type="text" @click="addBlock('buttonGroup')">
          <el-icon><Pointer /></el-icon>按钮模块
        </el-button>
      </div>
    </template>

    <!-- 获取链接弹窗 -->
    <el-dialog title="获取页面链接" v-model="linkDialogVisible" width="680px">
      <el-form label-width="100px" inline>
        <el-form-item label="Campaign ID">
          <el-select v-model="linkForm.campaignId" clearable filterable placeholder="请选择 (无)" style="width:240px">
            <el-option v-for="opt in campaignOptions" :key="opt.value" :label="opt.label" :value="opt.value" />
          </el-select>
        </el-form-item>
        <el-form-item label="Channel ID">
          <el-select v-model="linkForm.channelId" clearable filterable placeholder="请选择 (无)" style="width:240px">
            <el-option v-for="opt in channelOptions" :key="opt.value" :label="opt.label" :value="opt.value" />
          </el-select>
        </el-form-item>
      </el-form>
      <div v-if="linkTargetPublished" class="link-section">
        <div class="link-label"><span class="dot dot-green"></span> 正式环境链接</div>
        <div class="link-row">
          <span class="link-type">H5链接</span>
          <el-input :value="assemblyProdH5" readonly size="small" />
          <el-button size="small" type="primary" plain @click="copyLink(assemblyProdH5)">复制</el-button>
        </div>
        <div class="link-row">
          <span class="link-type">小程序链接</span>
          <el-input :value="assemblyProdApp" readonly size="small" />
          <el-button size="small" type="primary" plain @click="copyLink(assemblyProdApp)">复制</el-button>
        </div>
      </div>
      <div class="link-section">
        <div class="link-label"><span class="dot dot-blue"></span> 草稿预览链接 (测试)</div>
        <div class="link-row">
          <el-input :value="assemblyTestUrl" readonly size="small" />
          <el-button size="small" type="primary" plain @click="copyLink(assemblyTestUrl)">复制</el-button>
        </div>
      </div>
    </el-dialog>
  </div>
</template>

<script>
import { ElMessage, ElMessageBox } from 'element-plus'
import request from '@/utils/request'
import { fetchLinkOptions } from '@/utils/linkOptions'

let blockIdCounter = 0
function genBlockId() {
  return 'b_' + (++blockIdCounter) + '_' + Math.random().toString(36).substr(2, 6)
}

export default {
  name: 'PageAssembly',
  data() {
    return {
      viewState: 'list',
      tableData: [],
      searchForm: { id: '', name: '' },
      editData: this.createEmptyAssembly(),
      isNew: true,
      isModified: false,
      quillReady: false,
      sortTarget: 1,
      linkDialogVisible: false,
      linkForm: { campaignId: '', channelId: '' },
      campaignOptions: [],
      channelOptions: [],
      linkTargetId: '',
      linkTargetPublished: false,
      quillOptions: {
        placeholder: '请输入图文内容...',
        modules: {
          toolbar: [
            ['bold', 'italic', 'underline'],
            [{ list: 'ordered' }, { list: 'bullet' }],
            [{ header: [1, 2, 3, false] }],
            ['link', 'image'],
            ['clean']
          ]
        }
      }
    }
  },
  computed: {
    hasPrivilegeBlock() {
      return this.editData.blocks.some(b => b.type === 'privilege')
    },
    showModifiedStatus() {
      return this.editData.status === 'modified' || (this.editData.status === 'published' && this.isModified)
    },
    canPublish() {
      return !this.isModified && !!this.editData.id && ['draft', 'modified'].includes(this.editData.status)
    },
    linkQueryStr() {
      const params = []
      if (this.linkForm.campaignId) params.push(`CampaignId=${this.linkForm.campaignId}`)
      if (this.linkForm.channelId) params.push(`Channelid=${this.linkForm.channelId}`)
      return params.length ? '&' + params.join('&') : ''
    },
    assemblyProdH5() {
      return `https://hsbc.com/assembly?id=${this.linkTargetId}${this.linkQueryStr}`
    },
    assemblyProdApp() {
      return `hsbc-app://assembly?id=${this.linkTargetId}${this.linkQueryStr}`
    },
    assemblyTestUrl() {
      return `https://test.hsbc.com/assembly?id=${this.linkTargetId}&draft=1${this.linkQueryStr}`
    }
  },
  created() {
    this.fetchList()
  },
  methods: {
    createDefaultJumpConfig(overrides = {}) {
      return {
        linkType: 'h5',
        fixedLink: '',
        linkH5: '',
        miniAppId: '',
        miniAppPath: '',
        ...overrides
      }
    },
    createDefaultDialogJumpConfig(overrides = {}) {
      return {
        confirmLinkType: 'h5',
        confirmFixedLink: '',
        confirmLinkH5: '',
        confirmMiniAppId: '',
        confirmMiniAppPath: '',
        ...overrides
      }
    },
    createDefaultButtonDialog() {
      return {
        title: '',
        desc: '',
        cancelText: '取消',
        showConfirm: true,
        confirmText: '确认',
        confirmLink: '',
        ...this.createDefaultDialogJumpConfig()
      }
    },
    createDefaultButton(overrides = {}) {
      return {
        text: '新按钮',
        link: '',
        style: 'secondary',
        actionType: 'link',
        ...this.createDefaultJumpConfig(),
        ...overrides,
        dialog: {
          ...this.createDefaultButtonDialog(),
          ...(overrides.dialog || {})
        }
      }
    },
    createDefaultShareConfig(overrides = {}) {
      return {
        enabled: false,
        thumbnail: '',
        title: '',
        desc: '',
        ...overrides
      }
    },
    ensureReactiveField(target, key, value) {
      if (Object.prototype.hasOwnProperty.call(target, key)) {
        target[key] = value
      } else {
        target[key] = value
      }
    },
    getPartnerFixedLink(type) {
      if (type === 'taikoo') return '太古地产'
      if (type === 'dragon') return '龙腾'
      return ''
    },
    isFixedJumpValue(value) {
      return value === '太古地产' || value === '龙腾'
    },
    normalizePartnerJumpType(type, fixedLink = '') {
      if (type === 'taikoo' || type === 'dragon') return type
      if (type === 'fixed') return fixedLink === '龙腾' ? 'dragon' : 'taikoo'
      return type
    },
    inferButtonJumpType(btn = {}) {
      if (btn.linkType) return this.normalizePartnerJumpType(btn.linkType, btn.fixedLink || btn.link)
      if (btn.miniAppId || btn.miniAppPath) return 'miniProgram'
      if (btn.link === '太古地产') return 'taikoo'
      if (btn.link === '龙腾') return 'dragon'
      return 'h5'
    },
    inferDialogJumpType(dialog = {}) {
      if (dialog.confirmLinkType) return this.normalizePartnerJumpType(dialog.confirmLinkType, dialog.confirmFixedLink || dialog.confirmLink)
      if (dialog.confirmMiniAppId || dialog.confirmMiniAppPath) return 'miniProgram'
      if (dialog.confirmLink === '太古地产') return 'taikoo'
      if (dialog.confirmLink === '龙腾') return 'dragon'
      if (dialog.confirmLinkH5 || dialog.confirmLink) return 'h5'
      return 'h5'
    },
    syncButtonLegacyLink(btn) {
      const partnerLink = this.getPartnerFixedLink(btn.linkType)
      if (partnerLink) btn.link = partnerLink
      if (btn.linkType === 'h5') btn.link = btn.linkH5 || ''
      if (btn.linkType === 'miniProgram') btn.link = ''
    },
    syncDialogLegacyLink(dialog) {
      const partnerLink = this.getPartnerFixedLink(dialog.confirmLinkType)
      if (partnerLink) dialog.confirmLink = partnerLink
      if (dialog.confirmLinkType === 'h5') dialog.confirmLink = dialog.confirmLinkH5 || ''
      if (dialog.confirmLinkType === 'miniProgram') dialog.confirmLink = ''
    },
    clearInactiveButtonJumpFields(btn) {
      btn.fixedLink = this.getPartnerFixedLink(btn.linkType)
      if (btn.linkType !== 'h5') btn.linkH5 = ''
      if (btn.linkType !== 'miniProgram') {
        btn.miniAppId = ''
        btn.miniAppPath = ''
      }
      this.syncButtonLegacyLink(btn)
    },
    clearInactiveDialogJumpFields(dialog) {
      dialog.confirmFixedLink = this.getPartnerFixedLink(dialog.confirmLinkType)
      if (dialog.confirmLinkType !== 'h5') dialog.confirmLinkH5 = ''
      if (dialog.confirmLinkType !== 'miniProgram') {
        dialog.confirmMiniAppId = ''
        dialog.confirmMiniAppPath = ''
      }
      this.syncDialogLegacyLink(dialog)
    },
    normalizeButtonJumpConfig(btn) {
      const linkType = this.inferButtonJumpType(btn)
      const legacyLink = btn.link || ''
      const normalized = this.createDefaultJumpConfig({
        ...btn,
        linkType,
        fixedLink: btn.fixedLink || (this.isFixedJumpValue(legacyLink) ? legacyLink : ''),
        linkH5: btn.linkH5 || (!this.isFixedJumpValue(legacyLink) ? legacyLink : ''),
        miniAppId: btn.miniAppId || '',
        miniAppPath: btn.miniAppPath || ''
      })
      Object.keys(normalized).forEach(key => {
        btn[key] = normalized[key]
      })
      this.clearInactiveButtonJumpFields(btn)
    },
    normalizeDialogJumpConfig(dialog) {
      const confirmLinkType = this.inferDialogJumpType(dialog)
      const legacyLink = dialog.confirmLink || ''
      const normalized = this.createDefaultDialogJumpConfig({
        ...dialog,
        confirmLinkType,
        confirmFixedLink: dialog.confirmFixedLink || (this.isFixedJumpValue(legacyLink) ? legacyLink : ''),
        confirmLinkH5: dialog.confirmLinkH5 || (!this.isFixedJumpValue(legacyLink) ? legacyLink : ''),
        confirmMiniAppId: dialog.confirmMiniAppId || '',
        confirmMiniAppPath: dialog.confirmMiniAppPath || ''
      })
      Object.keys(normalized).forEach(key => {
        dialog[key] = normalized[key]
      })
      this.clearInactiveDialogJumpFields(dialog)
    },
    normalizeButton(btn) {
      if (!btn) return
      if (!btn.text) this.ensureReactiveField(btn, 'text', '')
      if (!btn.style) this.ensureReactiveField(btn, 'style', 'secondary')
      if (!btn.actionType) this.ensureReactiveField(btn, 'actionType', 'link')
      if (!Object.prototype.hasOwnProperty.call(btn, 'link')) this.ensureReactiveField(btn, 'link', '')
      this.normalizeButtonJumpConfig(btn)
      if (!btn.dialog) {
        this.ensureReactiveField(btn, 'dialog', this.createDefaultButtonDialog())
      } else {
        const defaults = this.createDefaultButtonDialog()
        Object.keys(defaults).forEach(key => {
          if (!Object.prototype.hasOwnProperty.call(btn.dialog, key)) {
            btn.dialog[key] = defaults[key]
          }
        })
      }
      this.normalizeDialogJumpConfig(btn.dialog)
    },
    normalizeAssemblyData(data) {
      data.shareConfig = this.createDefaultShareConfig(data.shareConfig || {})
      const blocks = data.blocks || []
      blocks.forEach(block => {
        if (block.type !== 'buttonGroup') return
        if (!block.data) block.data = { buttons: [] }
        if (!block.data.buttons) block.data.buttons = []
        block.data.buttons.forEach(btn => this.normalizeButton(btn))
      })
      return data
    },
    createEmptyAssembly() {
      return {
        id: '', name: '', status: 'draft',
        shareConfig: this.createDefaultShareConfig(),
        blocks: []
      }
    },
    async fetchList() {
      const res = await request.get('/api/assembly/list', { params: this.searchForm })
      this.tableData = res.data.sort((a, b) => b.id.localeCompare(a.id))
    },
    async loadLinkOptions() {
      const { campaignOptions, channelOptions } = await fetchLinkOptions()
      this.campaignOptions = campaignOptions
      this.channelOptions = channelOptions
    },
    handleSearch() {
      this.fetchList()
    },
    handleReset() {
      this.searchForm = { id: '', name: '' }
      this.fetchList()
    },
    handleCreate() {
      this.editData = this.createEmptyAssembly()
      this.isNew = true
      this.isModified = true
      this.quillReady = false
      this.viewState = 'edit'
      this.$nextTick(() => { this.quillReady = true })
    },
    handleEdit(row) {
      this.editData = this.normalizeAssemblyData(JSON.parse(JSON.stringify(row)))
      this.isNew = false
      this.isModified = false
      this.quillReady = false
      this.viewState = 'edit'
      this.$nextTick(() => { this.quillReady = true })
    },
    async handleDelete(row) {
      await ElMessageBox.confirm('确认要删除该组装页吗？此操作不可恢复。', '提示', { type: 'warning' })
      await request.post('/api/assembly/delete', { id: row.id })
      ElMessage.success('删除成功')
      this.fetchList()
    },
    backToList() {
      this.viewState = 'list'
      this.fetchList()
    },
    async handleSaveAssembly(type) {
      const data = this.editData
      this.normalizeAssemblyData(data)
      let hasError = false
      data.blocks.forEach(block => {
        if (block.type === 'buttonGroup') {
          block.data.buttons.forEach(btn => {
            if (btn.actionType === 'link' && !this.validateButtonJumpConfig(btn)) hasError = true
            if (btn.actionType === 'dialog' && btn.dialog && btn.dialog.showConfirm !== false && !this.validateDialogJumpConfig(btn.dialog)) hasError = true
          })
        }
      })
      if (hasError) {
        ElMessage.error('保存失败：按钮模块中存在未填写完整的跳转链接配置！')
        return
      }
      if (data.shareConfig.enabled) {
        if (!data.shareConfig.thumbnail) {
          ElMessage.error('保存失败：分享缩略图为必填项！')
          return
        }
        if (!data.shareConfig.title.trim()) {
          ElMessage.error('保存失败：分享标题为必填项！')
          return
        }
      }
      if (!data.name.trim()) data.name = '未命名组装页'
      if (type === 'publish' && this.isModified) {
        ElMessage.warning('请先保存草稿后再发布上线')
        return
      }
      const currentStatus = data.status
      data.status = type === 'publish' ? 'published' : (currentStatus === 'published' || currentStatus === 'modified' ? 'modified' : 'draft')

      const res = await request.post('/api/assembly/save', data)
      if (!data.id) data.id = res.data.id
      if (type === 'draft') this.isNew = false
      this.isModified = false
      ElMessage.success(type === 'publish' ? '已发布组装页配置！' : '已保存组装页配置草稿！')
      if (type === 'publish') this.backToList()
    },

    onQuillChange() {
      if (this.quillReady) this.isModified = true
    },

    triggerShareUpload() {
      const ref = this.$refs.shareUpload
      if (ref) ref.click()
    },
    onShareImageUpload(event) {
      const file = event.target.files[0]
      if (!file) return
      const reader = new FileReader()
      reader.onload = (e) => {
        this.editData.shareConfig.thumbnail = e.target.result
        this.isModified = true
      }
      reader.readAsDataURL(file)
      event.target.value = ''
    },

    // 模块操作
    addBlock(type) {
      const block = { type, id: genBlockId(), data: {} }
      if (type === 'image') block.data = { imageUrl: '' }
      if (type === 'privilege') block.data = { title: '新权益标题', items: [{ id: '1', title: '模拟权益', desc: '描述' }] }
      if (type === 'richtext') block.data = { content: '<p>输入富文本内容...</p>' }
      if (type === 'buttonGroup') block.data = { buttons: [this.createDefaultButton({ text: '点击跳转', style: 'primary' })] }
      this.editData.blocks.push(block)
      this.isModified = true
    },
    duplicateBlock(index) {
      const block = this.editData.blocks[index]
      if (block.type === 'privilege') {
        ElMessage.warning('权益商品模块每页仅限配置一个，无法复制！')
        return
      }
      const copy = JSON.parse(JSON.stringify(block))
      copy.id = genBlockId()
      this.editData.blocks.splice(index + 1, 0, copy)
      this.isModified = true
    },
    removeBlock(index) {
      ElMessageBox.confirm('确定删除该模块吗？', '提示', { type: 'warning' }).then(() => {
        this.editData.blocks.splice(index, 1)
        this.isModified = true
      }).catch(() => {})
    },
    moveBlock(from, to) {
      if (to < 0 || to >= this.editData.blocks.length || from === to) return
      const item = this.editData.blocks.splice(from, 1)[0]
      this.editData.blocks.splice(to, 0, item)
      this.isModified = true
    },
    movePrivilegeItem(bIdx, from, to) {
      const items = this.editData.blocks[bIdx].data.items
      if (to < 0 || to >= items.length || from === to) return
      const item = items.splice(from, 1)[0]
      items.splice(to, 0, item)
      this.isModified = true
    },

    // 按钮操作
    addButton(bIdx) {
      const block = this.editData.blocks[bIdx]
      if (!block.data.buttons) block.data.buttons = []
      block.data.buttons.push(this.createDefaultButton())
      this.isModified = true
    },
    handleButtonActionChange(btn) {
      this.normalizeButton(btn)
      this.isModified = true
    },
    handleButtonJumpTypeChange(btn) {
      this.clearInactiveButtonJumpFields(btn)
      this.isModified = true
    },
    handleButtonJumpFieldChange(btn) {
      this.syncButtonLegacyLink(btn)
      this.isModified = true
    },
    handleDialogJumpTypeChange(dialog) {
      this.clearInactiveDialogJumpFields(dialog)
      this.isModified = true
    },
    handleDialogJumpFieldChange(dialog) {
      this.syncDialogLegacyLink(dialog)
      this.isModified = true
    },
    validateButtonJumpConfig(btn) {
      if (this.getPartnerFixedLink(btn.linkType)) return true
      if (btn.linkType === 'h5') return !!(btn.linkH5 || '').trim()
      if (btn.linkType === 'miniProgram') {
        return !!(btn.miniAppId || '').trim() && !!(btn.miniAppPath || '').trim()
      }
      return false
    },
    validateDialogJumpConfig(dialog) {
      if (this.getPartnerFixedLink(dialog.confirmLinkType)) return true
      if (dialog.confirmLinkType === 'h5') return !!(dialog.confirmLinkH5 || '').trim()
      if (dialog.confirmLinkType === 'miniProgram') {
        return !!(dialog.confirmMiniAppId || '').trim() && !!(dialog.confirmMiniAppPath || '').trim()
      }
      return false
    },
    removeButton(bIdx, btnIdx) {
      this.editData.blocks[bIdx].data.buttons.splice(btnIdx, 1)
      this.isModified = true
    },
    moveButton(bIdx, from, to) {
      const buttons = this.editData.blocks[bIdx].data.buttons
      if (to < 0 || to >= buttons.length || from === to) return
      const item = buttons.splice(from, 1)[0]
      buttons.splice(to, 0, item)
      this.isModified = true
    },

    // 图片上传
    triggerBlockUpload(bIdx) {
      const ref = this.$refs['blockUpload' + bIdx]
      if (ref) (Array.isArray(ref) ? ref[0] : ref).click()
    },
    onBlockImageUpload(event, bIdx) {
      const file = event.target.files[0]
      if (!file) return
      const reader = new FileReader()
      reader.onload = (e) => {
        this.editData.blocks[bIdx].data.imageUrl = e.target.result
        this.isModified = true
      }
      reader.readAsDataURL(file)
      event.target.value = ''
    },

    // 链接弹窗
    async showLinkDialog(id) {
      if (!id) {
        ElMessage.warning('请先保存组装页再获取链接！')
        return
      }
      await this.loadLinkOptions()
      this.linkTargetId = id
      const item = this.tableData.find(a => a.id === id) || this.editData
      this.linkTargetPublished = item && item.status === 'published'
      this.linkForm = { campaignId: '', channelId: '' }
      this.linkDialogVisible = true
    },
    copyLink(text) {
      navigator.clipboard.writeText(text).then(() => {
        ElMessage.success('链接已复制到剪贴板')
      })
    }
  }
}
</script>

<style scoped>
.search-section .search-row { margin-bottom: 0; }
.search-section .action-row { margin-top: 15px; }
.button-group { display: inline-block; margin-left: 20px; }

.toolbar {
  display: flex; justify-content: space-between; align-items: center;
  margin-bottom: 16px; padding: 12px 16px;
  background: #fff; border-radius: 4px; box-shadow: 0 1px 4px rgba(0,0,0,.06);
}
.toolbar-left { display: flex; align-items: center; gap: 12px; }
.toolbar-title { margin: 0; font-size: 18px; font-weight: 600; }
.toolbar-right { display: flex; gap: 8px; }

.section-card { margin-bottom: 16px; }
.basic-info { display: flex; align-items: center; gap: 24px; }
.info-item { display: flex; align-items: center; gap: 8px; }
.info-label { font-size: 13px; color: #909399; white-space: nowrap; }

.page-assembly :deep(.el-input:not(.el-textarea)) {
  --el-input-height: 32px;
}
.page-assembly :deep(.el-input:not(.el-textarea) .el-input__wrapper),
.page-assembly :deep(.el-select__wrapper) {
  height: 32px;
  min-height: 32px;
}
.page-assembly :deep(.el-input:not(.el-textarea) .el-input__inner) {
  height: 30px;
  line-height: 30px;
}
.page-assembly :deep(.el-select .el-select__placeholder),
.page-assembly :deep(.el-select .el-select__selected-item) {
  line-height: 30px;
}

.required-star { color: #F56C6C; }
.dialog-desc-editor {
  width: 100%;
  background: #fafafa;
  border: 1px solid #dcdfe6;
  border-radius: 4px;
  overflow: hidden;
}
.dialog-desc-editor .ql-container { height: 100px; font-size: 13px; }
.dialog-desc-editor .ql-toolbar { background: #fff; border: none; border-bottom: 1px solid #ebeef5; }
.share-toggle-checkbox {
  position: absolute;
  right: 0;
  top: 0;
  z-index: 10;
  padding: 6px 8px;
  background: #f5f7fa;
  border-bottom: 1px solid #ebeef5;
  border-left: 1px solid #ebeef5;
  border-radius: 0 0 0 8px;
  margin: 0;
  color: #409EFF;
  font-weight: normal;
  font-size: 13px;
}
.share-card-body {
  display: flex;
  gap: 40px;
  align-items: flex-start;
  padding: 0 4px;
}
.share-thumb-section {
  width: 108px;
  flex-shrink: 0;
}
.share-field-label {
  font-size: 13px;
  color: #303133;
  margin-bottom: 8px;
}
.share-thumb-upload {
  width: 108px;
  height: 108px;
  background: #eef6fc;
  border: 1px solid #dcdfe6;
  border-radius: 4px;
  position: relative;
  overflow: hidden;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
}
.share-thumb-upload:hover .upload-overlay { display: flex; }
.share-thumb-img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.share-thumb-tip {
  margin-top: 8px;
  text-align: center;
  font-size: 12px;
  color: #909399;
}
.share-fields {
  flex: 1;
  min-width: 0;
}

.block-card {
  background: #fff; border-radius: 8px; border: 1px solid #ebeef5;
  margin-bottom: 16px; overflow: hidden; position: relative;
  box-shadow: 0 1px 4px rgba(0,0,0,.04);
}
.block-header {
  background: #f5f7fa; padding: 10px 160px 10px 16px; min-height: 44px;
  border-bottom: 1px solid #ebeef5; font-weight: 600; font-size: 14px;
  display: flex; align-items: center;
}
.block-header i { margin-right: 6px; color: #909399; }
.button-module-header { gap: 14px; }
.button-module-title { display: inline-flex; align-items: center; }
.add-button-btn {
  padding: 7px 10px;
  background: #ecf5ff;
  border-color: #d9ecff;
}
.block-hint { font-size: 12px; color: #c0c4cc; font-weight: normal; margin-left: 8px; }
.block-body { padding: 16px; }
.block-actions {
  position: absolute; right: 0; top: 0; z-index: 10;
  display: flex; gap: 4px; background: #f5f7fa;
  border-bottom: 1px solid #ebeef5; border-left: 1px solid #ebeef5;
  padding: 6px 8px; border-radius: 0 0 0 8px;
}

.image-upload-area { display: flex; align-items: center; gap: 16px; }
.upload-box {
  width: 256px; height: 144px;
  background: #eef6fc; border: 1px solid #dcdfe6; border-radius: 4px;
  position: relative; overflow: hidden; cursor: pointer;
  display: flex; align-items: center; justify-content: center; flex-shrink: 0;
}
.upload-box-wide { width: 360px; height: 160px; }
.upload-box:hover .upload-overlay { display: flex; }
.preview-img { width: 100%; height: 100%; object-fit: cover; }
.upload-placeholder {
  display: flex; flex-direction: column; align-items: center;
  color: #c0c4cc; font-size: 12px;
}
.upload-placeholder i { font-size: 28px; margin-bottom: 4px; }
.upload-overlay {
  display: none; position: absolute; inset: 0;
  background: rgba(0,0,0,.5); color: #fff;
  flex-direction: column; align-items: center; justify-content: center; font-size: 14px;
}
.upload-overlay i { font-size: 24px; margin-bottom: 4px; }
.upload-tip {
  font-size: 12px; color: #909399; line-height: 1.6;
  background: #f5f7fa; padding: 8px 12px; border-radius: 4px; border: 1px solid #ebeef5;
}

.privilege-row {
  display: flex; align-items: center; gap: 12px;
  padding: 10px 12px; border: 1px solid #ebeef5; border-radius: 6px;
  background: #fafafa; margin-bottom: 8px;
}
.privilege-no { flex-shrink: 0; }
.privilege-icon {
  width: 40px; height: 40px; border-radius: 50%;
  border: 1px solid #dcdfe6; background: #fff;
  display: flex; align-items: center; justify-content: center;
  color: #909399; font-size: 18px; flex-shrink: 0;
}
.privilege-info { flex: 1; opacity: .8; }
.privilege-title { font-size: 13px; font-weight: 600; }
.privilege-desc { font-size: 12px; color: #909399; margin-top: 2px; }

.button-config-item {
  position: relative; border: 1px solid #ebeef5; border-radius: 6px;
  padding: 24px 20px 20px; margin-bottom: 12px; background: #fafafa;
}
.btn-index {
  position: absolute; left: 0; top: 0;
  background: #606266; color: #fff; font-size: 10px; font-weight: 700;
  padding: 2px 8px; border-radius: 6px 0 6px 0;
}
.btn-config-body { flex: 1; }
.button-form .el-form-item { margin-bottom: 16px; }
.button-form .el-form-item__label {
  line-height: 20px;
  padding: 0 0 6px;
  color: #303133;
  font-weight: 400;
}
.button-form .el-select { width: 100%; }
.button-form > .el-row { padding-right: 102px; }
.btn-config-actions {
  position: absolute; right: 20px; top: 32px;
  display: flex; gap: 6px;
}
.dialog-config {
  background: #fff; padding: 18px 20px 4px; border: 1px solid #ebeef5; border-radius: 4px;
  margin-top: 4px;
}
.dialog-config-title {
  font-size: 14px; font-weight: 500; color: #303133;
  border-bottom: 1px solid #ebeef5; padding-bottom: 12px; margin-bottom: 14px;
}
.dialog-button-config {
  width: 33.333%;
  min-width: 280px;
  margin-bottom: 16px;
}
.confirm-config-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 22px;
  margin-bottom: 6px;
  color: #303133;
  font-size: 14px;
  line-height: 20px;
}
.confirm-config-header :deep(.el-checkbox) {
  height: 20px;
  margin-right: 0;
}
.jump-config-panel {
  background: #fff;
  padding: 16px 18px 4px;
  border: 1px solid #ebeef5;
  border-radius: 4px;
  margin-top: 4px;
}
.dialog-jump-config {
  margin-top: 2px;
}
.jump-config-title {
  font-size: 13px;
  font-weight: 600;
  color: #303133;
  border-bottom: 1px solid #ebeef5;
  padding-bottom: 10px;
  margin-bottom: 12px;
}
.jump-type-item {
  margin-bottom: 18px;
}
.jump-config-panel :deep(.el-radio) {
  margin-right: 22px;
}

.add-block-bar {
  display: flex; gap: 16px; justify-content: center;
  padding: 20px; border: 2px dashed #dcdfe6; border-radius: 8px;
  background: #fff; margin-bottom: 16px;
}

.empty-tip {
  text-align: center; color: #c0c4cc; padding: 16px;
  border: 1px dashed #dcdfe6; border-radius: 4px;
}

.list-actions {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  width: 100%;
  white-space: nowrap;
}
.list-actions :deep(.el-button) {
  margin-left: 0;
  padding: 0;
}

.link-section { margin-top: 16px; padding: 12px; background: #fafafa; border-radius: 6px; border: 1px solid #ebeef5; }
.link-section + .link-section { margin-top: 12px; }
.link-label { font-weight: 600; margin-bottom: 8px; display: flex; align-items: center; gap: 6px; }
.link-row { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; }
.link-type { font-size: 12px; color: #909399; width: 70px; flex-shrink: 0; }
.dot { width: 8px; height: 8px; border-radius: 50%; display: inline-block; }
.dot-green { background: #67C23A; }
.dot-blue { background: #409EFF; }
</style>
