# 一键换装集成指南

本文档面向**接入众趣 zqsdk 的开发者**，说明如何在自己的项目中实现「一键换装」功能。按步骤完成能力判断、模式切换与 UI 搭建即可完成集成。

---

## 一、功能简介与前置条件

### 1.1 什么是一键换装

一键换装是众趣 SDK 提供的「装修前后对比」能力：用户可以在全景模式下查看同一空间的装修前后效果，支持分屏对比和全屏切换两种展示方式。你的项目需要：

- 在合适的时机判断**当前模型是否支持换装**；
- **调用 SDK 方法**切换换装展示模式；
- 提供**换装入口、模式切换、退出**等 UI（样式可完全自定义）。

### 1.2 展示模式说明

| 模式 | 常量值 | 说明 |
|------|--------|------|
| 普通模式 | `normal` | 默认状态，显示「一键换装」入口按钮 |
| 分屏模式 | `split` | 左右分屏对比，左侧装修前，右侧装修后 |
| 全屏模式 | `leftmain` | 全屏展示装修后效果 |

### 1.3 效果展示
![分屏换装展示效果](../../screenshot/pano-dress/分屏换装.png)
![全屏换装展示效果](../../screenshot/pano-dress/全屏换装.png)

### 1.4 前置条件

- 已接入众趣 zqsdk，并在 `zqsdk.init` 的 `complete` 回调中拿到 `window.housePlay`（或等价实例）。
- 模型在众趣后台已配置换装数据；支持换装的模型会在初始化时提供 `has_decoration` 标识和 `panoDress` 数据。

---

## 二、集成步骤概览

| 步骤 | 内容 |
|------|------|
| 1 | 在 SDK 初始化完成后判断是否支持换装 |
| 2 | 维护换装相关状态（当前模式、是否有换装数据） |
| 3 | 在用户操作时调用 `changeShowMode` 切换模式 |
| 4 | 实现换装入口、模式切换按钮等 UI |

以下按步骤展开。

---

## 三、第一步：判断模型是否支持换装

**时机**：在 `zqsdk.init` 的 `complete` 回调中，`housePlay.start()` 之前或之后均可。

**做法**：从 `housePlay.version.has_decoration` 判断是否支持换装，换装数据存储在 `housePlay.settings.panoDress`。

```javascript
zqsdk.init({
  modelId: 'your-model-id',
  complete: function() {
    const housePlay = window.housePlay;

    // 判断是否支持换装
    const hasDecoration = housePlay.version && housePlay.version.has_decoration;

    if (hasDecoration) {
      // 在你的应用里：显示「一键换装」入口
      yourApp.setHasPanoDressData(true);
      yourApp.setPanoDressData(housePlay.settings.panoDress);
      yourApp.setCurrentShowMode('normal');
    }

    housePlay.start();
  }
});
```

---

## 四、第二步：准备换装状态

建议在你自己项目中维护至少以下状态（名称可自定）：

| 状态 | 含义 | 建议更新方式 |
|------|------|--------------|
| 是否有换装数据 | 当前模型是否支持换装 | 仅在 SDK 初始化完成时根据 `has_decoration` 设置 |
| 当前显示模式 | `normal` / `split` / `leftmain` | 调用 `changeShowMode` 后更新 |
| 换装数据 | 换装配置信息 | 从 `housePlay.settings.panoDress` 获取 |

**显示模式常量定义**：

```javascript
const VR_SHOW_MODE = {
  NORMAL: 'normal',     // 普通模式
  SPLIT: 'split',       // 分屏换装模式
  LEFT_MAIN: 'leftmain' // 全屏换装模式
};
```

---

## 五、第三步：调用 SDK 方法切换模式

SDK 提供 `changeShowMode` 方法用于切换换装展示模式：

| 方法 | 说明 | 参数 |
|------|------|------|
| `housePlay.changeShowMode(mode)` | 切换换装展示模式 | `'normal'` / `'split'` / `'leftmain'` |

**调用示例**：

```javascript
// 进入分屏换装模式
function enterDecoration() {
  // 仅在全景模式下可用
  if (yourApp.currentMode !== 'panorama') {
    return;
  }
  housePlay.changeShowMode('split');
  yourApp.setCurrentShowMode('split');
}

// 退出换装模式
function exitDecoration() {
  housePlay.changeShowMode('normal');
  yourApp.setCurrentShowMode('normal');
}

// 切换到全屏换装模式
function fullScreenDecoration() {
  housePlay.changeShowMode('leftmain');
  yourApp.setCurrentShowMode('leftmain');
}

// 切换到分屏换装模式
function splitDecoration() {
  housePlay.changeShowMode('split');
  yourApp.setCurrentShowMode('split');
}
```

---

## 六、第四步：实现换装 UI

UI 完全由你自定义，只需满足功能对应关系即可。

### 6.1 建议具备的 UI 元素

1. **换装入口按钮**
   - 显示条件：SDK 已就绪 且 当前模型有换装数据 且 当前为普通模式 且 当前为全景模式
   - 点击：调用 `changeShowMode('split')` 进入分屏换装模式

2. **分屏模式控制按钮**
   - 显示条件：当前为分屏模式（`split`）
   - 包含：
     - **退出按钮**（左上角）：调用 `changeShowMode('normal')` 退出换装
     - **全屏按钮**（右侧面板顶部）：调用 `changeShowMode('leftmain')` 切换到全屏模式

3. **全屏模式控制按钮**
   - 显示条件：当前为全屏模式（`leftmain`）
   - 包含：
     - **分屏按钮**（左上角）：调用 `changeShowMode('split')` 切换回分屏模式

### 6.2 按钮位置建议

| 模式 | 按钮 | 位置 |
|------|------|------|
| 普通模式 | 一键换装入口 | 右侧面板上方 |
| 分屏模式 | 退出按钮 | 左侧面板左上角 |
| 分屏模式 | 全屏按钮 | 右侧面板顶部中间 |
| 全屏模式 | 分屏按钮 | 左上角 |

### 6.3 显示条件逻辑

```javascript
// 是否显示换装入口按钮
function showEntryButton() {
  return yourApp.hasPanoDressData &&
         yourApp.currentShowMode === 'normal' &&
         yourApp.currentMode === 'panorama' &&
         yourApp.sdkStatus === 'ready';
}

// 是否显示分屏模式控制按钮
function showSplitControls() {
  return yourApp.currentShowMode === 'split' &&
         yourApp.sdkStatus === 'ready';
}

// 是否显示全屏模式控制按钮
function showFullScreenControls() {
  return yourApp.currentShowMode === 'leftmain' &&
         yourApp.sdkStatus === 'ready';
}
```

---

## 七、API 速查表

### 7.1 方法（housePlay）

| 方法 | 说明 | 参数 |
|------|------|------|
| `housePlay.changeShowMode(mode)` | 切换换装展示模式 | `'normal'` \| `'split'` \| `'leftmain'` |

### 7.2 判断换装是否可用

- 在 SDK 初始化完成后读取：`housePlay.version.has_decoration`，为 `true` 即表示支持换装。
- 换装数据：`housePlay.settings.panoDress`

### 7.3 显示模式常量

| 常量 | 值 | 说明 |
|------|-----|------|
| `NORMAL` | `'normal'` | 普通模式，显示换装入口 |
| `SPLIT` | `'split'` | 分屏模式，左右对比 |
| `LEFT_MAIN` | `'leftmain'` | 全屏模式，展示装修后效果 |

---

## 八、常见问题与注意事项

1. **换装仅在全景模式下可用**
   在进入换装模式前，应检查当前是否为全景模式（`panorama`）。在模型视图（`dollhouse`）或户型图视图（`floorplan`）下调用 `changeShowMode` 可能不会生效。

2. **模式切换后同步更新本地状态**
   调用 `changeShowMode` 后，应同步更新你本地维护的 `currentShowMode` 状态，以保证 UI 显示正确。

3. **与其他功能的互斥**
   进入换装模式时，建议暂停或隐藏其他功能（如导览、讲房），避免功能冲突。可在进入换装前调用：
   ```javascript
   housePlay.pause();        // 暂停导览
   housePlay.pauseLecture(); // 暂停讲房
   ```

4. **方法是否存在**
   部分环境或旧版 SDK 可能未暴露 `changeShowMode`，调用前可用 `housePlay.changeShowMode != null` 做兼容检查。

---

## 九、本仓库实现参考

若需参考本仓库的完整实现（Vue 3 + Pinia + TypeScript），可参阅：

| 文件 | 说明 |
|------|------|
| `src/stores/vr.ts` | VR 状态管理，包含换装状态（`hasPanoDressData`、`currentShowMode`）和 `changeShowMode` 方法封装 |
| `src/config/constants.ts` | 显示模式常量定义（`VR_SHOW_MODE`） |
| `src/modules/pc/pano-dress/NestPanoDress.vue` | 换装控制组件，包含入口按钮和模式切换按钮 |
| `src/components/common/pc/NestCircleButton.vue` | 圆形按钮组件（用于换装入口） |
| `src/components/common/pc/NestRectButton.vue` | 矩形按钮组件（用于模式切换） |
| `src/layouts/pc/PcHouseLayout.vue` | PC 端布局（换装组件挂载点） |

### 核心代码示例

**状态管理（vr.ts）**：

```typescript
// 换装状态
const hasPanoDressData = ref(false)
const panoDressData = ref<unknown | null>(null)
const currentShowMode = ref<VrShowMode>('normal')

// SDK 初始化时检查换装数据
if (housePlayInstance?.version?.has_decoration) {
  hasPanoDressData.value = true
  panoDressData.value = housePlayInstance?.settings?.panoDress
  currentShowMode.value = 'normal'
}

// 切换显示模式
function changeShowMode(showMode: VrShowMode): void {
  housePlayInstance?.changeShowMode(showMode)
  currentShowMode.value = showMode
}
```

**换装组件（NestPanoDress.vue）**：

```vue
<template>
  <!-- 入口按钮：普通模式 + 全景模式 + 有换装数据 -->
  <div v-if="showEntryButton" class="pano-dress-entry">
    <NestCircleButton
      :icon="decorationIcon"
      :label="t('decoration')"
      @click="handleEnterDecoration"
    />
  </div>

  <!-- 分屏模式：退出按钮 -->
  <div v-if="currentShowMode === 'split'" class="pano-dress-exit">
    <NestRectButton
      :icon="exitIcon"
      :label="t('exitDecoration')"
      @click="handleExitDecoration"
    />
  </div>

  <!-- 分屏模式：全屏按钮 -->
  <div v-if="currentShowMode === 'split'" class="pano-dress-fullscreen">
    <NestRectButton
      :icon="fullScreenIcon"
      :label="t('fullScreenDecoration')"
      @click="handleFullScreenDecoration"
    />
  </div>

  <!-- 全屏模式：分屏按钮 -->
  <div v-if="currentShowMode === 'leftmain'" class="pano-dress-split">
    <NestRectButton
      :icon="splitIcon"
      :label="t('splitDecoration')"
      @click="handleSplitDecoration"
    />
  </div>
</template>
```

## 十、离线数据包资源
如果您在业务上使用的是众趣模型离线数据包的场景时， 当触发换装流程异步任务后， 任务完成后，众趣会给您的服务器回调【换装数据相关资源】。 此时您需要将拿到的数据包解压，并进行数据替换。

替换目录如下：

#### 模型id目录
1. version.txt （单个文件）

#### 模型版本目录
1. settings.txt （单个文件）
2. settings_local.txt （单个文件）
3. panoDress.txt （单个文件）
4. {{换装id目录}} （整个目录）


#### 截图示意
模型id目录：
![模型id目录版本文件截图示意](../../screenshot/pano-dress/换装离线数据包模型版本文件.png)

模型版本目录：
![模型版本目录资源替换截图示意](../../screenshot/pano-dress/换装离线数据包-模型版本目录资源替换.png)