From 08e9a67dd679f311e79a27b04cd0c53a30b4bccf Mon Sep 17 00:00:00 2001
From: doum <doum>
Date: 星期四, 04 六月 2026 18:33:22 +0800
Subject: [PATCH] aaa

---
 h5/components/search.vue                                                                     |  742 +++-
 server/db/upgrade_anchor_v3.sql                                                              |   65 
 company_admin/src/assets/images/submit_btn_defult.png                                        |    0 
 server/zhubo/src/main/java/com/doumee/api/business/WebParamController.java                   |    6 
 server/service/src/main/java/com/doumee/dao/business/model/WebParam.java                     |    8 
 h5/apis/index.js                                                                             |   22 
 server/zhubo/src/main/java/com/doumee/api/business/H5Controller.java                         |   30 
 server/service/src/main/java/com/doumee/service/business/impl/CategoryServiceImpl.java       |  125 
 company_admin/src/components/business/FeaturedGoodsPicker.vue                                |  190 +
 h5/pages/index_2/index.vue                                                                   |   15 
 company_admin/src/api/business/category.js                                                   |   20 
 company_admin/src/views/business/pageConfiguration.vue                                       |  182 +
 server/service/src/main/java/com/doumee/service/business/PreselectOrderService.java          |   23 
 h5/utils/anchorHome.js                                                                       |   16 
 h5/static/sounds/hover.mp3                                                                   |    0 
 company_admin/src/components/business/OperaSubCategoryWindow.vue                             |  116 
 h5/pages.json                                                                                |    8 
 company_admin/src/views/system/user.vue                                                      |    2 
 h5/components/bigImg.vue                                                                     |  129 
 server/company/src/main/java/com/doumee/api/business/WebParamController.java                 |   20 
 h5/pages/login/login.vue                                                                     |    8 
 server/service/src/main/java/com/doumee/dao/business/PreselectOrderSeqMapper.java            |    9 
 company_admin/src/api/business/order.js                                                      |   17 
 h5/static/ic_submit_order@2x.png                                                             |    0 
 server/company/src/main/java/com/doumee/api/business/CategoryController.java                 |   37 
 server/service/src/main/java/com/doumee/service/business/H5InitService.java                  |   11 
 company_admin/src/views/system/position.vue                                                  |    2 
 server/company/src/main/java/com/doumee/api/business/PreselectOrderController.java           |   53 
 company_admin/src/api/business/page.js                                                       |   14 
 server/service/src/main/java/com/doumee/dao/business/model/dto/H5InitDataDTO.java            |   30 
 company_admin/src/views/system/data-permission.vue                                           |    2 
 company_admin/src/views/system/department.vue                                                |    2 
 company_admin/src/views/system/menu.vue                                                      |    2 
 h5/pages/index_3/index.vue                                                                   | 3101 +++++++++++++++++++
 server/service/src/main/java/com/doumee/dao/business/model/dto/PreselectOrderItemDTO.java    |   25 
 company_admin/src/assets/style/style.scss                                                    |   34 
 server/service/src/main/java/com/doumee/service/business/WebParamService.java                |    6 
 company_admin/src/assets/images/submit_btn_defult2.png                                       |    0 
 company_admin/src/views/business/goods.vue                                                   |    2 
 server/service/src/main/java/com/doumee/dao/business/model/Goods.java                        |    8 
 server/service/src/main/java/com/doumee/service/business/CategoryService.java                |   10 
 server/service/src/main/java/com/doumee/dao/business/PreselectOrderMapper.java               |    9 
 company_admin/src/views/business/order.vue                                                   |  463 ++
 server/service/src/main/java/com/doumee/dao/business/model/PreselectOrderSeq.java            |   17 
 h5/static/sounds/click.mp3                                                                   |    0 
 server/service/src/main/java/com/doumee/dao/business/PreselectOrderItemMapper.java           |    9 
 h5/utils/request.js                                                                          |    4 
 server/zhubo/src/main/java/com/doumee/api/business/PreselectOrderController.java             |   34 
 company_admin/src/views/system/permission.vue                                                |    2 
 company_admin/src/plugins/messagebox.js                                                      |    3 
 h5/utils/sound.js                                                                            |   87 
 h5/utils/goodsFilter.js                                                                      |   43 
 server/service/src/main/java/com/doumee/dao/system/dto/UpdateWebParamDTO.java                |    6 
 server/service/src/main/java/com/doumee/service/business/impl/H5InitServiceImpl.java         |   51 
 server/db/business.menu.anchor_v3.sql                                                        |    3 
 server/db/business.order.permissions.sql                                                     |    5 
 server/service/src/main/java/com/doumee/dao/business/model/PreselectOrderItem.java           |   30 
 server/service/src/main/java/com/doumee/service/business/impl/PreselectOrderServiceImpl.java |  335 ++
 server/service/src/main/java/com/doumee/service/business/impl/WebParamServiceImpl.java       |  106 
 company_admin/src/assets/style/variables.scss                                                |    4 
 server/service/src/main/java/com/doumee/dao/business/model/PreselectOrder.java               |   65 
 server/service/src/main/java/com/doumee/dao/business/model/dto/GoodsRequest.java             |    3 
 company_admin/src/components/business/OperaGoodsWindow.vue                                   |   37 
 company_admin/src/views/business/pageConfigurationNew.vue                                    | 2912 +++++++++++++++++
 server/service/src/main/java/com/doumee/service/business/impl/GoodsServiceImpl.java          |   16 
 company_admin/src/views/business/category.vue                                                |  238 
 company_admin/src/views/system/role.vue                                                      |    2 
 server/service/src/main/java/com/doumee/dao/business/model/dto/SubmitPreselectOrderDTO.java  |   26 
 company_admin/src/views/system/dict.vue                                                      |    2 
 server/db/business.anchor_page_version.sql                                                   |    3 
 server/db/business.order.fix_create_time.sql                                                 |    6 
 company_admin/src/components/system/dict/DictDataManagerWindow.vue                           |    2 
 h5/App.vue                                                                                   |   51 
 server/service/src/main/java/com/doumee/dao/business/model/Category.java                     |    8 
 74 files changed, 9,254 insertions(+), 420 deletions(-)

diff --git a/company_admin/src/api/business/category.js b/company_admin/src/api/business/category.js
index 91a328e..7d90de1 100644
--- a/company_admin/src/api/business/category.js
+++ b/company_admin/src/api/business/category.js
@@ -58,4 +58,24 @@
 // 琛� - H5
 export function list (data) {
   return request.post('/business/category/list', data)
+}
+
+export function fetchTree (data) {
+  return request.post('/business/category/tree', data || {})
+}
+
+export function fetchChildren (parentId) {
+  return request.get(`/business/category/children/${parentId}`)
+}
+
+export function createSub (data) {
+  return request.post('/business/category/createSub', data)
+}
+
+export function updateSub (data) {
+  return request.post('/business/category/updateSub', data)
+}
+
+export function deleteSub (id) {
+  return request.get(`/business/category/deleteSub/${id}`)
 }
\ No newline at end of file
diff --git a/company_admin/src/api/business/order.js b/company_admin/src/api/business/order.js
new file mode 100644
index 0000000..133cca1
--- /dev/null
+++ b/company_admin/src/api/business/order.js
@@ -0,0 +1,17 @@
+import request from '../../utils/request'
+
+export function fetchList (data) {
+  return request.post('/business/order/page', data, { trim: true })
+}
+
+export function queryById (id) {
+  return request.get(`/business/order/${id}`)
+}
+
+export function deleteById (id) {
+  return request.get(`/business/order/delete/${id}`)
+}
+
+export function exportDetail (id) {
+  return request.get(`/business/order/exportDetail/${id}`, { download: true })
+}
diff --git a/company_admin/src/api/business/page.js b/company_admin/src/api/business/page.js
index c08b7de..199c989 100644
--- a/company_admin/src/api/business/page.js
+++ b/company_admin/src/api/business/page.js
@@ -17,4 +17,16 @@
 // 鏇存柊浼佷笟閰嶇疆淇℃伅
 export function renewUpdate (data) {
     return request.post('/business/webParam/renewUpdate', data)
-  }聽
\ No newline at end of file
+  }
+
+export function getByLoginAnchor () {
+  return request.get('/business/webParam/getByLoginAnchor')
+}
+
+export function renewAnchorUpdate (data) {
+  return request.post('/business/webParam/renewAnchorUpdate', data)
+}
+
+export function updateAnchorPageVersion (data) {
+  return request.post('/business/webParam/updateAnchorPageVersion', data)
+}
\ No newline at end of file
diff --git a/company_admin/src/assets/images/submit_btn_defult.png b/company_admin/src/assets/images/submit_btn_defult.png
new file mode 100644
index 0000000..687067c
--- /dev/null
+++ b/company_admin/src/assets/images/submit_btn_defult.png
Binary files differ
diff --git a/company_admin/src/assets/images/submit_btn_defult2.png b/company_admin/src/assets/images/submit_btn_defult2.png
new file mode 100644
index 0000000..c4a0adf
--- /dev/null
+++ b/company_admin/src/assets/images/submit_btn_defult2.png
Binary files differ
diff --git a/company_admin/src/assets/style/style.scss b/company_admin/src/assets/style/style.scss
index 114ca0e..65cfec3 100644
--- a/company_admin/src/assets/style/style.scss
+++ b/company_admin/src/assets/style/style.scss
@@ -30,3 +30,37 @@
 .el-transfer__buttons {
   padding: 0 16px !important;
 }
+
+// 鍒犻櫎鎸夐挳缁熶竴鏍峰紡
+.el-button.btn-delete.el-button--text,
+.el-button.el-button--text:has(.el-icon-delete) {
+  color: $danger-color;
+
+  &:hover,
+  &:focus {
+    color: $danger-color-hover;
+  }
+
+  &:active {
+    color: $danger-color-active;
+  }
+}
+
+.el-button:not(.el-button--text):not(.el-button--danger):has(.el-icon-delete) {
+  color: #fff;
+  background-color: $danger-color;
+  border-color: $danger-color;
+
+  &:hover,
+  &:focus {
+    color: #fff;
+    background-color: $danger-color-hover;
+    border-color: $danger-color-hover;
+  }
+
+  &:active {
+    color: #fff;
+    background-color: $danger-color-active;
+    border-color: $danger-color-active;
+  }
+}
diff --git a/company_admin/src/assets/style/variables.scss b/company_admin/src/assets/style/variables.scss
index ff3f939..9042dea 100644
--- a/company_admin/src/assets/style/variables.scss
+++ b/company_admin/src/assets/style/variables.scss
@@ -1,5 +1,9 @@
 // 涓昏壊璋�
 $primary-color: #2E68EC;
+// 鍗遍櫓/鍒犻櫎鑹�
+$danger-color: #F56C6C;
+$danger-color-hover: #f78989;
+$danger-color-active: #dd6161;
 // 澶撮儴楂樺害
 $header-height: 60px;
 // 鑿滃崟瀹藉害
diff --git a/company_admin/src/components/business/FeaturedGoodsPicker.vue b/company_admin/src/components/business/FeaturedGoodsPicker.vue
new file mode 100644
index 0000000..53d71b9
--- /dev/null
+++ b/company_admin/src/components/business/FeaturedGoodsPicker.vue
@@ -0,0 +1,190 @@
+<template>
+  <GlobalWindow
+    title="浠庝紒涓氬晢鍝佸簱閫夋嫨"
+    width="70%"
+    :visible.sync="visible"
+    :confirm-working="isWorking"
+    @confirm="confirm">
+    <el-form :inline="true" :model="searchForm" class="search-form">
+      <el-form-item label="鍟嗗搧鍚嶇О">
+        <el-input v-model="searchForm.name" placeholder="璇疯緭鍏ュ晢鍝佸悕绉�" clearable @keyup.enter.native="search" />
+      </el-form-item>
+      <el-form-item label="绫诲埆">
+        <el-select v-model="searchForm.categoryId" clearable placeholder="鍏ㄩ儴">
+          <el-option
+            v-for="item in categoryList"
+            :key="item.id"
+            :label="item.name"
+            :value="item.id" />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="鍝佺墝">
+        <el-select v-model="searchForm.brandId" clearable placeholder="鍏ㄩ儴">
+          <el-option
+            v-for="item in brandList"
+            :key="item.id"
+            :label="item.name"
+            :value="item.id" />
+        </el-select>
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" @click="search">鏌ヨ</el-button>
+      </el-form-item>
+    </el-form>
+    <el-table
+      v-loading="isWorking"
+      :data="tableData"
+      stripe
+      border
+      highlight-current-row
+      @current-change="handleCurrentChange">
+      <el-table-column label="" width="55">
+        <template slot-scope="{ row }">
+          <el-radio v-model="selectedId" :label="row.id">&nbsp;</el-radio>
+        </template>
+      </el-table-column>
+      <el-table-column prop="id" label="鍟嗗搧ID" min-width="90" />
+      <el-table-column prop="name" label="鍟嗗搧鍚嶇О/鍨嬪彿" min-width="220" show-overflow-tooltip>
+        <template slot-scope="{ row }">
+          <div class="goods-cell">
+            <el-image
+              v-if="row.imgurl"
+              class="goods-thumb"
+              :src="row.prefixUrl + row.imgurl"
+              fit="cover" />
+            <span>{{ row.name }}</span>
+          </div>
+        </template>
+      </el-table-column>
+      <el-table-column prop="brandName" label="鍝佺墝" min-width="100" show-overflow-tooltip />
+      <el-table-column prop="categoryName" label="绫诲埆" min-width="100" show-overflow-tooltip />
+      <el-table-column label="鎸囧浠凤紙鍏冿級" min-width="110">
+        <template slot-scope="{ row }">
+          {{ row.type === 0 ? row.zdPrice : row.baseZdPrice }}
+        </template>
+      </el-table-column>
+    </el-table>
+    <el-pagination
+      class="pager"
+      @size-change="handleSizeChange"
+      @current-change="handlePageChange"
+      :current-page="page"
+      :page-sizes="[10, 20, 30]"
+      :page-size="pageSize"
+      layout="total, sizes, prev, pager, next, jumper"
+      :total="total" />
+  </GlobalWindow>
+</template>
+
+<script>
+import GlobalWindow from '@/components/common/GlobalWindow'
+import { fetchList } from '@/api/business/goods'
+import { brand, category } from '@/api/system/common.js'
+
+export default {
+  name: 'FeaturedGoodsPicker',
+  components: { GlobalWindow },
+  data () {
+    return {
+      visible: false,
+      isWorking: false,
+      searchForm: {
+        name: '',
+        categoryId: '',
+        brandId: '',
+        status: 0
+      },
+      categoryList: [],
+      brandList: [],
+      tableData: [],
+      page: 1,
+      pageSize: 10,
+      total: 0,
+      selectedId: '',
+      selectedRow: null
+    }
+  },
+  methods: {
+    open () {
+      this.visible = true
+      this.selectedId = ''
+      this.selectedRow = null
+      this.page = 1
+      this.searchForm.name = ''
+      this.searchForm.categoryId = ''
+      this.searchForm.brandId = ''
+      this.loadOptions()
+      this.getList()
+    },
+    loadOptions () {
+      if (!this.categoryList.length) {
+        category().then(res => { this.categoryList = res || [] })
+      }
+      if (!this.brandList.length) {
+        brand().then(res => { this.brandList = res || [] })
+      }
+    },
+    search () {
+      this.page = 1
+      this.getList()
+    },
+    getList () {
+      this.isWorking = true
+      fetchList({
+        page: this.page,
+        capacity: this.pageSize,
+        model: { ...this.searchForm }
+      }).then(res => {
+        this.tableData = res.records || []
+        this.total = res.total || 0
+      }).catch(e => {
+        this.$tip.apiFailed(e)
+      }).finally(() => {
+        this.isWorking = false
+      })
+    },
+    handleSizeChange (size) {
+      this.pageSize = size
+      this.page = 1
+      this.getList()
+    },
+    handlePageChange (page) {
+      this.page = page
+      this.getList()
+    },
+    handleCurrentChange (row) {
+      if (!row) return
+      this.selectedId = row.id
+      this.selectedRow = row
+    },
+    confirm () {
+      if (!this.selectedRow) {
+        this.$message.warning('璇烽�夋嫨鍟嗗搧')
+        return
+      }
+      this.$emit('select', { ...this.selectedRow })
+      this.visible = false
+    }
+  }
+}
+</script>
+
+<style scoped lang="scss">
+.search-form {
+  margin-bottom: 12px;
+}
+.goods-cell {
+  display: flex;
+  align-items: center;
+  gap: 8px;
+}
+.goods-thumb {
+  width: 56px;
+  height: 56px;
+  flex-shrink: 0;
+}
+.pager {
+  margin-top: 12px;
+  text-align: right;
+}
+</style>
diff --git a/company_admin/src/components/business/OperaGoodsWindow.vue b/company_admin/src/components/business/OperaGoodsWindow.vue
index cd62c60..7ef2c34 100644
--- a/company_admin/src/components/business/OperaGoodsWindow.vue
+++ b/company_admin/src/components/business/OperaGoodsWindow.vue
@@ -21,10 +21,19 @@
           </el-select>
         </el-form-item>
         <el-form-item label="鍟嗗搧绫诲埆" prop="categoryId">
-          <!-- @change="changeCategory(form.categoryId)" -->
-          <el-select v-model="form.categoryId" filterable placeholder="璇烽�夋嫨锛屽崟閫�">
+          <el-select v-model="form.categoryId" filterable placeholder="璇烽�夋嫨涓�绾х被鍒�" @change="onCategoryChange">
             <el-option
               v-for="item in categoryList"
+              :key="item.id"
+              :label="item.name"
+              :value="item.id">
+            </el-option>
+          </el-select>
+        </el-form-item>
+        <el-form-item label="浜岀骇绫诲埆">
+          <el-select v-model="form.subCategoryId" clearable filterable placeholder="鍙�夛紝璇烽�夋嫨浜岀骇绫诲埆">
+            <el-option
+              v-for="item in subCategoryList"
               :key="item.id"
               :label="item.name"
               :value="item.id">
@@ -99,6 +108,7 @@
   import GlobalWindow from '@/components/common/GlobalWindow'
   import { brand } from '@/api/system/common.js'
   import { findListForGoodsId, create, updateById, companyCreate, companyUpdateById } from '@/api/business/goods.js'
+  import { fetchChildren } from '@/api/business/category.js'
   export default {
     name: 'OperaGoodsWindow',
     extends: BaseOpera,
@@ -113,6 +123,7 @@
           id: null,
           name: '',
           categoryId: '',
+          subCategoryId: '',
           brandId: '',
           zdPrice: '',
           price: '',
@@ -148,6 +159,7 @@
         },
         options: [],
         categoryList: [],
+        subCategoryList: [],
         brandList: []
       }
     },
@@ -328,18 +340,31 @@
               this.brandList = res
             })
         },
+        onCategoryChange (val) {
+          this.form.subCategoryId = ''
+          this.subCategoryList = []
+          if (val) {
+            fetchChildren(val).then(res => {
+              this.subCategoryList = res || []
+            })
+          }
+        },
         getcategory(type) {
           findListForGoodsId(this.form.id || '')
             .then(res => {
-              this.categoryList = res
+              this.categoryList = (res || []).filter(item => !item.parentId)
+              if (this.form.categoryId) {
+                fetchChildren(this.form.categoryId).then(list => {
+                  this.subCategoryList = list || []
+                })
+              }
               if (type === 1) {
                 this.categoryList.forEach(item => {
                   if (item.id === this.form.categoryId) {
                     this.form.goodsParamList = JSON.parse(JSON.stringify(item.paramList))
-                    this.form.goodsParamList.forEach(item => {
-                      item.pramaId = item.id
+                    this.form.goodsParamList.forEach(p => {
+                      p.pramaId = p.id
                     })
-                    console.log(this.form.goodsParamList)
                   }
                 })
               }
diff --git a/company_admin/src/components/business/OperaSubCategoryWindow.vue b/company_admin/src/components/business/OperaSubCategoryWindow.vue
new file mode 100644
index 0000000..9c5c1d7
--- /dev/null
+++ b/company_admin/src/components/business/OperaSubCategoryWindow.vue
@@ -0,0 +1,116 @@
+<template>
+  <GlobalWindow
+    :title="title"
+    :visible.sync="visible"
+    :confirm-working="isWorking"
+    @confirm="confirm"
+  >
+    <el-form :model="form" ref="form" :rules="rules" label-width="100px">
+      <el-form-item label="瀛愮被鍒悕绉�" prop="name">
+        <el-input v-model="form.name" maxlength="30" placeholder="璇疯緭鍏ュ瓙绫诲埆鍚嶇О" v-trim/>
+      </el-form-item>
+      <el-form-item label="鎺掑簭鐮�" prop="sortnum">
+        <el-input v-model="form.sortnum" type="number" placeholder="鍗囧簭鎺掑垪" v-trim/>
+      </el-form-item>
+      <el-form-item label="鍥炬爣">
+        <el-upload
+          :action="action"
+          :file-list="form.fileList"
+          :data="{ folder: 'category_img' }"
+          list-type="picture-card"
+          :limit="1"
+          :on-success="fileSuccess"
+          :on-remove="handleRemove">
+          <i class="el-icon-plus"></i>
+        </el-upload>
+      </el-form-item>
+      <el-form-item label="鐘舵��" prop="status">
+        <el-switch v-model="form.status" :active-value="0" :inactive-value="1" />
+      </el-form-item>
+    </el-form>
+  </GlobalWindow>
+</template>
+
+<script>
+import GlobalWindow from '@/components/common/GlobalWindow'
+import { createSub, updateSub } from '@/api/business/category'
+
+export default {
+  name: 'OperaSubCategoryWindow',
+  components: { GlobalWindow },
+  data () {
+    return {
+      title: '',
+      visible: false,
+      isWorking: false,
+      action: process.env.VUE_APP_API_PREFIX + '/public/upload',
+      form: {
+        id: null,
+        parentId: null,
+        name: '',
+        sortnum: 1,
+        imgurl: '',
+        status: 0,
+        fileList: []
+      },
+      rules: {
+        name: [{ required: true, message: '涓嶈兘涓虹┖', trigger: 'blur' }]
+      }
+    }
+  },
+  methods: {
+    open (title, parentRow, editRow) {
+      this.title = title
+      this.visible = true
+      if (editRow) {
+        const previewUrl = editRow.imgurl && editRow.prefixUrl
+          ? editRow.prefixUrl + editRow.imgurl
+          : (editRow.imgurl && editRow.imgurl.indexOf('http') === 0 ? editRow.imgurl : '')
+        this.form = {
+          id: editRow.id,
+          parentId: editRow.parentId,
+          name: editRow.name,
+          sortnum: editRow.sortnum || 1,
+          imgurl: editRow.imgurl || '',
+          status: editRow.status != null ? editRow.status : 0,
+          fileList: previewUrl ? [{ url: previewUrl, name: editRow.imgurl }] : []
+        }
+      } else {
+        this.form = {
+          id: null,
+          parentId: parentRow.id,
+          name: '',
+          sortnum: 1,
+          imgurl: '',
+          status: 0,
+          fileList: []
+        }
+      }
+    },
+    fileSuccess (response) {
+      if (response && response.data) {
+        this.form.imgurl = response.data.imgaddr
+        this.form.fileList = [{ url: response.data.url, name: response.data.imgname || response.data.imgaddr }]
+      }
+    },
+    handleRemove () {
+      this.form.imgurl = ''
+      this.form.fileList = []
+    },
+    confirm () {
+      this.$refs.form.validate(valid => {
+        if (!valid) return
+        this.isWorking = true
+        const api = this.form.id ? updateSub : createSub
+        api(this.form).then(() => {
+          this.$message.success('鎿嶄綔鎴愬姛')
+          this.visible = false
+          this.$emit('success')
+        }).finally(() => {
+          this.isWorking = false
+        })
+      })
+    }
+  }
+}
+</script>
diff --git a/company_admin/src/components/system/dict/DictDataManagerWindow.vue b/company_admin/src/components/system/dict/DictDataManagerWindow.vue
index bbad831..009cdb7 100644
--- a/company_admin/src/components/system/dict/DictDataManagerWindow.vue
+++ b/company_admin/src/components/system/dict/DictDataManagerWindow.vue
@@ -10,7 +10,7 @@
       <template v-slot:table-wrap>
         <ul class="toolbar">
           <li><el-button type="primary" @click="$refs.operaDictDataWindow.open('鏂板缓瀛楀吀鏁版嵁', searchForm.dictId)" icon="el-icon-plus">鏂板缓</el-button></li>
-          <li><el-button @click="deleteByIdInBatch" icon="el-icon-delete">鍒犻櫎</el-button></li>
+          <li><el-button type="danger" @click="deleteByIdInBatch" icon="el-icon-delete">鍒犻櫎</el-button></li>
         </ul>
         <el-table
           v-loading="isWorking.search"
diff --git a/company_admin/src/plugins/messagebox.js b/company_admin/src/plugins/messagebox.js
index 5d3d2ec..132e51c 100644
--- a/company_admin/src/plugins/messagebox.js
+++ b/company_admin/src/plugins/messagebox.js
@@ -7,7 +7,8 @@
     return MessageBox.confirm(message, '鍒犻櫎鎻愰啋', {
       confirmButtonText: '纭鍒犻櫎',
       cancelButtonText: '鍙栨秷',
-      type: 'warning'
+      type: 'warning',
+      confirmButtonClass: 'el-button--danger'
     })
   },
   // 绂佺敤浜屾纭
diff --git a/company_admin/src/views/business/category.vue b/company_admin/src/views/business/category.vue
index 4c654a9..0971b55 100644
--- a/company_admin/src/views/business/category.vue
+++ b/company_admin/src/views/business/category.vue
@@ -1,169 +1,153 @@
 <template>
   <TableLayout :permissions="['business:category:query']">
-    <!-- 鎼滅储琛ㄥ崟 -->
     <el-form ref="searchForm" slot="search-form" :model="searchForm" label-width="100px" inline>
       <el-form-item label="绫诲埆鍚嶇О" prop="name">
-        <el-input v-model="searchForm.name" placeholder="璇疯緭鍏ョ被鍒悕绉�" @keypress.enter.native="search"></el-input>
+        <el-input v-model="searchForm.name" placeholder="璇疯緭鍏ョ被鍒悕绉�" @keypress.enter.native="loadTree"></el-input>
       </el-form-item>
       <el-form-item label="绫诲瀷" prop="type">
         <el-select v-model="searchForm.type" clearable placeholder="璇烽�夋嫨">
-          <el-option
-            v-for="item in typeList"
-            :key="item.id"
-            :label="item.name"
-            :value="item.id">
-          </el-option>
+          <el-option v-for="item in typeList" :key="item.id" :label="item.name" :value="item.id"></el-option>
         </el-select>
       </el-form-item>
       <section>
-        <el-button type="primary" @click="search">鎼滅储</el-button>
-        <!-- <el-button type="primary" :loading="isWorking.export" v-permissions="['business:category:exportExcel']" @click="exportExcel">瀵煎嚭</el-button> -->
-        <el-button @click="reset">閲嶇疆</el-button>
+        <el-button type="primary" @click="loadTree">鎼滅储</el-button>
+        <el-button @click="resetSearch">閲嶇疆</el-button>
       </section>
     </el-form>
-    <!-- 琛ㄦ牸鍜屽垎椤� -->
     <template v-slot:table-wrap>
       <ul class="toolbar" v-permissions="['business:category:create']">
-        <li><el-button type="primary" @click="$refs.operaCategoryWindow.open('鏂板缓绫诲埆')" icon="el-icon-plus" v-permissions="['business:category:create']">鏂板缓</el-button></li>
+        <li><el-button type="primary" @click="$refs.operaCategoryWindow.open('鏂板缓绫诲埆')" icon="el-icon-plus">鏂板缓涓�绾х被鍒�</el-button></li>
       </ul>
       <el-table
-        v-loading="isWorking.search"
-        :data="tableData.list"
+        v-loading="loading"
+        :data="tableData"
+        :tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
+        row-key="id"
         stripe
         border
+        default-expand-all
         :header-cell-style="rowStyle"
         :cell-style="rowStyle"
       >
-        <el-table-column prop="imgurl" label="鍥炬爣" min-width="100px">
+        <el-table-column prop="imgurl" label="鍥炬爣" min-width="90px">
           <template slot-scope="{row}">
-              <el-image
-              v-if="row.imgurl"
-              style="width: 70px; height: 70px"
-              :src="row.prefixUrl + row.imgurl"
-              :preview-src-list="[row.prefixUrl + row.imgurl]"
-              fit="cover"></el-image>
+            <el-image v-if="row.imgurl" style="width: 50px; height: 50px" :src="categoryImgUrl(row)" fit="cover"></el-image>
           </template>
         </el-table-column>
-        <el-table-column prop="name" label="绫诲埆鍚嶇О" min-width="100px"></el-table-column>
-        <el-table-column prop="categoryName" label="绫诲瀷" min-width="100px">
+        <el-table-column prop="name" label="绫诲埆鍚嶇О" min-width="140px">
           <template slot-scope="{row}">
-            <span v-if="row.type == 0">鑷畾涔�</span>
-            <span v-else>绯荤粺</span>
+            <span v-if="row.parentId">鈹� {{ row.name }}</span>
+            <span v-else>{{ row.name }}</span>
           </template>
         </el-table-column>
-        <el-table-column prop="sortnum" label="鎺掑簭鐮�(鍗囧簭)" min-width="100px"></el-table-column>
-        <el-table-column prop="createDate" label="鍒涘缓鏃堕棿" min-width="100px"></el-table-column>
-        <el-table-column prop="status" label="鐘舵��" min-width="100px">
+        <el-table-column label="绾у埆" min-width="80px">
+          <template slot-scope="{row}">{{ row.parentId ? '浜岀骇绫诲埆' : '涓�绾х被鍒�' }}</template>
+        </el-table-column>
+        <el-table-column prop="sortnum" label="鎺掑簭鐮�" min-width="80px"></el-table-column>
+        <el-table-column prop="createDate" label="鍒涘缓鏃堕棿" min-width="140px"></el-table-column>
+        <el-table-column prop="status" label="鐘舵��" min-width="80px">
           <template slot-scope="{row}">
-              <el-switch
-                  v-model="row.status"
-                  @change="changeStatus(row)"
-                  active-color="#13ce66"
-                  inactive-color="#ff4949"
-                  :active-value="0"
-                  :inactive-value="1">
-              </el-switch>
+            <el-switch v-model="row.status" @change="changeStatus(row)" :active-value="0" :inactive-value="1" />
           </template>
         </el-table-column>
-        <el-table-column
-          v-if="containPermissions(['business:category:update', 'business:category:delete'])"
-          label="鎿嶄綔"
-          min-width="120"
-          fixed="right"
-        >
+        <el-table-column label="鎿嶄綔" min-width="220px" fixed="right">
           <template slot-scope="{row}">
-            <el-button type="text" @click="edit(row.id)" icon="el-icon-edit" v-permissions="['business:category:update']">缂栬緫</el-button>
-            <el-button type="text" @click="deleteById(row)" icon="el-icon-delete" v-permissions="['business:category:delete']">鍒犻櫎</el-button>
+            <template v-if="!row.parentId">
+              <el-button type="text" @click="edit(row.id)" v-permissions="['business:category:update']">缂栬緫</el-button>
+              <el-button type="text" @click="$refs.operaSubCategoryWindow.open('鏂板缓瀛愮被鍒�', row)" v-permissions="['business:category:create']">鏂板瀛愮被鍒�</el-button>
+              <el-button type="text" class="btn-delete" icon="el-icon-delete" @click="deleteParent(row)" v-permissions="['business:category:delete']">鍒犻櫎</el-button>
+            </template>
+            <template v-else>
+              <el-button type="text" @click="$refs.operaSubCategoryWindow.open('缂栬緫瀛愮被鍒�', { id: row.parentId }, row)" v-permissions="['business:category:update']">缂栬緫</el-button>
+              <el-button type="text" class="btn-delete" icon="el-icon-delete" @click="deleteSub(row)" v-permissions="['business:category:delete']">鍒犻櫎</el-button>
+            </template>
           </template>
         </el-table-column>
       </el-table>
-      <pagination
-        @size-change="handleSizeChange"
-        @current-change="handlePageChange"
-        :pagination="tableData.pagination"
-      >
-      </pagination>
     </template>
-    <!-- 鏂板缓/淇敼 -->
-    <OperaCategoryWindow ref="operaCategoryWindow" @success="handlePageChange"/>
+    <OperaCategoryWindow ref="operaCategoryWindow" @success="loadTree"/>
+    <OperaSubCategoryWindow ref="operaSubCategoryWindow" @success="loadTree"/>
   </TableLayout>
 </template>
-  
+
 <script>
-  import BaseTable from '@/components/base/BaseTable'
-  import TableLayout from '@/layouts/TableLayout'
-  import Pagination from '@/components/common/Pagination'
-  import OperaCategoryWindow from '@/components/business/OperaCategoryWindow'
-  import { queryById, updateDisableById } from '@/api/business/category.js'
-  export default {
-    name: 'Category',
-    extends: BaseTable,
-    components: { TableLayout, Pagination, OperaCategoryWindow },
-    data () {
-      return {
-        // 鎼滅储
-        searchForm: {
-          name: '',
-          type: ''
-        },
-        typeList: [
-          { name: '鑷畾涔�', id: 0 },
-          { name: '绯荤粺', id: 1 }
-        ]
-      }
+import TableLayout from '@/layouts/TableLayout'
+import OperaCategoryWindow from '@/components/business/OperaCategoryWindow'
+import OperaSubCategoryWindow from '@/components/business/OperaSubCategoryWindow'
+import { fetchTree, queryById, updateDisableById, deleteById, deleteSub as deleteSubApi } from '@/api/business/category.js'
+
+export default {
+  name: 'Category',
+  components: { TableLayout, OperaCategoryWindow, OperaSubCategoryWindow },
+  data () {
+    return {
+      loading: false,
+      tableData: [],
+      searchForm: { name: '', type: '' },
+      typeList: [{ name: '鑷畾涔�', id: 0 }, { name: '绯荤粺', id: 1 }]
+    }
+  },
+  created () {
+    this.loadTree()
+  },
+  methods: {
+    rowStyle () { return 'text-align:center' },
+    categoryImgUrl (row) {
+      if (!row.imgurl) return ''
+      if (row.imgurl.indexOf('http') === 0) return row.imgurl
+      return (row.prefixUrl || '') + row.imgurl
     },
-    created () {
-      this.config({
-        module: '绫诲埆',
-        api: '/business/category',
-        'field.id': 'id',
-        'field.main': 'name'
-      })
-      this.search()
-    },
-    methods: {
-      rowStyle() {
-      return "text-align:center";
-    },
-      changeStatus(item) {
-        updateDisableById({
-          id: item.id,
-          status: item.status
-        }).then(res => {
-          this.$tip.apiSuccess('鏇存柊鎴愬姛')
-        }).finally(() => {
-          // this.search()
+    loadTree () {
+      this.loading = true
+      fetchTree(this.searchForm).then(res => {
+        this.tableData = (res || []).map(p => {
+          const prefixUrl = p.prefixUrl || ''
+          const children = (p.children || []).map(c => ({
+            ...c,
+            parentId: c.parentId != null ? c.parentId : p.id,
+            prefixUrl: c.prefixUrl || prefixUrl
+          }))
+          return { ...p, children }
         })
-      },
-      edit(id) {
-        queryById(id)
-          .then(res => {
-            let obj = {
-              id,
-              name: res.name,
-              attrFirst: res.attrFirst,
-              attrFirstList: res.attrFirstList,
-              attrSecond: res.attrSecond,
-              attrSecondList: res.attrSecondList,
-              sortnum: res.sortnum,
-              paramList: res.paramList,
-              imgurl: res.imgurl,
-              budgetList: res.budgetList.length > 0 ? res.budgetList : [{ maxamount: '', minamount: '' }],
-              type: res.type,
-              platCateId: res.platCateId,
-              tableData: res.paramList.map(item => {
-                return {
-                  id: item.id,
-                  name: item.name,
-                  isselect: item.isselect,
-                  isshow: item.isshow
-                }
-              }),
-              fileList: res.imgurl ? [{ url: res.prefixUrl + res.imgurl }] : []
-            }
-            this.$refs.operaCategoryWindow.open('缂栬緫绫诲埆', obj)
-          })
-      }
+      }).finally(() => { this.loading = false })
+    },
+    resetSearch () {
+      this.searchForm = { name: '', type: '' }
+      this.loadTree()
+    },
+    changeStatus (item) {
+      updateDisableById({ id: item.id, status: item.status }).then(() => {
+        this.$message.success('鏇存柊鎴愬姛')
+      })
+    },
+    edit (id) {
+      queryById(id).then(res => {
+        const obj = {
+          id, name: res.name, sortnum: res.sortnum, paramList: res.paramList,
+          imgurl: res.imgurl, budgetList: res.budgetList.length > 0 ? res.budgetList : [{ maxamount: '', minamount: '' }],
+          type: res.type, platCateId: res.platCateId,
+          tableData: res.paramList.map(item => ({ id: item.id, name: item.name, isselect: item.isselect, isshow: item.isshow })),
+          fileList: res.imgurl ? [{ url: res.prefixUrl + res.imgurl }] : []
+        }
+        this.$refs.operaCategoryWindow.open('缂栬緫绫诲埆', obj)
+      })
+    },
+    deleteParent (row) {
+      this.$confirm(`纭鍒犻櫎銆�${row.name}銆�?`, '鎻愮ず').then(() => {
+        deleteById(row.id).then(() => {
+          this.$message.success('鍒犻櫎鎴愬姛')
+          this.loadTree()
+        })
+      })
+    },
+    deleteSub (row) {
+      this.$confirm(`纭鍒犻櫎瀛愮被鍒��${row.name}銆�?`, '鎻愮ず').then(() => {
+        deleteSubApi(row.id).then(() => {
+          this.$message.success('鍒犻櫎鎴愬姛')
+          this.loadTree()
+        })
+      })
     }
   }
-</script>
\ No newline at end of file
+}
+</script>
diff --git a/company_admin/src/views/business/goods.vue b/company_admin/src/views/business/goods.vue
index bfe25e8..ee35be5 100644
--- a/company_admin/src/views/business/goods.vue
+++ b/company_admin/src/views/business/goods.vue
@@ -68,7 +68,7 @@
 
           <el-button type="primary" :loading="isWorking.export" @click="bulkOperation(0)">鎵归噺涓婃灦</el-button>
           <el-button type="primary" :loading="isWorking.export" @click="bulkOperation(1)">鎵归噺涓嬫灦</el-button>
-          <el-button style="background: red;border-color:red" type="primary" :loading="isWorking.export" @click="deleteByIdInBatch" icon="el-icon-delete" v-permissions="['business:goods:delete']">鎵归噺鍒犻櫎</el-button>
+          <el-button type="danger" :loading="isWorking.export" @click="deleteByIdInBatch" icon="el-icon-delete" v-permissions="['business:goods:delete']">鎵归噺鍒犻櫎</el-button>
         </ul>
         <el-table
           v-loading="isWorking.search"
diff --git a/company_admin/src/views/business/order.vue b/company_admin/src/views/business/order.vue
new file mode 100644
index 0000000..e7b0fe1
--- /dev/null
+++ b/company_admin/src/views/business/order.vue
@@ -0,0 +1,463 @@
+<template>
+  <TableLayout :permissions="['business:order:query']">
+    <el-form ref="searchForm" slot="search-form" :model="searchForm" label-width="100px" inline>
+      <el-form-item label="璁㈠崟缂栧彿" prop="orderNo">
+        <el-input v-model="searchForm.orderNo" placeholder="璁㈠崟缂栧彿" @keypress.enter.native="search"></el-input>
+      </el-form-item>
+      <el-form-item label="涓绘挱璐﹀彿" prop="anchorUsername">
+        <el-input v-model="searchForm.anchorUsername" placeholder="涓绘挱璐﹀彿"></el-input>
+      </el-form-item>
+      <section>
+        <el-button type="primary" @click="search">鎼滅储</el-button>
+        <el-button @click="reset">閲嶇疆</el-button>
+      </section>
+    </el-form>
+    <template v-slot:table-wrap>
+      <el-table v-loading="isWorking.search" :data="tableData.list" stripe border :header-cell-style="rowStyle" :cell-style="rowStyle">
+        <el-table-column prop="orderNo" label="璁㈠崟缂栧彿" min-width="140"></el-table-column>
+        <el-table-column prop="userBudget" label="瀹㈡埛棰勭畻" min-width="100"></el-table-column>
+        <el-table-column prop="categoryCount" label="鍝佺被鏁伴噺" min-width="90"></el-table-column>
+        <el-table-column prop="goodsCount" label="鍟嗗搧鏁伴噺" min-width="90"></el-table-column>
+        <el-table-column prop="totalZdPrice" label="鏃楄埌浠�" min-width="100"></el-table-column>
+        <el-table-column prop="totalPrice" label="鎸囧浠�" min-width="100"></el-table-column>
+        <el-table-column prop="anchorUsername" label="涓绘挱璐﹀彿" min-width="120"></el-table-column>
+        <el-table-column label="鍒涘缓鏃堕棿" min-width="160">
+          <template slot-scope="{row}">{{ formatOrderCreateTime(row) }}</template>
+        </el-table-column>
+        <el-table-column label="鎻愪氦鏃堕棿" min-width="160">
+          <template slot-scope="{row}">{{ formatDateTime(row.submitTime) }}</template>
+        </el-table-column>
+        <el-table-column prop="durationText" label="鍒涘缓鏃堕暱" min-width="100"></el-table-column>
+        <el-table-column label="鎿嶄綔" min-width="200" fixed="right">
+          <template slot-scope="{row}">
+            <el-button type="text" @click="showDetail(row.id)">璇︽儏</el-button>
+            <el-button type="text" :loading="exportingId === row.id" @click="exportRow(row.id)" v-permissions="['business:order:exportExcel']">瀵煎嚭</el-button>
+            <el-button type="text" class="btn-delete" icon="el-icon-delete" @click="deleteRow(row)" v-permissions="['business:order:delete']">鍒犻櫎</el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+      <pagination @size-change="handleSizeChange" @current-change="handlePageChange" :pagination="tableData.pagination" />
+    </template>
+    <el-dialog
+      title="璁㈠崟璇︽儏"
+      :visible.sync="detailVisible"
+      width="920px"
+      custom-class="order-detail-dialog"
+      append-to-body>
+      <template v-if="detail">
+        <div class="order-detail-panel">
+          <div class="order-detail-hero">
+            <div class="order-detail-hero__content">
+              <div class="order-detail-hero__label">璁㈠崟缂栧彿</div>
+              <div class="order-detail-hero__no">{{ detail.orderNo || '--' }}</div>
+              <div class="order-detail-hero__tags">
+                <span class="order-detail-tag">
+                  <i class="el-icon-user"></i>
+                  {{ detail.anchorUsername || '--' }}
+                </span>
+                <span class="order-detail-tag">
+                  <i class="el-icon-timer"></i>
+                  鍒涘缓鏃堕暱 {{ detail.durationText || '--' }}
+                </span>
+              </div>
+            </div>
+          </div>
+
+          <div class="order-detail-stats">
+            <div class="order-detail-stat">
+              <div class="order-detail-stat__label">瀹㈡埛棰勭畻</div>
+              <div class="order-detail-stat__value">{{ formatMoney(detail.userBudget) }}</div>
+            </div>
+            <div class="order-detail-stat">
+              <div class="order-detail-stat__label">鍝佺被鏁伴噺</div>
+              <div class="order-detail-stat__value">{{ formatCount(detail.categoryCount) }}</div>
+            </div>
+            <div class="order-detail-stat">
+              <div class="order-detail-stat__label">鍟嗗搧鏁伴噺</div>
+              <div class="order-detail-stat__value">{{ formatCount(detail.goodsCount) }}</div>
+            </div>
+            <div class="order-detail-stat">
+              <div class="order-detail-stat__label">鏃楄埌浠峰悎璁�</div>
+              <div class="order-detail-stat__value">{{ formatMoney(detail.totalZdPrice) }}</div>
+            </div>
+            <div class="order-detail-stat order-detail-stat--primary">
+              <div class="order-detail-stat__label">鎸囧浠峰悎璁�</div>
+              <div class="order-detail-stat__value">{{ formatMoney(detail.totalPrice) }}</div>
+            </div>
+          </div>
+
+          <div class="order-detail-meta">
+            <div class="order-detail-meta__item">
+              <div class="order-detail-meta__icon"><i class="el-icon-date"></i></div>
+              <div class="order-detail-meta__body">
+                <div class="order-detail-meta__label">鍒涘缓鏃堕棿</div>
+                <div class="order-detail-meta__value">{{ formatOrderCreateTime(detail) }}</div>
+              </div>
+            </div>
+            <div class="order-detail-meta__divider"></div>
+            <div class="order-detail-meta__item">
+              <div class="order-detail-meta__icon"><i class="el-icon-circle-check"></i></div>
+              <div class="order-detail-meta__body">
+                <div class="order-detail-meta__label">鎻愪氦鏃堕棿</div>
+                <div class="order-detail-meta__value">{{ formatDateTime(detail.submitTime) }}</div>
+              </div>
+            </div>
+          </div>
+        </div>
+
+        <div class="order-detail-table-wrap">
+          <div class="order-detail-section-head">
+            <span class="order-detail-section-head__title">鍟嗗搧鏄庣粏</span>
+            <span class="order-detail-section-head__extra">鍏� {{ (detail.items && detail.items.length) || 0 }} 浠�</span>
+          </div>
+          <el-table v-if="detail.items" :data="detail.items" border stripe :header-cell-style="rowStyle" :cell-style="rowStyle">
+            <el-table-column prop="sortNum" label="搴忓彿" width="60"></el-table-column>
+            <el-table-column prop="categoryName" label="鍝佺被鍚嶇О" min-width="120"></el-table-column>
+            <el-table-column prop="goodsName" label="浜у搧鍨嬪彿" min-width="160"></el-table-column>
+            <el-table-column label="鏃楄埌浠�" min-width="100">
+              <template slot-scope="{row}">{{ formatMoney(row.zdPrice) }}</template>
+            </el-table-column>
+            <el-table-column label="鎸囧浠�" min-width="100">
+              <template slot-scope="{row}">{{ formatMoney(row.price) }}</template>
+            </el-table-column>
+          </el-table>
+        </div>
+      </template>
+    </el-dialog>
+  </TableLayout>
+</template>
+
+<script>
+import BaseTable from '@/components/base/BaseTable'
+import TableLayout from '@/layouts/TableLayout'
+import Pagination from '@/components/common/Pagination'
+import { queryById, exportDetail, deleteById } from '@/api/business/order'
+
+export default {
+  name: 'PreselectOrder',
+  extends: BaseTable,
+  components: { TableLayout, Pagination },
+  data () {
+    return {
+      searchForm: { orderNo: '', anchorUsername: '' },
+      detailVisible: false,
+      detail: null,
+      exportingId: null
+    }
+  },
+  created () {
+    this.config({
+      module: '璁㈠崟',
+      api: '/business/order',
+      'field.id': 'id',
+      'field.main': 'orderNo'
+    })
+    this.search()
+  },
+  methods: {
+    rowStyle () { return 'text-align:center' },
+    formatDateTime (val) {
+      if (val == null || val === '') return '--'
+      if (typeof val === 'string' && /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/.test(val)) {
+        return val
+      }
+      const date = val instanceof Date ? val : new Date(val)
+      if (Number.isNaN(date.getTime())) return String(val)
+      const pad = n => String(n).padStart(2, '0')
+      return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`
+    },
+    formatOrderCreateTime (row) {
+      if (!row) return '--'
+      return this.formatDateTime(row.sessionCreateTime || row.createTime)
+    },
+    formatMoney (val) {
+      if (val == null || val === '') return '--'
+      return `锟�${val}`
+    },
+    formatCount (val) {
+      if (val == null || val === '') return '--'
+      return val
+    },
+    showDetail (id) {
+      queryById(id).then(res => {
+        this.detail = res
+        this.detailVisible = true
+      })
+    },
+    exportRow (id) {
+      this.exportingId = id
+      exportDetail(id)
+        .then(response => {
+          this.download(response)
+          this.$message.success('瀵煎嚭鎴愬姛')
+        })
+        .catch(e => {
+          this.$tip.apiFailed(e)
+        })
+        .finally(() => {
+          this.exportingId = null
+        })
+    },
+    deleteRow (row) {
+      this.$confirm(`纭鍒犻櫎璁㈠崟銆�${row.orderNo}銆�?`, '鎻愮ず').then(() => {
+        deleteById(row.id).then(() => {
+          this.$message.success('鍒犻櫎鎴愬姛')
+          this.search()
+        })
+      })
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+@import '@/assets/style/variables.scss';
+
+.order-detail-panel {
+  overflow: hidden;
+  border-radius: 8px;
+  border: 1px solid #e8edf5;
+  background: #fff;
+  box-shadow: 0 4px 18px rgba(46, 104, 236, 0.06);
+}
+
+.order-detail-hero {
+  position: relative;
+  padding: 22px 24px 20px;
+  background: linear-gradient(135deg, $primary-color 0%, #5b8def 55%, #7ba4f5 100%);
+  overflow: hidden;
+
+  &::before {
+    content: '';
+    position: absolute;
+    right: -30px;
+    top: -40px;
+    width: 160px;
+    height: 160px;
+    border-radius: 50%;
+    background: rgba(255, 255, 255, 0.08);
+  }
+
+  &::after {
+    content: '';
+    position: absolute;
+    right: 80px;
+    bottom: -50px;
+    width: 120px;
+    height: 120px;
+    border-radius: 50%;
+    background: rgba(255, 255, 255, 0.06);
+  }
+}
+
+.order-detail-hero__content {
+  position: relative;
+  z-index: 1;
+}
+
+.order-detail-hero__label {
+  font-size: 12px;
+  color: rgba(255, 255, 255, 0.82);
+  letter-spacing: 0.5px;
+}
+
+.order-detail-hero__no {
+  margin-top: 6px;
+  font-size: 22px;
+  font-weight: 600;
+  color: #fff;
+  letter-spacing: 0.5px;
+  line-height: 1.3;
+  word-break: break-all;
+}
+
+.order-detail-hero__tags {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 10px;
+  margin-top: 14px;
+}
+
+.order-detail-tag {
+  display: inline-flex;
+  align-items: center;
+  gap: 6px;
+  padding: 5px 12px;
+  border-radius: 999px;
+  background: rgba(255, 255, 255, 0.16);
+  backdrop-filter: blur(4px);
+  font-size: 12px;
+  color: #fff;
+  line-height: 1;
+
+  i {
+    font-size: 13px;
+  }
+}
+
+.order-detail-stats {
+  display: grid;
+  grid-template-columns: repeat(5, minmax(0, 1fr));
+  gap: 12px;
+  padding: 16px 18px 6px;
+}
+
+.order-detail-stat {
+  padding: 14px 12px;
+  border-radius: 8px;
+  background: linear-gradient(180deg, #f7f9fd 0%, #f3f6fb 100%);
+  border: 1px solid #e9eef6;
+  text-align: center;
+  transition: transform 0.2s ease, box-shadow 0.2s ease;
+
+  &:hover {
+    transform: translateY(-1px);
+    box-shadow: 0 6px 16px rgba(46, 104, 236, 0.08);
+  }
+}
+
+.order-detail-stat--primary {
+  background: linear-gradient(180deg, #fff8ef 0%, #fff3e3 100%);
+  border-color: #fde6c8;
+
+  .order-detail-stat__label {
+    color: #c6842d;
+  }
+
+  .order-detail-stat__value {
+    color: #e6a23c;
+  }
+}
+
+.order-detail-stat__label {
+  font-size: 12px;
+  color: #909399;
+  line-height: 1.4;
+}
+
+.order-detail-stat__value {
+  margin-top: 8px;
+  font-size: 18px;
+  font-weight: 600;
+  color: #303133;
+  line-height: 1.2;
+  word-break: break-all;
+}
+
+.order-detail-meta {
+  display: flex;
+  align-items: stretch;
+  margin: 10px 18px 16px;
+  padding: 14px 16px;
+  border-radius: 8px;
+  background: #fafbfd;
+  border: 1px dashed #e3e8f0;
+}
+
+.order-detail-meta__item {
+  flex: 1;
+  display: flex;
+  align-items: center;
+  gap: 12px;
+  min-width: 0;
+}
+
+.order-detail-meta__divider {
+  width: 1px;
+  margin: 0 18px;
+  background: #e4e7ed;
+  flex-shrink: 0;
+}
+
+.order-detail-meta__icon {
+  width: 36px;
+  height: 36px;
+  border-radius: 50%;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  background: rgba(46, 104, 236, 0.1);
+  color: $primary-color;
+  flex-shrink: 0;
+
+  i {
+    font-size: 16px;
+  }
+}
+
+.order-detail-meta__body {
+  min-width: 0;
+}
+
+.order-detail-meta__label {
+  font-size: 12px;
+  color: #909399;
+  line-height: 1.4;
+}
+
+.order-detail-meta__value {
+  margin-top: 4px;
+  font-size: 14px;
+  font-weight: 500;
+  color: #303133;
+  line-height: 1.4;
+  word-break: break-all;
+}
+
+.order-detail-table-wrap {
+  margin-top: 4px;
+}
+
+.order-detail-section-head {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  margin-bottom: 12px;
+  padding-bottom: 10px;
+  border-bottom: 1px solid #eef2f7;
+}
+
+.order-detail-section-head__title {
+  position: relative;
+  padding-left: 11px;
+  font-size: 15px;
+  font-weight: 600;
+  color: #303133;
+  line-height: 1;
+
+  &::before {
+    content: '';
+    position: absolute;
+    left: 0;
+    top: 1px;
+    width: 3px;
+    height: 14px;
+    border-radius: 2px;
+    background: $primary-color;
+  }
+}
+
+.order-detail-section-head__extra {
+  font-size: 12px;
+  color: #909399;
+}
+</style>
+
+<style lang="scss">
+.order-detail-dialog {
+  .el-dialog__body {
+    padding: 16px 20px 22px;
+  }
+
+  .el-dialog__header {
+    padding: 18px 20px 10px;
+    border-bottom: 1px solid #f0f2f5;
+  }
+
+  .el-dialog__title {
+    font-size: 16px;
+    font-weight: 600;
+    color: #303133;
+  }
+}
+</style>
diff --git a/company_admin/src/views/business/pageConfiguration.vue b/company_admin/src/views/business/pageConfiguration.vue
index 3f519c6..340777a 100644
--- a/company_admin/src/views/business/pageConfiguration.vue
+++ b/company_admin/src/views/business/pageConfiguration.vue
@@ -242,7 +242,32 @@
 
     </div>
     <div class="setting">
-      <div class="pz_head">淇℃伅</div>
+      <div class="pz_head pz_head--with-switch">
+        <span>涓绘挱绔〉闈㈤厤缃� <span class="version-tag">V2</span></span>
+        <span
+          class="version-switch version-switch--v2"
+          :class="{ 'version-switch--active': useThisVersion }"
+        >
+          <span class="version-switch__icon"><i class="el-icon-mobile-phone" /></span>
+          <span class="version-switch__body">
+            <span class="version-switch__label">绔嬪嵆浣跨敤璇ョ増鏈�</span>
+            <span class="version-switch__status">{{ useThisVersion ? '宸插惎鐢� 路 涓绘挱绔綋鍓嶇増鏈�' : '鐐瑰嚮鎸夐挳鍚庝富鎾灏嗗垏鎹㈣嚦鏈増鏈�' }}</span>
+          </span>
+          <span v-if="useThisVersion" class="version-switch__badge">
+            <i class="el-icon-success" /> 褰撳墠鐗堟湰
+          </span>
+          <el-button
+            v-else
+            type="primary"
+            size="medium"
+            :loading="versionSwitchLoading"
+            class="version-switch__btn version-switch__btn--v2"
+            @click="onUseThisVersion"
+          >
+            绔嬪嵆鐢熸晥
+          </el-button>
+        </span>
+      </div>
       <el-form ref="form" :model="form" label-width="130px" label-suffix="锛�">
         <el-form-item label="鍐呭鑼冨洿灏哄" props="rangeSize">
           <el-input v-model="form.rangeSize" type="number" style="width: 200px;" placeholder="鏀寔750px-1200px"></el-input>
@@ -493,7 +518,7 @@
 </template>
 
 <script>
-import { getByLoginNew, renewUpdate } from '@/api/business/page.js'
+import { getByLoginNew, renewUpdate, updateAnchorPageVersion } from '@/api/business/page.js'
 import UploadAvatarImage from '@/components/common/UploadAvatarImage.vue'
 import { upload } from '@/api/system/common'
 export default {
@@ -643,16 +668,34 @@
     getDesc() {
       getByLoginNew()
           .then(res => {
-            if (res.newParam) {
+            if (res && res.newParam) {
               var param= JSON.parse(res.newParam)
               for (const key in this.form) {
                 if(param[key]){
                   this.form[key] = param[key]
                 }
               }
-              // this.form = JSON.parse(res.newParam)
             }
+            this.anchorPageVersion = (res && res.anchorPageVersion) || 'v2'
+            this.useThisVersion = this.anchorPageVersion === 'v2'
           })
+    },
+
+    onUseThisVersion () {
+      if (this.useThisVersion || this.versionSwitchLoading) return
+      this.versionSwitchLoading = true
+      updateAnchorPageVersion({ anchorPageVersion: 'v2' })
+        .then(() => {
+          this.anchorPageVersion = 'v2'
+          this.useThisVersion = true
+          this.$message.success('宸插惎鐢� V2 閰嶇疆')
+        })
+        .catch(e => {
+          this.$tip.apiFailed(e)
+        })
+        .finally(() => {
+          this.versionSwitchLoading = false
+        })
     },
 
     submit() {
@@ -664,7 +707,10 @@
         this.$message.warning({ message: '灏哄蹇呴』灏忎簬1200' })
         return
       }
-      renewUpdate({ newParam: JSON.stringify(this.form) })
+      renewUpdate({
+        newParam: JSON.stringify(this.form),
+        anchorPageVersion: this.anchorPageVersion
+      })
           .then(res => {
             this.$message.success({ message: '淇濆瓨鎴愬姛' })
           })
@@ -674,6 +720,9 @@
   data() {
     return {
       isUploading: false,
+      useThisVersion: false,
+      versionSwitchLoading: false,
+      anchorPageVersion: 'v2',
       type: '0', // 0銆佷富鐣岄潰 1銆丳K鏁堟灉 2銆佹悳绱㈡晥鏋�
       shopList: [
         { categoryImgurl: '', categoryName: '娲楄。鏈�', brandName: '娴峰皵', ppShow: false, ppData: ['CLOMO', '娴峰皵', '鏍煎叞浠�', '涓夋槦'], name: 'EG100MATE35S', xhShow: false, xhData: ['EG100MATE35S', 'DG100MATE35S', 'FG100MATE35S'], zdPrice: '1899', price: '1899' },
@@ -1485,6 +1534,129 @@
 
   }
 
+  .pz_head--with-switch {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    gap: 16px;
+    flex-wrap: wrap;
+  }
+
+  .version-tag {
+    display: inline-block;
+    margin-left: 8px;
+    padding: 3px 10px;
+    border-radius: 4px;
+    font-size: 13px;
+    font-weight: 700;
+    color: #409eff;
+    background: #ecf5ff;
+    border: 1px solid #b3d8ff;
+    vertical-align: middle;
+  }
+
+  .version-switch {
+    display: inline-flex;
+    align-items: center;
+    gap: 14px;
+    padding: 12px 20px;
+    border-radius: 10px;
+    border: 2px solid #409eff;
+    background: linear-gradient(135deg, #ecf5ff 0%, #f5faff 100%);
+    box-shadow: 0 4px 14px rgba(64, 158, 255, 0.22);
+    transition: all 0.25s ease;
+    cursor: default;
+
+    &--active {
+      border-color: #409eff;
+      background: linear-gradient(135deg, #409eff 0%, #66b1ff 100%);
+      box-shadow: 0 6px 20px rgba(64, 158, 255, 0.4);
+
+      .version-switch__icon {
+        background: rgba(255, 255, 255, 0.25);
+        color: #fff;
+      }
+
+      .version-switch__label {
+        color: #fff;
+      }
+
+      .version-switch__status {
+        color: rgba(255, 255, 255, 0.88);
+      }
+    }
+
+    &__icon {
+      flex-shrink: 0;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      width: 40px;
+      height: 40px;
+      border-radius: 50%;
+      background: rgba(64, 158, 255, 0.15);
+      color: #409eff;
+      font-size: 20px;
+    }
+
+    &__body {
+      display: flex;
+      flex-direction: column;
+      gap: 2px;
+      min-width: 0;
+    }
+
+    &__label {
+      font-size: 15px;
+      font-weight: 700;
+      color: #409eff;
+      white-space: nowrap;
+      line-height: 1.3;
+    }
+
+    &__status {
+      font-size: 12px;
+      color: #909399;
+      white-space: nowrap;
+      line-height: 1.3;
+    }
+
+    &__badge {
+      flex-shrink: 0;
+      display: inline-flex;
+      align-items: center;
+      gap: 6px;
+      padding: 8px 16px;
+      border-radius: 20px;
+      font-size: 14px;
+      font-weight: 600;
+      color: #fff;
+      background: rgba(255, 255, 255, 0.25);
+      white-space: nowrap;
+    }
+
+    &__btn {
+      flex-shrink: 0;
+      padding: 10px 22px;
+      font-size: 14px;
+      font-weight: 700;
+      border-radius: 20px;
+      border: none;
+      box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
+
+      &--v2 {
+        background: #fff;
+        color: #409eff;
+
+        &:hover,
+        &:focus {
+          background: #f5faff;
+          color: #409eff;
+        }
+      }
+    }
+  }
+
   .pz_item {
     // width: 750px;
     display: flex;
diff --git a/company_admin/src/views/business/pageConfigurationNew.vue b/company_admin/src/views/business/pageConfigurationNew.vue
new file mode 100644
index 0000000..599496e
--- /dev/null
+++ b/company_admin/src/views/business/pageConfigurationNew.vue
@@ -0,0 +1,2912 @@
+<template>
+
+  <div class="anchor-pz">
+
+    <div class="prediv">
+
+      <div class="option">
+
+        <div class="pz_head">椤甸潰棰勮</div>
+
+        <el-tabs v-model="type">
+
+          <el-tab-pane label="涓荤晫闈�" name="0"></el-tab-pane>
+
+          <el-tab-pane label="PK鍙�" name="1"></el-tab-pane>
+
+          <el-tab-pane label="鍟嗗搧鍒楄〃" name="2"></el-tab-pane>
+
+          <el-tab-pane label="鎼滅储鏁堟灉" name="3"></el-tab-pane>
+
+        </el-tabs>
+
+      </div>
+
+      <div class="prediv-content">
+
+        <div class="content h5-preview" :style="contentStyle">
+
+          <div class="pv_banner_wrap">
+            <img :src="topImgUrl" alt="" class="pv_banner_img">
+            <div class="pv_user_bar"><span>涓绘挱绔�</span></div>
+          </div>
+
+          <div class="pv_nav_back" v-if="type !== '0'">鈥�</div>
+
+          <div class="pv_sticky_header" :style="{ background: previewMainBg }">
+            <div class="pv_submit_row">
+              <div class="pv_submit_btn submit_btn--icon" :style="submitBtnStyle">
+                <img :src="submitBtnIconPreviewUrl" class="pv_submit_icon_img" alt="">
+                <span class="pv_submit_text">鎻愪氦璁㈠崟</span>
+              </div>
+            </div>
+            <div
+              class="pv_home_top"
+              :class="{ 'pv_home_top--compact': type === '1' || type === '3' }"
+              :style="homeTopSectionStyle">
+              <div class="pv_order_bar">
+                <div class="pv_order_left">
+                  <span class="pv_order_label">鐢ㄦ埛棰勭畻</span>
+                  <span class="pv_order_currency">楼</span>
+                  <span class="pv_budget_val">603</span>
+                  <div class="pv_remain_inline">
+                    <span class="pv_order_label">鍓╀綑缁忚垂</span>
+                    <span class="pv_remain_val">楼 303.00</span>
+                  </div>
+                </div>
+                <div class="pv_order_right">
+                  <div class="pv_order_no_row">
+                    <span class="pv_order_label">璁㈠崟缂栧彿</span>
+                    <span class="pv_order_no">260602741600</span>
+                  </div>
+                </div>
+              </div>
+
+              <div class="pv_featured" v-if="previewFeaturedDisplay.length">
+                <div class="pv_featured_left">
+                  <span class="pv_featured_title">绮惧搧浼橀��</span>
+                  <span class="pv_featured_sub">瀹炴祴澶氱淮缁煎悎璇勫畾</span>
+                </div>
+                <div class="pv_featured_products">
+                  <div class="pv_featured_item" v-for="(item, idx) in previewFeaturedDisplay" :key="idx">
+                    <div class="pv_featured_img_wrap">
+                      <img v-if="featuredPreviewUrl(item)" :src="featuredPreviewUrl(item)" class="pv_featured_img">
+                      <div v-else class="pv_featured_img_ph"></div>
+                    </div>
+                    <div class="pv_featured_info">
+                      <span class="pv_featured_name">{{ item.title }}</span>
+                      <div class="pv_featured_tag_row">
+                        <span class="pv_tag pv_tag_dim" v-if="item.tags && item.tags[0]">{{ item.tags[0] }}</span>
+                        <span class="pv_tag" :class="idx === 0 ? 'pv_tag_blue' : 'pv_tag_green'" v-if="item.tags && item.tags[1]">{{ item.tags[1] }}</span>
+                      </div>
+                    </div>
+                  </div>
+                </div>
+              </div>
+
+              <div class="pv_table_head_wrap" v-if="type === '0'">
+                <div class="pv_shop_table_head">
+                  <span class="pv_col pv_col_cat">鍝佺被 <span class="pv_arrow">鈻�</span></span>
+                  <span class="pv_col">鍨嬪彿</span>
+                  <span class="pv_col">鏃楄埌浠�</span>
+                  <span class="pv_col">鎸囧浠�</span>
+                </div>
+              </div>
+
+              <div class="pv_table_head_wrap pv_product_list_head" v-if="type === '2'">
+                <div class="pv_shop_table_head pv_product_shop_table_head">
+                  <span class="pv_col">鍝佺墝 <span class="pv_arrow">鈻�</span></span>
+                  <span class="pv_col pv_product_search_col">
+                    <span class="pv_product_search_inline" :style="searchStyle">
+                      <span class="pv_search_ico">馃攳</span>
+                      <span class="pv_search_divider"></span>
+                      <span class="pv_search_ph">鎼滅储鍨嬪彿</span>
+                    </span>
+                  </span>
+                  <span class="pv_col pv_product_cat_col pv_product_cat_col--span">
+                    <span class="pv_product_cat_inner">
+                      <span class="pv_cat_line">鏅鸿兘闂ㄩ攣</span>
+                    </span>
+                  </span>
+                </div>
+              </div>
+            </div>
+          </div>
+
+          <!-- 涓荤晫闈� -->
+          <template v-if="type === '0'">
+            <div class="pv_shop_table">
+              <div class="pv_shop_table_body">
+                <div class="pv_shop_row" v-for="(row, i) in mockRows" :key="'r-' + i">
+                  <div class="pv_shop_row_main">
+                    <div class="pv_row_cat_cell">
+                      <div class="pv_cat_icon_ph"></div>
+                      <span class="pv_cell_ellipsis">{{ row.categoryName }}</span>
+                    </div>
+                    <div class="pv_row_info_group">
+                      <span class="pv_col pv_cell_ellipsis">{{ row.name || '' }}</span>
+                      <span class="pv_col pv_cell_ellipsis">{{ row.zdPrice || '' }}</span>
+                      <span class="pv_col pv_cell_ellipsis">{{ row.price || '' }}</span>
+                    </div>
+                  </div>
+                </div>
+                <div class="pv_shop_row pv_shop_row--expand">
+                  <div class="pv_expand_cat" :style="rowStyle">
+                    <span class="pv_expand_del">脳</span>
+                    <div class="pv_expand_cat_ph"></div>
+                    <span>鏅鸿兘瀹跺眳</span>
+                  </div>
+                  <div class="pv_expand_panel" :style="rowStyle">
+                    <div class="pv_expand_data_row">
+                      <span class="pv_expand_model">涔濈墽/ZS860 (鍑�鐣孶ltra)</span>
+                      <span class="pv_expand_zd">4,899</span>
+                      <span class="pv_expand_price">3,300</span>
+                    </div>
+                    <div class="pv_sub_cat_grid">
+                      <div class="pv_sub_cat_item" v-for="s in subCats" :key="s">
+                        <div class="pv_sub_icon"></div>
+                        <span>{{ s }}</span>
+                      </div>
+                    </div>
+                    <div class="pv_expand_search_wrap">
+                      <div class="pv_expand_search" :style="searchStyle">
+                        <span class="pv_search_ico">馃攳</span>
+                        <span class="pv_search_ph">鎼滅储鍟嗗搧鍚嶇О/鍨嬪彿</span>
+                      </div>
+                    </div>
+                  </div>
+                </div>
+              </div>
+            </div>
+          </template>
+
+          <!-- PK鍙� -->
+          <template v-if="type === '1'">
+            <div class="pv_pk_page" :style="pkShowcaseTheme">
+              <div class="pv_pk_cards">
+                <div class="pv_pk_side">
+                  <div class="pv_pk_showcase">
+                    <div class="pv_pk_showcase_frame">
+                      <div class="pv_pk_visual">
+                        <div class="pv_pk_img_card">
+                          <img class="pv_pk_img" :src="pkPreviewProduct('left').img" alt="">
+                        </div>
+                        <div class="pv_pk_reflect"><img :src="pkPreviewProduct('left').img" alt=""></div>
+                        <div class="pv_pk_ground"></div>
+                      </div>
+                    </div>
+                  </div>
+                  <div class="pv_pk_podium"></div>
+                  <div class="pv_pk_detail">
+                    <div class="pv_pk_detail_head" :style="tableHeaderStyle">{{ pkPreviewProduct('left').name }}</div>
+                    <div class="pv_pk_detail_body">
+                      <div class="pv_pk_price_cell">
+                        <span class="pv_pk_price_label">鏈熼棿浠�</span>
+                        <span class="pv_pk_price_val">楼{{ pkPreviewProduct('left').zdPrice }}</span>
+                      </div>
+                      <div class="pv_pk_price_cell pv_pk_price_cell--hot">
+                        <span class="pv_pk_price_label">鎸囧浠�</span>
+                        <span class="pv_pk_price_val">楼{{ pkPreviewProduct('left').price }}</span>
+                      </div>
+                    </div>
+                  </div>
+                </div>
+                <div class="pv_pk_vs" :style="{ color: pkVsPreviewColor }">VS</div>
+                <div class="pv_pk_side">
+                  <div class="pv_pk_showcase">
+                    <div class="pv_pk_showcase_frame">
+                      <div class="pv_pk_visual">
+                        <div class="pv_pk_img_card">
+                          <img class="pv_pk_img" :src="pkPreviewProduct('right').img" alt="">
+                        </div>
+                        <div class="pv_pk_reflect"><img :src="pkPreviewProduct('right').img" alt=""></div>
+                        <div class="pv_pk_ground"></div>
+                      </div>
+                    </div>
+                  </div>
+                  <div class="pv_pk_podium"></div>
+                  <div class="pv_pk_detail">
+                    <div class="pv_pk_detail_head" :style="tableHeaderStyle">{{ pkPreviewProduct('right').name }}</div>
+                    <div class="pv_pk_detail_body">
+                      <div class="pv_pk_price_cell">
+                        <span class="pv_pk_price_label">鏈熼棿浠�</span>
+                        <span class="pv_pk_price_val">楼{{ pkPreviewProduct('right').zdPrice }}</span>
+                      </div>
+                      <div class="pv_pk_price_cell pv_pk_price_cell--hot">
+                        <span class="pv_pk_price_label">鎸囧浠�</span>
+                        <span class="pv_pk_price_val">楼{{ pkPreviewProduct('right').price }}</span>
+                      </div>
+                    </div>
+                  </div>
+                </div>
+              </div>
+              <div class="pv_pk_params">
+                <div class="pv_pk_param_col">
+                  <div class="pv_pk_param_head" :style="tableHeaderStyle">
+                    <div class="pv_pk_action_group">
+                      <div class="pv_pk_change"><span>鈫�</span><span>鎹釜鍟嗗搧</span></div>
+                      <div class="pv_pk_confirm"><span>鉁�</span><span>閫夌敤宸︿晶</span></div>
+                    </div>
+                  </div>
+                  <div class="pv_pk_param_pills">
+                    <div class="pv_pill" v-for="(p, pi) in pkPreviewParams" :key="'l-' + pi">
+                      <span class="pv_pill_label">{{ p.name }}</span>
+                      <span class="pv_pill_val">{{ p.val }}</span>
+                    </div>
+                  </div>
+                </div>
+                <div class="pv_pk_vs pv_pk_vs--spacer">VS</div>
+                <div class="pv_pk_param_col">
+                  <div class="pv_pk_param_head" :style="tableHeaderStyle">
+                    <div class="pv_pk_action_group">
+                      <div class="pv_pk_change"><span>鈫�</span><span>鎹釜鍟嗗搧</span></div>
+                      <div class="pv_pk_confirm"><span>鉁�</span><span>閫夌敤鍙充晶</span></div>
+                    </div>
+                  </div>
+                  <div class="pv_pk_param_pills">
+                    <div class="pv_pill" v-for="(p, pi) in pkPreviewParams" :key="'r-' + pi">
+                      <span class="pv_pill_label">{{ p.name }}</span>
+                      <span class="pv_pill_val">{{ p.val }}</span>
+                    </div>
+                  </div>
+                </div>
+              </div>
+            </div>
+          </template>
+
+          <!-- 鍟嗗搧鍒楄〃 -->
+          <template v-if="type === '2'">
+            <div class="pv_product_page">
+              <div class="pv_product_grid">
+                <div class="pv_product_card" v-for="(p, i) in productList" :key="i" :style="productCardStyle">
+                  <div class="pv_product_card_img">
+                    <img :src="p.img" alt="">
+                  </div>
+                  <div class="pv_product_card_info">
+                    <span class="pv_product_brand">{{ p.brandName }}</span>
+                    <span class="pv_product_model">{{ p.name }}</span>
+                    <span class="pv_product_price_label">鎸囧浠� 楼 {{ p.price }}</span>
+                  </div>
+                </div>
+              </div>
+            </div>
+          </template>
+
+          <!-- 鎼滅储鏁堟灉 -->
+          <template v-if="type === '3'">
+            <div class="pv_pk_page pv_pk_page--ghost">
+              <div class="pv_pk_cards pv_pk_cards--ghost">
+                <div class="pv_pk_side pv_pk_side--ghost" v-for="n in 2" :key="n">
+                  <div class="pv_pk_showcase pv_pk_showcase--ghost"><div class="pv_pk_showcase_frame"></div></div>
+                </div>
+              </div>
+            </div>
+            <div class="pv_search_overlay">
+              <div class="pv_pk_search_panel">
+                <div class="pv_pk_search_head" :style="themeStyle">
+                  <div class="pv_pk_search_head_left">
+                    <span class="pv_pk_search_title">閫夋嫨鍟嗗搧</span>
+                    <span class="pv_pk_search_badge">PK 宸︿晶</span>
+                  </div>
+                  <span class="pv_pk_search_close">脳</span>
+                </div>
+                <div class="pv_pk_search_body">
+                  <div class="pv_pk_search_cat">
+                    <span class="pv_pk_search_cat_label">鍝佺被</span>
+                    <span class="pv_pk_search_cat_name">鏅鸿兘椹《</span>
+                  </div>
+                  <div class="pv_pk_search_bar" :style="searchStyle">
+                    <span class="pv_search_ico">馃攳</span>
+                    <span class="pv_search_divider"></span>
+                    <span class="pv_pk_search_input">鎼滅储鍨嬪彿</span>
+                  </div>
+                  <div class="pv_pk_search_table_head">
+                    <span>鍟嗗搧鍨嬪彿</span>
+                    <span>鎸囧浠�</span>
+                  </div>
+                  <div class="pv_pk_search_list">
+                    <div
+                      class="pv_pk_search_item"
+                      v-for="(s, i) in searchData"
+                      :key="i"
+                      :class="{ 'pv_pk_search_item--active': i === 0 }">
+                      <span class="pv_pk_search_item_name">{{ s.name }}</span>
+                      <span class="pv_pk_search_item_price">楼{{ s.price }}</span>
+                    </div>
+                  </div>
+                  <div class="pv_pk_search_footer">鍏� {{ searchData.length }} 浠跺晢鍝�</div>
+                </div>
+              </div>
+            </div>
+          </template>
+
+        </div>
+
+      </div>
+
+    </div>
+
+
+
+    <div class="form-panel">
+
+      <div class="pz_head pz_head--with-switch">
+        <span>涓绘挱绔〉闈㈤厤缃� <span class="version-tag">V3</span></span>
+        <span
+          class="version-switch version-switch--v3"
+          :class="{ 'version-switch--active': useThisVersion }"
+        >
+          <span class="version-switch__icon"><i class="el-icon-mobile-phone" /></span>
+          <span class="version-switch__body">
+            <span class="version-switch__label">绔嬪嵆浣跨敤璇ョ増鏈�</span>
+            <span class="version-switch__status">{{ useThisVersion ? '宸插惎鐢� 路 涓绘挱绔綋鍓嶇増鏈�' : '鐐瑰嚮鎸夐挳鍚庝富鎾灏嗗垏鎹㈣嚦鏈増鏈�' }}</span>
+          </span>
+          <span v-if="useThisVersion" class="version-switch__badge">
+            <i class="el-icon-success" /> 褰撳墠鐗堟湰
+          </span>
+          <el-button
+            v-else
+            type="warning"
+            size="medium"
+            :loading="versionSwitchLoading"
+            class="version-switch__btn version-switch__btn--v3"
+            @click="onUseThisVersion"
+          >
+            绔嬪嵆鐢熸晥
+          </el-button>
+        </span>
+      </div>
+
+      <el-form :model="form" label-width="120px">
+
+        <el-form-item label="鍐呭瀹藉害">
+          <el-input v-model="form.rangeSize" placeholder="750-1200" style="width:200px" />
+          <span class="form-tip">px锛屾敮鎸� 750-1200</span>
+        </el-form-item>
+
+        <el-form-item label="椤甸潰鑳屾櫙">
+          <div class="color-select-item">
+            <el-radio-group v-model="form.pageBg.mode" @change="onPageBgModeChange">
+              <el-radio :label="0">榛樿</el-radio>
+              <el-radio :label="1">鑷畾涔夐鑹�</el-radio>
+              <el-radio :label="2">鑷畾涔夊浘</el-radio>
+            </el-radio-group>
+            <template v-if="form.pageBg.mode === 0">
+              <div class="img-default-preview page-bg-default-preview">
+                <span class="page-bg-swatch" :style="{ background: pageBgDefaultColor }"></span>
+                <span class="form-tip">榛樿鑳屾櫙鑹�</span>
+              </div>
+            </template>
+            <template v-if="form.pageBg.mode === 1">
+              <el-color-picker v-model="form.pageBg.bgColor" size="mini" />
+              <span class="inline-label">涓嶉�忔槑搴�</span>
+              <el-input v-model="form.pageBg.alpha" style="width:80px" placeholder="100" />
+              <span class="inline-label">%</span>
+            </template>
+            <template v-if="form.pageBg.mode === 2">
+              <div v-if="!form.pageBg.imgurl" class="img-default-preview">
+                <el-image :src="defaultBgImg" style="width:100px;height:100px" fit="contain" />
+              </div>
+              <div class="img-select-item">
+                <el-image v-if="form.pageBg.imgurl" style="width:100px;height:100px" :src="form.pageBg.imgurl" fit="contain" class="thumb-block" />
+                <el-upload :action="action" :headers="uploadHeaders" :data="{ folder: 'web_param' }" :show-file-list="false"
+                  :on-success="(r,f)=>uploadSuccess(r,f,'pageBg')" :on-error="uploadError">
+                  <el-button size="small" icon="el-icon-plus">涓婁紶</el-button>
+                </el-upload>
+                <span class="form-tip">椤甸潰鑳屾櫙鍥撅紝寤鸿 1920脳1080px</span>
+              </div>
+            </template>
+          </div>
+        </el-form-item>
+
+        <el-form-item label="鎻愪氦璁㈠崟鍥炬爣">
+          <el-radio-group v-model="form.submitBtnIcon.type" @change="onSubmitBtnIconTypeChange">
+            <el-radio :label="0">榛樿</el-radio>
+            <el-radio :label="1">鑷畾涔�</el-radio>
+          </el-radio-group>
+          <div v-if="form.submitBtnIcon.type === 0" class="img-default-preview">
+            <el-image :src="defaultSubmitBtnIcon" style="width:48px;height:48px" fit="contain" />
+          </div>
+          <div v-else class="img-select-item">
+            <el-image v-if="form.submitBtnIcon.imgurl" style="width:48px;height:48px" :src="form.submitBtnIcon.imgurl" fit="contain" class="thumb-block" />
+            <el-upload :action="action" :headers="uploadHeaders" :data="{ folder: 'web_param' }" :show-file-list="false"
+              :on-success="(r,f)=>uploadSuccess(r,f,'submitBtnIcon')" :on-error="uploadError">
+              <el-button size="small" icon="el-icon-plus">涓婁紶</el-button>
+            </el-upload>
+            <span class="form-tip">鎸夐挳鍥炬爣妯″紡锛屽缓璁� 48脳48px PNG</span>
+          </div>
+        </el-form-item>
+
+        <el-form-item label="鎻愪氦璁㈠崟鑳屾櫙鑹�">
+          <div class="color-select-item">
+            <el-radio-group v-model="form.submitBtnBg.bgType" @change="onSubmitBtnBgTypeChange">
+              <el-radio :label="0">榛樿</el-radio>
+              <el-radio :label="1">鑷畾涔�</el-radio>
+            </el-radio-group>
+            <template v-if="form.submitBtnBg.bgType === 1">
+              <el-color-picker v-model="form.submitBtnBg.bgColor" size="mini" />
+              <span class="inline-label">涓嶉�忔槑搴�</span>
+              <el-input v-model="form.submitBtnBg.bgAlpha" style="width:80px" placeholder="100" />
+              <span class="inline-label">%</span>
+            </template>
+          </div>
+        </el-form-item>
+
+        <el-form-item label="鎻愪氦璁㈠崟瀛椾綋鑹�">
+          <div class="color-select-item">
+            <el-radio-group v-model="form.submitBtnColor.colorType" @change="onSubmitBtnColorTypeChange">
+              <el-radio :label="0">榛樿</el-radio>
+              <el-radio :label="1">鑷畾涔�</el-radio>
+            </el-radio-group>
+            <template v-if="form.submitBtnColor.colorType === 1">
+              <el-color-picker v-model="form.submitBtnColor.color" size="mini" />
+            </template>
+          </div>
+        </el-form-item>
+
+        <el-form-item label="椤堕儴瀹d紶鍥�">
+          <el-radio-group v-model="form.topImg.type" @change="onTopImgTypeChange">
+            <el-radio :label="0">榛樿</el-radio>
+            <el-radio :label="1">鑷畾涔�</el-radio>
+          </el-radio-group>
+          <div v-if="form.topImg.type === 0" class="img-default-preview">
+            <el-image :src="defaultBanner" style="width:100px;height:100px" fit="contain" />
+          </div>
+          <div v-else class="img-select-item">
+            <el-image v-if="form.topImg.imgurl" style="width:100px;height:100px" :src="form.topImg.imgurl" fit="contain" class="thumb-block" />
+            <el-upload :action="action" :headers="uploadHeaders" :data="{ folder: 'web_param' }" :show-file-list="false"
+              :on-success="(r,f)=>uploadSuccess(r,f,'topImg')" :on-error="uploadError">
+              <el-button size="small" icon="el-icon-plus">涓婁紶</el-button>
+            </el-upload>
+            <span class="form-tip">寤鸿灏哄 700脳300px</span>
+          </div>
+        </el-form-item>
+
+        <el-form-item label="涓婚鑳屾櫙鑹�">
+          <div class="color-select-item">
+            <el-radio-group v-model="form.theme.bgType" @change="onThemeBgTypeChange">
+              <el-radio :label="0">榛樿</el-radio>
+              <el-radio :label="1">鑷畾涔�</el-radio>
+            </el-radio-group>
+            <template v-if="form.theme.bgType === 1">
+              <el-color-picker v-model="form.theme.bgColor" size="mini" />
+            </template>
+            <span class="form-tip">椤舵爮銆佽〃鏍艰〃澶淬�丳K 鏍囬鍖虹瓑</span>
+          </div>
+        </el-form-item>
+
+        <el-form-item label="涓婚瀛椾綋鑹�">
+          <div class="color-select-item">
+            <el-radio-group v-model="form.theme.colorType" @change="onThemeColorTypeChange">
+              <el-radio :label="0">榛樿</el-radio>
+              <el-radio :label="1">鑷畾涔�</el-radio>
+            </el-radio-group>
+            <template v-if="form.theme.colorType === 1">
+              <el-color-picker v-model="form.theme.color" size="mini" />
+            </template>
+          </div>
+        </el-form-item>
+
+        <el-form-item label="琛岃儗鏅壊">
+          <div class="color-select-item">
+            <el-radio-group v-model="form.table.rowBgType" @change="onTableRowBgTypeChange">
+              <el-radio :label="0">榛樿</el-radio>
+              <el-radio :label="1">鑷畾涔�</el-radio>
+            </el-radio-group>
+            <template v-if="form.table.rowBgType === 1">
+              <el-color-picker v-model="form.table.rowBg" size="mini" />
+            </template>
+          </div>
+        </el-form-item>
+
+        <el-form-item label="鎼滅储妗嗚儗鏅�">
+          <div class="color-select-item">
+            <el-radio-group v-model="form.search.bgType" @change="onSearchBgTypeChange">
+              <el-radio :label="0">榛樿</el-radio>
+              <el-radio :label="1">鑷畾涔�</el-radio>
+            </el-radio-group>
+            <template v-if="form.search.bgType === 1">
+              <el-color-picker v-model="form.search.bgColor" size="mini" />
+              <span class="inline-label">涓嶉�忔槑搴�</span>
+              <el-input v-model="form.search.bgAlpha" style="width:80px" placeholder="100" />
+              <span class="inline-label">%</span>
+            </template>
+          </div>
+        </el-form-item>
+
+        <el-form-item label="鍟嗗搧鍗¤儗鏅�">
+          <div class="color-select-item">
+            <el-radio-group v-model="form.productList.bgType" @change="onProductListBgTypeChange">
+              <el-radio :label="0">榛樿</el-radio>
+              <el-radio :label="1">鑷畾涔�</el-radio>
+            </el-radio-group>
+            <template v-if="form.productList.bgType === 1">
+              <el-color-picker v-model="form.productList.bgColor" size="mini" />
+              <span class="inline-label">涓嶉�忔槑搴�</span>
+              <el-input v-model="form.productList.bgAlpha" style="width:80px" placeholder="100" />
+              <span class="inline-label">%</span>
+            </template>
+          </div>
+        </el-form-item>
+
+        <el-form-item label="PK鑳屾櫙鑹�">
+          <div class="color-select-item">
+            <el-radio-group v-model="form.pk.bgColorType" @change="onPkBgColorTypeChange">
+              <el-radio :label="0">榛樿</el-radio>
+              <el-radio :label="1">鑷畾涔�</el-radio>
+            </el-radio-group>
+            <template v-if="form.pk.bgColorType === 1">
+              <el-color-picker v-model="form.pk.bgColor" size="mini" />
+            </template>
+            <span class="form-tip">鎺у埗 H5 PK 鍙板睍绀烘銆佹寜閽瓑涓婚鑹�</span>
+          </div>
+        </el-form-item>
+
+        <el-form-item label="PK瀵规瘮鑹�">
+          <div class="color-select-item">
+            <el-radio-group v-model="form.pk.vsColorType" @change="onPkVsColorTypeChange">
+              <el-radio :label="0">榛樿</el-radio>
+              <el-radio :label="1">鑷畾涔�</el-radio>
+            </el-radio-group>
+            <template v-if="form.pk.vsColorType === 1">
+              <el-color-picker v-model="form.pk.vsColor" size="mini" />
+            </template>
+            <span class="form-tip">鎺у埗 H5 PK 鍙� VS 鏍囪瘑棰滆壊</span>
+          </div>
+        </el-form-item>
+
+        <div v-for="(feat, fi) in form.featured" :key="fi" class="featured-form">
+
+          <div class="sub-title">绮惧搧浼橀�� {{ fi + 1 }}</div>
+
+          <el-form-item label="閫夋嫨鍟嗗搧">
+
+            <el-button type="primary" plain size="small" @click="openFeaturedPicker(fi)">浠庝紒涓氬晢鍝佸簱閫夋嫨</el-button>
+
+            <el-button v-if="feat.goodsId" type="text" @click="clearFeaturedGoods(fi)">娓呴櫎</el-button>
+
+          </el-form-item>
+
+          <div v-if="feat.goodsId || feat.title" class="featured-selected">
+
+            <img v-if="featuredPreviewUrl(feat)" :src="featuredPreviewUrl(feat)" class="featured-selected-img" />
+
+            <div v-else class="featured-selected-img featured-selected-ph"></div>
+
+            <div class="featured-selected-info">
+
+              <div class="featured-selected-name">{{ feat.title || '鏈�夋嫨鍟嗗搧' }}</div>
+
+              <div v-if="feat.goodsId" class="featured-selected-id">鍟嗗搧ID锛歿{ feat.goodsId }}</div>
+
+            </div>
+
+          </div>
+
+          <el-form-item label="鏍囩">
+
+            <el-input v-model="feat.tags[0]" placeholder="鏍囩1" style="margin-bottom:8px" />
+
+            <el-input v-model="feat.tags[1]" placeholder="鏍囩2" />
+
+          </el-form-item>
+
+        </div>
+
+        <FeaturedGoodsPicker ref="featuredGoodsPicker" @select="onFeaturedGoodsSelect" />
+
+        <el-form-item>
+
+          <el-button type="primary" @click="submit">淇濆瓨閰嶇疆</el-button>
+
+        </el-form-item>
+
+      </el-form>
+
+    </div>
+
+  </div>
+
+</template>
+
+
+
+<script>
+
+import { getByLoginAnchor, renewAnchorUpdate, updateAnchorPageVersion } from '@/api/business/page'
+import FeaturedGoodsPicker from '@/components/business/FeaturedGoodsPicker'
+import Cookies from 'js-cookie'
+
+const PAGE_DEFAULTS = {
+  pageBg: { mode: 0, bgColor: '#E8DCC8', alpha: '100', img: '', imgurl: '' },
+  theme: { bgType: 0, bgColor: '#4A3728', colorType: 0, color: '#FFFFFF' },
+  table: {
+    rowBgType: 0,
+    rowBg: '#FFFFFF',
+    hoverScale: 1.05
+  },
+  search: { bgType: 0, bgColor: '#FFFFFF', bgAlpha: '100' },
+  productList: { bgType: 0, bgColor: '#FFFFFF', bgAlpha: '100' },
+  pk: { bgColorType: 0, bgColor: '#FF8C42', vsColorType: 0, vsColor: '#FF8C42' },
+  submitBtnIcon: { type: 0, img: '', imgurl: '' },
+  submitBtnBg: { bgType: 0, bgColor: '#FFFFFF', bgAlpha: '100' },
+  submitBtnColor: { colorType: 0, color: '#4A3728' }
+}
+
+export default {
+
+  name: 'PageConfigurationNew',
+
+  components: { FeaturedGoodsPicker },
+
+  data () {
+
+    return {
+
+      type: '0',
+
+      action: process.env.VUE_APP_API_PREFIX + '/public/upload',
+
+      resourcePath: '',
+
+      defaultBanner: require('@/assets/images/banner_defult.png'),
+
+      defaultBgImg: require('@/assets/images/background_defult.png'),
+
+      defaultSubmitBtnIcon: require('@/assets/images/submit_btn_defult2.png'),
+
+      pageBgDefaultColor: PAGE_DEFAULTS.pageBg.bgColor,
+
+      useThisVersion: false,
+
+      versionSwitchLoading: false,
+
+      anchorPageVersion: 'v2',
+
+      featuredPickerIndex: 0,
+
+      subCats: ['鏅鸿兘闂ㄩ攣', '椹《', '娴撮湼', '鑺辨磼', '鏅捐。鏋�'],
+
+      mockRows: [
+
+        { categoryName: '鏅鸿兘瀹跺眳', name: '涔濈墽/ZS860 (鍑�鐣孶ltra)', zdPrice: '4,899', price: '3,300' }
+
+      ],
+
+      productList: [
+
+        { img: require('@/assets/images/product/1a.png'), brandName: '甯岀', name: 'QX3 杞诲ア鐗�', price: '2299' },
+
+        { img: require('@/assets/images/product/2a.png'), brandName: '灏忕墽', name: 'S23 Pro', price: '2999' },
+
+        { img: require('@/assets/images/product/3a.png'), brandName: '涔濈墽', name: 'ZS860', price: '4899' },
+
+        { img: require('@/assets/images/product/4a.png'), brandName: '鐟炲皵鐗�', name: 'M3 鏃楄埌', price: '3299' },
+
+        { img: require('@/assets/images/product/5a.png'), brandName: '鎭掓磥', name: 'Q9 鏅鸿兘', price: '3599' }
+
+      ],
+
+      pkPreviewProducts: {
+
+        left: { name: 'R6S 鐭ュ懗2.0', zdPrice: '4500', price: '6299', img: require('@/assets/images/product/1a.png') },
+
+        right: { name: 'TR Pro 鑷冲皧鐗�', zdPrice: '4500', price: '6299', img: require('@/assets/images/product/2a.png') }
+
+      },
+
+      pkPreviewParams: [
+
+        { name: '鍐插姏绛夌骇', val: '5.0绾�' },
+
+        { name: '鍔犵儹鏂瑰紡', val: '鍗崇儹寮�' }
+
+      ],
+
+      searchData: [
+
+        { name: '鏅鸿兘椹《 甯岀QX3-杞诲ア鐗�', price: '2299' },
+
+        { name: '鏅鸿兘椹《 鐟炲皵鐗筂3 鏃楄埌', price: '3299' },
+
+        { name: '鏅鸿兘椹《 涔濈墽ZS860', price: '4899' }
+
+      ],
+
+      form: {
+
+        rangeSize: '750',
+
+        pageBg: { ...PAGE_DEFAULTS.pageBg },
+
+        topImg: { type: 0, img: '', imgurl: '' },
+
+        submitBtnIcon: { ...PAGE_DEFAULTS.submitBtnIcon },
+
+        submitBtnBg: { ...PAGE_DEFAULTS.submitBtnBg },
+
+        submitBtnColor: { ...PAGE_DEFAULTS.submitBtnColor },
+
+        pkImg: { type: 0, img: '', imgurl: '' },
+
+        theme: { ...PAGE_DEFAULTS.theme },
+
+        table: { ...PAGE_DEFAULTS.table },
+
+        featured: [
+
+          { goodsId: '', prefixUrl: '', imgurl: '', imgaddr: '', img: '', title: '', tags: ['', ''] },
+
+          { goodsId: '', prefixUrl: '', imgurl: '', imgaddr: '', img: '', title: '', tags: ['', ''] }
+
+        ],
+
+        search: { ...PAGE_DEFAULTS.search },
+
+        productList: { ...PAGE_DEFAULTS.productList },
+
+        pk: { ...PAGE_DEFAULTS.pk }
+
+      }
+
+    }
+
+  },
+
+  computed: {
+
+    topImgUrl () {
+
+      if (Number(this.form.topImg.type) === 0) return this.defaultBanner
+
+      return this.form.topImg.imgurl || this.defaultBanner
+
+    },
+
+    submitBtnIconPreviewUrl () {
+
+      const cfg = this.form.submitBtnIcon || {}
+
+      if (Number(cfg.type) !== 1) {
+
+        return this.defaultSubmitBtnIcon
+
+      }
+
+      if (cfg.imgurl) {
+
+        if (/^https?:\/\//i.test(cfg.imgurl)) {
+
+          return cfg.imgurl
+
+        }
+
+        if (this.resourcePath && cfg.imgurl.indexOf(this.resourcePath) !== 0) {
+
+          return this.resourcePath + String(cfg.imgurl).replace(/^\//, '')
+
+        }
+
+        return cfg.imgurl
+
+      }
+
+      if (cfg.img && this.resourcePath) {
+
+        return this.resourcePath + String(cfg.img).replace(/^\//, '')
+
+      }
+
+      return this.defaultSubmitBtnIcon
+
+    },
+
+    submitBtnStyle () {
+
+      const bgCfg = this.form.submitBtnBg || {}
+
+      const colorCfg = this.form.submitBtnColor || {}
+
+      const style = {
+
+        color: this.getEffectiveColor(
+
+          colorCfg.colorType,
+
+          colorCfg.color,
+
+          PAGE_DEFAULTS.submitBtnColor.color
+
+        )
+
+      }
+
+      if (Number(bgCfg.bgType) !== 1) {
+
+        style.background = 'rgba(255, 255, 255, 1)'
+
+      } else {
+
+        style.background = this.withAlpha(bgCfg.bgColor || '#FFFFFF', bgCfg.bgAlpha)
+
+      }
+
+      return style
+
+    },
+
+    previewPageBgStyle () {
+
+      const pageBg = this.form.pageBg || {}
+
+      if (Number(pageBg.mode) !== 2 || !pageBg.imgurl) return {}
+
+      return {
+
+        backgroundImage: `url(${pageBg.imgurl})`,
+
+        backgroundSize: 'cover',
+
+        backgroundPosition: 'center top'
+
+      }
+
+    },
+
+    contentStyle () {
+
+      const pageBg = this.form.pageBg || {}
+
+      const mode = Number(pageBg.mode) || 0
+
+      const style = { width: (this.form.rangeSize || 750) + 'px', ...this.previewPageBgStyle }
+
+      if (mode === 2) return style
+
+      const bg = mode === 1
+
+        ? (pageBg.bgColor || PAGE_DEFAULTS.pageBg.bgColor)
+
+        : PAGE_DEFAULTS.pageBg.bgColor
+
+      const alpha = Math.round(Number(pageBg.alpha || 100) * 2.55).toString(16).padStart(2, '0')
+
+      style.backgroundColor = bg + alpha
+
+      return style
+
+    },
+
+    previewMainBg () {
+
+      const pageBg = this.form.pageBg || {}
+
+      const mode = Number(pageBg.mode) || 0
+
+      const bg = mode === 1
+
+        ? (pageBg.bgColor || PAGE_DEFAULTS.pageBg.bgColor)
+
+        : PAGE_DEFAULTS.pageBg.bgColor
+
+      const alpha = Math.round(Number(pageBg.alpha || 100) * 2.55).toString(16).padStart(2, '0')
+
+      return bg + alpha
+
+    },
+
+    themeStyle () {
+
+      const theme = this.form.theme || {}
+
+      const bg = this.getEffectiveColor(theme.bgType, theme.bgColor, PAGE_DEFAULTS.theme.bgColor)
+
+      const color = this.getEffectiveColor(theme.colorType, theme.color, PAGE_DEFAULTS.theme.color)
+
+      return { background: bg, color }
+
+    },
+
+    homeTopSectionStyle () {
+
+      const { background: bg, color } = this.themeStyle
+
+      return {
+
+        background: bg,
+
+        color,
+
+        '--home-top-bg': bg
+
+      }
+
+    },
+
+    pkShowcaseTheme () {
+
+      const pk = this.form.pk || {}
+
+      const primary = this.themeStyle.background
+
+      const accent = this.getPkThemeColor()
+
+      const gold = '#FFD88A'
+
+      return {
+
+        '--pk-show-primary': primary,
+
+        '--pk-show-accent': accent,
+
+        '--pk-show-gold': gold,
+
+        '--pk-show-primary-soft': this.hexToRgba(primary, 0.22),
+
+        '--pk-show-accent-soft': this.hexToRgba(accent, 0.38),
+
+        '--pk-show-gold-soft': this.hexToRgba(gold, 0.5),
+
+        '--pk-show-accent-faint': this.hexToRgba(accent, 0.12)
+
+      }
+
+    },
+
+    previewFeaturedDisplay () {
+
+      const demos = [
+
+        { title: '鏅鸿兘闂ㄩ攣 X9 Pro', tags: ['鍏ㄨ嚜鍔�', '3D浜鸿劯'] },
+
+        { title: '鏅鸿兘椹《 鑷冲皧鐗�', tags: ['鍗崇儹寮�', '铏瑰惛鍐叉按'] }
+
+      ]
+
+      const list = (this.form.featured || []).slice(0, 2)
+
+      return list.map((item, i) => ({
+
+        ...demos[i],
+
+        ...item,
+
+        title: item.title || demos[i].title,
+
+        tags: (item.tags && item.tags.some(t => t)) ? item.tags : demos[i].tags
+
+      }))
+
+    },
+
+    tableHeaderStyle () {
+
+      return this.themeStyle
+
+    },
+
+    rowStyle () {
+
+      const t = this.form.table || {}
+
+      return { background: this.getEffectiveColor(t.rowBgType, t.rowBg, PAGE_DEFAULTS.table.rowBg) }
+
+    },
+
+    searchStyle () {
+
+      const s = this.form.search || {}
+
+      const defaultBg = 'rgba(255, 255, 255, 0.92)'
+
+      if (Number(s.bgType) !== 1) return { background: defaultBg }
+
+      return { background: this.withAlpha(s.bgColor || '#FFFFFF', s.bgAlpha) }
+
+    },
+
+    productListStyle () {
+
+      const p = this.form.productList || {}
+
+      if (Number(p.bgType) !== 1) return { background: PAGE_DEFAULTS.productList.bgColor }
+
+      return { background: this.withAlpha(p.bgColor || '#FFFFFF', p.bgAlpha) }
+
+    },
+
+    productCardStyle () {
+
+      return this.productListStyle
+
+    },
+
+    pkVsPreviewColor () {
+
+      const pk = this.form.pk || {}
+
+      return this.getEffectiveColor(pk.vsColorType, pk.vsColor, PAGE_DEFAULTS.pk.vsColor)
+
+    },
+
+    uploadHeaders () {
+
+      const token = Cookies.get('eva-auth-token')
+
+      return token ? { 'eva-auth-token': token } : {}
+
+    }
+
+  },
+
+  created () {
+
+    this.loadConfig()
+
+  },
+
+  methods: {
+
+    loadConfig () {
+
+      getByLoginAnchor().then(res => {
+
+        if (res && res.resourcePath) this.resourcePath = res.resourcePath
+
+        this.anchorPageVersion = (res && res.anchorPageVersion) || 'v2'
+
+        this.useThisVersion = this.anchorPageVersion === 'v3'
+
+        if (res && res.anchorParam) {
+
+          const param = JSON.parse(res.anchorParam)
+
+          const hadPageBg = !!param.pageBg
+
+          const hadTheme = !!param.theme
+
+          Object.keys(this.form).forEach(key => {
+
+            if (param[key]) this.form[key] = param[key]
+
+          })
+
+          if (!hadPageBg) {
+
+            if (param.main) this.form.main = param.main
+
+            if (param.adImg) this.form.adImg = param.adImg
+
+            this.migratePageBgFromLegacy()
+
+          } else {
+
+            delete this.form.main
+
+            delete this.form.adImg
+
+          }
+
+          if (!hadTheme) {
+
+            if (param.header) this.form.header = param.header
+
+            if (param.table) this.form.table = { ...this.form.table, ...param.table }
+
+            this.migrateThemeFromLegacy()
+
+          } else {
+
+            delete this.form.header
+
+            this.stripLegacyThemeFields()
+
+          }
+
+          this.normalizeFeatured()
+
+          this.normalizeConfig()
+
+        }
+
+      })
+
+    },
+
+    onUseThisVersion () {
+
+      if (this.useThisVersion || this.versionSwitchLoading) return
+
+      this.versionSwitchLoading = true
+
+      updateAnchorPageVersion({ anchorPageVersion: 'v3' })
+
+        .then(() => {
+
+          this.anchorPageVersion = 'v3'
+
+          this.useThisVersion = true
+
+          this.$message.success('宸插惎鐢� V3 閰嶇疆')
+
+        })
+
+        .catch(e => {
+
+          this.$tip.apiFailed(e)
+
+        })
+
+        .finally(() => {
+
+          this.versionSwitchLoading = false
+
+        })
+
+    },
+
+    normalizeConfig () {
+
+      const assignDefaults = (target, defaults) => {
+
+        Object.keys(defaults).forEach(key => {
+
+          if (target[key] === undefined || target[key] === null || target[key] === '') {
+
+            this.$set(target, key, defaults[key])
+
+          }
+
+        })
+
+      }
+
+      if (!this.form.pageBg) {
+
+        this.$set(this.form, 'pageBg', { ...PAGE_DEFAULTS.pageBg })
+
+      }
+
+      assignDefaults(this.form.pageBg, PAGE_DEFAULTS.pageBg)
+
+      if (!this.form.theme) {
+
+        this.$set(this.form, 'theme', { ...PAGE_DEFAULTS.theme })
+
+      }
+
+      assignDefaults(this.form.theme, PAGE_DEFAULTS.theme)
+
+      assignDefaults(this.form.table, PAGE_DEFAULTS.table)
+
+      assignDefaults(this.form.search, PAGE_DEFAULTS.search)
+
+      assignDefaults(this.form.productList, PAGE_DEFAULTS.productList)
+
+      assignDefaults(this.form.pk, PAGE_DEFAULTS.pk)
+
+      if (!this.form.submitBtnIcon) {
+
+        this.$set(this.form, 'submitBtnIcon', { ...PAGE_DEFAULTS.submitBtnIcon })
+
+      }
+
+      assignDefaults(this.form.submitBtnIcon, PAGE_DEFAULTS.submitBtnIcon)
+
+      if (this.form.submitBtnIcon.type == null) {
+
+        this.form.submitBtnIcon.type = this.form.submitBtnIcon.imgurl ? 1 : 0
+
+      }
+
+      if (!this.form.submitBtnBg) {
+
+        this.$set(this.form, 'submitBtnBg', { ...PAGE_DEFAULTS.submitBtnBg })
+
+      }
+
+      assignDefaults(this.form.submitBtnBg, PAGE_DEFAULTS.submitBtnBg)
+
+      if (!this.form.submitBtnColor) {
+
+        this.$set(this.form, 'submitBtnColor', { ...PAGE_DEFAULTS.submitBtnColor })
+
+      }
+
+      assignDefaults(this.form.submitBtnColor, PAGE_DEFAULTS.submitBtnColor)
+
+      if (this.form.pk.bgColorType == null) {
+
+        if (this.form.pk.vsColor && !this.form.pk.bgColor) {
+
+          this.form.pk.bgColor = this.form.pk.vsColor
+
+          this.form.pk.bgColorType = this.form.pk.vsColorType != null ? this.form.pk.vsColorType : 1
+
+        } else {
+
+          this.form.pk.bgColorType = PAGE_DEFAULTS.pk.bgColorType
+
+        }
+
+      }
+
+      if (this.form.topImg && this.form.topImg.type == null) {
+
+        this.form.topImg.type = this.form.topImg.imgurl ? 1 : 0
+
+      }
+
+      if (this.form.pageBg.mode == null) {
+
+        this.form.pageBg.mode = this.form.pageBg.imgurl ? 2 : PAGE_DEFAULTS.pageBg.mode
+
+      }
+
+    },
+
+    migrateThemeFromLegacy () {
+
+      const base = { ...PAGE_DEFAULTS.theme }
+
+      const t = this.form.table || {}
+
+      const h = this.form.header || {}
+
+      const theme = { ...base }
+
+      if (Number(t.headerBgType) === 1) {
+
+        theme.bgType = 1
+
+        theme.bgColor = t.headerBg || base.bgColor
+
+      } else if (Number(h.bgType) === 1) {
+
+        theme.bgType = 1
+
+        theme.bgColor = h.bgColor || base.bgColor
+
+      }
+
+      if (Number(t.headerColorType) === 1) {
+
+        theme.colorType = 1
+
+        theme.color = t.headerColor || base.color
+
+      } else if (Number(h.colorType) === 1) {
+
+        theme.colorType = 1
+
+        theme.color = h.color || base.color
+
+      }
+
+      this.form.theme = theme
+
+      delete this.form.header
+
+      this.stripLegacyThemeFields()
+
+    },
+
+    stripLegacyThemeFields () {
+
+      if (!this.form.table) return
+
+      delete this.form.table.headerBgType
+
+      delete this.form.table.headerBg
+
+      delete this.form.table.headerColorType
+
+      delete this.form.table.headerColor
+
+    },
+
+    migratePageBgFromLegacy () {
+
+      const main = this.form.main || {}
+
+      const ad = this.form.adImg || {}
+
+      const base = { ...PAGE_DEFAULTS.pageBg }
+
+      if (Number(ad.type) === 1 && (ad.imgurl || ad.img)) {
+
+        this.form.pageBg = { ...base, mode: 2, img: ad.img || '', imgurl: ad.imgurl || '' }
+
+      } else if (Number(main.bgType) === 1) {
+
+        this.form.pageBg = {
+
+          ...base,
+
+          mode: 1,
+
+          bgColor: main.bgColor || PAGE_DEFAULTS.pageBg.bgColor,
+
+          alpha: main.alpha != null ? String(main.alpha) : PAGE_DEFAULTS.pageBg.alpha
+
+        }
+
+      } else {
+
+        this.form.pageBg = { ...base }
+
+      }
+
+      delete this.form.main
+
+      delete this.form.adImg
+
+    },
+
+    getEffectiveColor (type, custom, fallback) {
+
+      return Number(type) === 1 ? (custom || fallback) : fallback
+
+    },
+
+    withAlpha (hex, alpha) {
+
+      const color = (hex || '#FFFFFF').replace('#', '')
+
+      if (color.length !== 6) return hex || '#FFFFFF'
+
+      const a = Math.round(Number(alpha || 100) * 2.55).toString(16).padStart(2, '0')
+
+      return `#${color}${a}`
+
+    },
+
+    onPageBgModeChange (val) {
+
+      const mode = Number(val)
+
+      if (mode === 0) {
+
+        this.form.pageBg.bgColor = PAGE_DEFAULTS.pageBg.bgColor
+
+        this.form.pageBg.alpha = PAGE_DEFAULTS.pageBg.alpha
+
+        this.form.pageBg.img = ''
+
+        this.form.pageBg.imgurl = ''
+
+      } else if (mode === 1) {
+
+        this.form.pageBg.img = ''
+
+        this.form.pageBg.imgurl = ''
+
+      } else if (mode === 2) {
+
+        this.form.pageBg.bgColor = PAGE_DEFAULTS.pageBg.bgColor
+
+        this.form.pageBg.alpha = PAGE_DEFAULTS.pageBg.alpha
+
+      }
+
+    },
+
+    onTopImgTypeChange (val) {
+
+      if (Number(val) === 0) {
+
+        this.form.topImg.img = ''
+
+        this.form.topImg.imgurl = ''
+
+      }
+
+    },
+
+    onSubmitBtnIconTypeChange (val) {
+
+      if (Number(val) === 0) {
+
+        this.form.submitBtnIcon.img = ''
+
+        this.form.submitBtnIcon.imgurl = ''
+
+      }
+
+    },
+
+    onSubmitBtnBgTypeChange (val) {
+
+      if (Number(val) === 0) {
+
+        this.form.submitBtnBg.bgColor = PAGE_DEFAULTS.submitBtnBg.bgColor
+
+        this.form.submitBtnBg.bgAlpha = PAGE_DEFAULTS.submitBtnBg.bgAlpha
+
+      }
+
+    },
+
+    onSubmitBtnColorTypeChange (val) {
+
+      if (Number(val) === 0) {
+
+        this.form.submitBtnColor.color = PAGE_DEFAULTS.submitBtnColor.color
+
+      }
+
+    },
+
+    getPkThemeColor () {
+
+      const pk = this.form.pk || {}
+
+      return this.getEffectiveColor(pk.bgColorType, pk.bgColor, PAGE_DEFAULTS.pk.bgColor)
+
+    },
+
+    onPkBgColorTypeChange (val) {
+
+      if (Number(val) === 0) this.form.pk.bgColor = PAGE_DEFAULTS.pk.bgColor
+
+    },
+
+    onThemeBgTypeChange (val) {
+
+      if (Number(val) === 0) this.form.theme.bgColor = PAGE_DEFAULTS.theme.bgColor
+
+    },
+
+    onThemeColorTypeChange (val) {
+
+      if (Number(val) === 0) this.form.theme.color = PAGE_DEFAULTS.theme.color
+
+    },
+
+    onTableRowBgTypeChange (val) {
+
+      if (Number(val) === 0) this.form.table.rowBg = PAGE_DEFAULTS.table.rowBg
+
+    },
+
+    onSearchBgTypeChange (val) {
+
+      if (Number(val) === 0) {
+
+        this.form.search.bgColor = PAGE_DEFAULTS.search.bgColor
+
+        this.form.search.bgAlpha = PAGE_DEFAULTS.search.bgAlpha
+
+      }
+
+    },
+
+    onProductListBgTypeChange (val) {
+
+      if (Number(val) === 0) {
+
+        this.form.productList.bgColor = PAGE_DEFAULTS.productList.bgColor
+
+        this.form.productList.bgAlpha = PAGE_DEFAULTS.productList.bgAlpha
+
+      }
+
+    },
+
+    onPkVsColorTypeChange (val) {
+
+      if (Number(val) === 0) this.form.pk.vsColor = PAGE_DEFAULTS.pk.vsColor
+
+    },
+
+    normalizeFeatured () {
+
+      if (!Array.isArray(this.form.featured)) {
+
+        this.form.featured = []
+
+      }
+
+      while (this.form.featured.length < 2) {
+
+        this.form.featured.push({ goodsId: '', prefixUrl: '', imgurl: '', imgaddr: '', img: '', title: '', tags: ['', ''] })
+
+      }
+
+      this.form.featured = this.form.featured.slice(0, 2).map(item => {
+
+        const feat = { ...item }
+
+        if (!Array.isArray(feat.tags)) feat.tags = ['', '']
+
+        while (feat.tags.length < 2) feat.tags.push('')
+
+        feat.tags = feat.tags.slice(0, 2)
+
+        return feat
+
+      })
+
+    },
+
+    openFeaturedPicker (index) {
+
+      this.featuredPickerIndex = index
+
+      this.$refs.featuredGoodsPicker.open()
+
+    },
+
+    onFeaturedGoodsSelect (goods) {
+
+      const feat = this.form.featured[this.featuredPickerIndex]
+
+      if (!feat || !goods) return
+
+      feat.goodsId = goods.id
+
+      feat.title = goods.name || ''
+
+      feat.prefixUrl = goods.prefixUrl || ''
+
+      feat.imgurl = goods.imgurl || ''
+
+      feat.imgaddr = ''
+
+      feat.img = ''
+
+    },
+
+    clearFeaturedGoods (index) {
+
+      const feat = this.form.featured[index]
+
+      if (!feat) return
+
+      feat.goodsId = ''
+
+      feat.title = ''
+
+      feat.prefixUrl = ''
+
+      feat.imgurl = ''
+
+      feat.imgaddr = ''
+
+      feat.img = ''
+
+    },
+
+    uploadSuccess (res, file, field) {
+
+      if (!res) {
+
+        this.$message.error('涓婁紶澶辫触')
+
+        return
+
+      }
+
+      if (!(res.code === 200 || res.success) || !res.data) {
+
+        this.$message.error((res && res.message) || '涓婁紶澶辫触')
+
+        return
+
+      }
+
+      const data = res.data
+
+      if (!this.form[field]) {
+
+        this.$message.error('閰嶇疆瀛楁涓嶅瓨鍦�')
+
+        return
+
+      }
+
+      if (field === 'pageBg') {
+
+        this.form.pageBg.mode = 2
+
+        this.form.pageBg.img = data.imgaddr || ''
+
+        this.form.pageBg.imgurl = data.url || ((this.resourcePath || '') + (data.imgaddr || ''))
+
+      } else {
+
+        this.form[field].type = 1
+
+        this.form[field].img = data.imgaddr || ''
+
+        this.form[field].imgurl = data.url || ((this.resourcePath || '') + (data.imgaddr || ''))
+
+      }
+
+      this.$message.success('涓婁紶鎴愬姛')
+
+    },
+
+    uploadError () {
+
+      this.$message.error('涓婁紶澶辫触锛岃妫�鏌ョ櫥褰曠姸鎬佹垨缃戠粶')
+
+    },
+
+    featuredPreviewUrl (item) {
+      if (!item) return ''
+      if (item.prefixUrl && item.imgurl && !/^https?:\/\//i.test(item.imgurl)) {
+        return item.prefixUrl + item.imgurl
+      }
+      if (item.imgurl && /^https?:\/\//i.test(item.imgurl)) {
+        const secondHttp = item.imgurl.indexOf('http', item.imgurl.indexOf('://') + 3)
+        if (secondHttp > 0) return item.imgurl.substring(secondHttp)
+        return item.imgurl
+      }
+      if (item.imgaddr) return (this.resourcePath || '') + item.imgaddr
+      return item.imgurl || ''
+    },
+
+    pkPreviewProduct (side) {
+      return this.pkPreviewProducts[side] || this.pkPreviewProducts.left
+    },
+
+    hexToRgba (hex, alpha) {
+      const h = String(hex || '').replace('#', '')
+      if (h.length !== 6) return `rgba(74, 55, 40, ${alpha})`
+      const r = parseInt(h.slice(0, 2), 16)
+      const g = parseInt(h.slice(2, 4), 16)
+      const b = parseInt(h.slice(4, 6), 16)
+      return `rgba(${r}, ${g}, ${b}, ${alpha})`
+    },
+
+    submit () {
+
+      if (Number(this.form.rangeSize) < 750 || Number(this.form.rangeSize) > 1200) {
+
+        this.$message.warning('灏哄鑼冨洿750-1200')
+
+        return
+
+      }
+
+      const payload = { ...this.form }
+
+      delete payload.main
+
+      delete payload.adImg
+
+      delete payload.header
+
+      this.stripLegacyThemeFields()
+
+      if (payload.table) {
+
+        delete payload.table.headerBgType
+
+        delete payload.table.headerBg
+
+        delete payload.table.headerColorType
+
+        delete payload.table.headerColor
+
+      }
+
+      renewAnchorUpdate({
+        anchorParam: JSON.stringify(payload),
+        anchorPageVersion: this.anchorPageVersion
+      }).then(() => {
+
+        this.$message.success('淇濆瓨鎴愬姛')
+
+      })
+
+    }
+
+  }
+
+}
+
+</script>
+
+
+
+<style lang="scss" scoped>
+
+$brown: #4A3728;
+
+$brown-dark: #3D2E22;
+
+$cream: #E8DCC8;
+
+$panel: #F7F0E6;
+
+$orange: #FF8C42;
+
+$gold: #FFD88A;
+
+$row-bg: #F5EFE6;
+
+
+
+.anchor-pz {
+
+  display: flex;
+
+  padding: 20px;
+
+  background: #f5f5f5;
+
+
+
+  .prediv {
+
+    width: 420px;
+
+    margin-right: 20px;
+
+    flex-shrink: 0;
+
+
+
+    .prediv-content {
+
+      overflow: auto;
+
+      scrollbar-width: none;
+
+      -ms-overflow-style: none;
+
+      &::-webkit-scrollbar {
+
+        width: 0;
+
+        height: 0;
+
+        display: none;
+
+      }
+
+      height: 720px;
+
+      background: #eee;
+
+      border-radius: 12px;
+
+      padding: 8px;
+
+
+
+      .content {
+
+        transform: scale(0.52);
+
+        transform-origin: top left;
+
+        padding: 0;
+
+        border-radius: 0;
+
+        min-height: 1500px;
+
+        box-sizing: border-box;
+
+        position: relative;
+
+        overflow: hidden;
+
+      }
+
+    }
+
+  }
+
+
+
+  .h5-preview {
+    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
+    color: $brown;
+  }
+  
+  .pv_banner_wrap {
+    position: relative;
+    margin-bottom: 0;
+  
+    .pv_banner_img {
+      width: 100%;
+      display: block;
+    }
+  
+    .pv_user_bar {
+      position: absolute;
+      right: 12px;
+      top: 12px;
+      font-size: 12px;
+      color: $brown;
+      z-index: 2;
+    }
+  }
+  
+  .pv_nav_back {
+    position: absolute;
+    left: 12px;
+    top: 50%;
+    transform: translateY(-50%);
+    width: 48px;
+    height: 48px;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    font-size: 36px;
+    line-height: 1;
+    color: $brown;
+    z-index: 5;
+    opacity: 0.7;
+  }
+  
+  .pv_sticky_header {
+    position: relative;
+    z-index: 4;
+  }
+  
+  .pv_submit_row {
+    padding: 10px 14px 8px;
+  }
+  
+  .pv_submit_btn {
+    display: inline-flex;
+    align-items: center;
+    gap: 5px;
+    padding: 7px 16px;
+    border-radius: 18px;
+    font-size: 12px;
+    font-weight: 600;
+    box-shadow: 0 2px 8px rgba(74, 55, 40, 0.1);
+  }
+  
+  .pv_submit_icon_img {
+    width: 16px;
+    height: 16px;
+    object-fit: contain;
+    flex-shrink: 0;
+  }
+
+  .pv_submit_btn.submit_btn--icon {
+    display: inline-flex;
+    align-items: center;
+    gap: 5px;
+  }
+
+  .pv_submit_text {
+    color: inherit;
+  }
+  
+  .pv_home_top {
+    position: relative;
+    margin: 8px 12px 0;
+    padding: 12px 12px 0;
+    border-radius: 16px 16px 0 0;
+    overflow: visible;
+    z-index: 1;
+    --home-top-bg: #4A3728;
+  
+    &::after {
+      content: '';
+      position: absolute;
+      top: -18px;
+      left: -24px;
+      right: -24px;
+      bottom: -18px;
+      border-radius: 24px 24px 0 0;
+      background: radial-gradient(
+        ellipse 88% 92% at 50% 48%,
+        var(--home-top-bg) 0%,
+        rgba(74, 55, 40, 0.55) 55%,
+        rgba(74, 55, 40, 0.15) 78%,
+        transparent 100%
+      );
+      filter: blur(10px);
+      z-index: -1;
+      pointer-events: none;
+    }
+  
+    &--compact {
+      padding-bottom: 12px;
+      border-radius: 16px;
+  
+      &::after {
+        bottom: 0;
+        border-radius: 24px;
+      }
+  
+      .pv_featured { margin-bottom: 0; }
+    }
+  }
+  
+  .pv_order_bar {
+    display: flex;
+    justify-content: space-between;
+    align-items: stretch;
+    padding: 4px 4px 12px;
+    font-size: 14px;
+    position: relative;
+    z-index: 1;
+  }
+  
+  .pv_order_left {
+    display: flex;
+    align-items: center;
+    gap: 4px;
+    flex: 1;
+    min-width: 0;
+  }
+  
+  .pv_order_right {
+    display: flex;
+    align-items: center;
+    flex-shrink: 0;
+    margin-left: 8px;
+  }
+  
+  .pv_order_no_row { display: flex; align-items: center; gap: 6px; }
+  
+  .pv_remain_inline {
+    display: flex;
+    align-items: center;
+    gap: 6px;
+    margin-left: 10px;
+    font-size: 12px;
+    opacity: 0.85;
+    white-space: nowrap;
+  }
+  
+  .pv_remain_val { font-weight: 600; color: $gold; }
+  
+  .pv_order_label { opacity: 0.9; font-size: 13px; }
+  
+  .pv_order_currency, .pv_budget_val { font-weight: 700; font-size: 15px; }
+  
+  .pv_budget_val { font-size: 16px; }
+  
+  .pv_order_no { font-weight: 600; font-size: 14px; }
+  
+  .pv_featured {
+    display: flex;
+    align-items: center;
+    background: linear-gradient(180deg, #FFF9EC 0%, #FAF0E0 100%);
+    padding: 14px;
+    margin: 0 0 10px;
+    border-radius: 20px;
+    box-shadow: 0 2px 10px rgba(0, 0, 0, 0.12);
+    position: relative;
+    z-index: 1;
+  }
+  
+  .pv_featured_left {
+    flex-shrink: 0;
+    width: 218px;
+    min-width: 218px;
+    display: flex;
+    flex-direction: column;
+    justify-content: center;
+    text-align: center;
+    padding-right: 12px;
+    border-right: 1px solid rgba(74, 55, 40, 0.08);
+    margin-right: 10px;
+  }
+  
+  .pv_featured_title {
+    font-size: 22px;
+    font-weight: 700;
+    color: #333;
+    font-family: Georgia, 'Times New Roman', serif;
+    letter-spacing: 1px;
+    line-height: 1.15;
+    margin-bottom: 8px;
+  }
+  
+  .pv_featured_sub { font-size: 10px; color: #aaa; }
+  
+  .pv_featured_products {
+    flex: 1;
+    display: flex;
+    min-width: 0;
+  }
+  
+  .pv_featured_item {
+    flex: 1;
+    display: flex;
+    align-items: center;
+    gap: 8px;
+    padding: 2px 6px;
+    min-width: 0;
+  
+    &:not(:last-child) {
+      border-right: 1px solid rgba(74, 55, 40, 0.08);
+      margin-right: 6px;
+      padding-right: 10px;
+    }
+  }
+  
+  .pv_featured_img_wrap {
+    width: 44px;
+    height: 72px;
+    flex-shrink: 0;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+  }
+  
+  .pv_featured_img { width: 44px; height: 72px; object-fit: contain; }
+  
+  .pv_featured_img_ph {
+    width: 36px;
+    height: 60px;
+    background: rgba(74, 55, 40, 0.06);
+    border-radius: 4px;
+  }
+  
+  .pv_featured_info {
+    flex: 1;
+    min-width: 0;
+    display: flex;
+    flex-direction: column;
+    gap: 6px;
+  }
+  
+  .pv_featured_name {
+    font-size: 12px;
+    font-weight: 600;
+    color: #333;
+    overflow: hidden;
+    text-overflow: ellipsis;
+    white-space: nowrap;
+  }
+  
+  .pv_featured_tag_row { display: flex; gap: 8px; flex-wrap: wrap; }
+  
+  .pv_tag {
+    font-size: 11px;
+  
+    &.pv_tag_dim { color: #888; }
+    &.pv_tag_blue { color: #4A8FD4; font-weight: 500; }
+    &.pv_tag_green { color: #3CB371; font-weight: 500; }
+  }
+  
+  .pv_table_head_wrap {
+    position: relative;
+    z-index: 3;
+  }
+  
+  .pv_shop_table_head {
+    display: grid;
+    grid-template-columns: 1.1fr 2fr 0.9fr 0.9fr;
+    padding: 11px 14px 12px;
+    font-size: 13px;
+    font-weight: 600;
+    position: relative;
+    z-index: 1;
+  }
+  
+  .pv_product_shop_table_head {
+    grid-template-columns: 1fr 1.4fr 1fr;
+  }
+  
+  .pv_col { min-width: 0; }
+  
+  .pv_col_cat { display: flex; align-items: center; gap: 4px; }
+  
+  .pv_arrow { font-size: 10px; opacity: 0.8; }
+  
+  .pv_product_search_col { display: flex; align-items: center; }
+  
+  .pv_product_search_inline {
+    flex: 1;
+    display: flex;
+    align-items: center;
+    gap: 6px;
+    border-radius: 16px;
+    padding: 5px 10px;
+    min-width: 0;
+    font-size: 12px;
+    color: rgba(74, 55, 40, 0.5);
+  }
+  
+  .pv_search_ico { font-size: 12px; flex-shrink: 0; }
+  
+  .pv_search_divider {
+    width: 1px;
+    height: 14px;
+    background: rgba(74, 55, 40, 0.12);
+    flex-shrink: 0;
+  }
+  
+  .pv_search_ph { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
+  
+  .pv_product_cat_col {
+    display: flex;
+    justify-content: flex-end;
+  
+    &--span { grid-column: 3 / -1; }
+  }
+  
+  .pv_product_cat_inner {
+    display: inline-flex;
+    align-items: center;
+    gap: 6px;
+    max-width: 100%;
+  }
+  
+  .pv_cat_line {
+    font-size: 13px;
+    font-weight: 600;
+    overflow: hidden;
+    text-overflow: ellipsis;
+    white-space: nowrap;
+  }
+  
+  .pv_shop_table {
+    margin: 10px 12px;
+    position: relative;
+  }
+  
+  .pv_shop_table_body { padding: 0 10px 10px; }
+  
+  .pv_shop_row { margin-bottom: 10px; }
+  
+  .pv_shop_row_main {
+    display: flex;
+    gap: 10px;
+    font-size: 13px;
+    min-height: 52px;
+  }
+  
+  .pv_row_cat_cell {
+    width: 108px;
+    flex-shrink: 0;
+    display: flex;
+    align-items: center;
+    gap: 6px;
+    padding: 10px 8px;
+    background: #fff;
+    border-radius: 14px;
+    box-shadow: 0 2px 8px rgba(74, 55, 40, 0.08);
+  }
+  
+  .pv_cat_icon_ph {
+    width: 20px;
+    height: 20px;
+    background: $panel;
+    border-radius: 4px;
+    flex-shrink: 0;
+  }
+  
+  .pv_row_info_group {
+    flex: 1;
+    display: grid;
+    grid-template-columns: 2fr 0.9fr 0.9fr;
+    gap: 10px;
+    align-items: center;
+    padding: 10px;
+    background: #fff;
+    border-radius: 14px;
+    box-shadow: 0 2px 8px rgba(74, 55, 40, 0.08);
+  }
+  
+  .pv_cell_ellipsis {
+    overflow: hidden;
+    text-overflow: ellipsis;
+    white-space: nowrap;
+    min-width: 0;
+  }
+  
+  .pv_shop_row--expand {
+    display: flex;
+    gap: 10px;
+  }
+  
+  .pv_expand_cat {
+    position: relative;
+    width: 88px;
+    flex-shrink: 0;
+    border-radius: 14px;
+    padding: 16px 10px;
+    text-align: center;
+    box-shadow: 0 4px 16px rgba(74, 55, 40, 0.12);
+    font-size: 13px;
+    font-weight: 600;
+  }
+  
+  .pv_expand_del {
+    position: absolute;
+    right: -6px;
+    top: -6px;
+    width: 22px;
+    height: 22px;
+    background: $orange;
+    color: #fff;
+    border-radius: 50%;
+    font-size: 16px;
+    line-height: 22px;
+  }
+  
+  .pv_expand_cat_ph {
+    width: 48px;
+    height: 48px;
+    background: $panel;
+    border-radius: 8px;
+    margin: 0 auto 8px;
+  }
+  
+  .pv_expand_panel {
+    flex: 1;
+    border-radius: 14px;
+    padding: 14px;
+    box-shadow: 0 4px 16px rgba(74, 55, 40, 0.12);
+  }
+  
+  .pv_expand_data_row {
+    display: grid;
+    grid-template-columns: 2fr 0.9fr 0.9fr;
+    gap: 10px;
+    font-size: 13px;
+    margin-bottom: 12px;
+    padding-bottom: 10px;
+    border-bottom: 1px solid rgba(74, 55, 40, 0.08);
+  }
+  
+  .pv_expand_model { font-weight: 600; color: $brown; }
+  
+  .pv_expand_zd, .pv_expand_price { text-align: center; }
+  
+  .pv_sub_cat_grid {
+    display: flex;
+    gap: 12px;
+    flex-wrap: wrap;
+    margin-bottom: 12px;
+  }
+  
+  .pv_sub_cat_item {
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    width: 56px;
+    font-size: 11px;
+  
+    .pv_sub_icon {
+      width: 40px;
+      height: 40px;
+      background: $panel;
+      border-radius: 8px;
+      margin-bottom: 4px;
+    }
+  }
+  
+  .pv_expand_search {
+    display: flex;
+    align-items: center;
+    gap: 8px;
+    padding: 10px 14px;
+    border-radius: 24px;
+    font-size: 13px;
+    color: rgba(74, 55, 40, 0.5);
+  }
+  
+  .pv_pk_page {
+    padding: 32px 24px 24px;
+    box-sizing: border-box;
+    max-width: 100%;
+  
+    &--ghost { opacity: 0.35; filter: blur(1px); pointer-events: none; }
+  }
+  
+  .pv_pk_cards {
+    display: grid;
+    grid-template-columns: 1fr auto 1fr;
+    gap: 12px;
+    align-items: start;
+    margin-bottom: 20px;
+  
+    &--ghost { margin-bottom: 0; }
+  }
+  
+  .pv_pk_side {
+    min-width: 0;
+    display: flex;
+    flex-direction: column;
+  
+    &--ghost .pv_pk_showcase_frame {
+      min-height: 120px;
+      background: rgba(255, 255, 255, 0.5);
+      border-radius: 14px;
+    }
+  }
+  
+  .pv_pk_showcase {
+    border-radius: 14px;
+    padding: 3px;
+    background: linear-gradient(
+      135deg,
+      var(--pk-show-accent, #{$orange}) 0%,
+      var(--pk-show-gold, #{$gold}) 42%,
+      var(--pk-show-primary, #{$brown}) 100%
+    );
+    box-shadow: 0 8px 24px var(--pk-show-accent-soft, rgba(255, 140, 66, 0.28));
+  }
+  
+  .pv_pk_showcase_frame {
+    border-radius: 11px;
+    padding: 14px 10px 10px;
+    min-height: 168px;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    background: linear-gradient(165deg, rgba(255, 255, 255, 0.98) 0%, rgba(255, 251, 244, 0.94) 100%);
+    border: 1px solid rgba(255, 255, 255, 0.72);
+  }
+  
+  .pv_pk_visual {
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+  }
+  
+  .pv_pk_img_card {
+    padding: 7px;
+    border-radius: 18px;
+    background: linear-gradient(180deg, #fff 0%, #fffaf3 100%) padding-box,
+      linear-gradient(145deg, var(--pk-show-gold, #{$gold}), var(--pk-show-accent, #{$orange}), var(--pk-show-primary, #{$brown})) border-box;
+    border: 2px solid transparent;
+    box-shadow: 0 10px 24px var(--pk-show-primary-soft, rgba(74, 55, 40, 0.2));
+  }
+  
+  .pv_pk_img {
+    display: block;
+    width: 88px;
+    height: 88px;
+    border-radius: 14px;
+    object-fit: contain;
+    background: #fff;
+  }
+  
+  .pv_pk_reflect {
+    width: 88px;
+    height: 28px;
+    margin-top: 3px;
+    overflow: hidden;
+    mask-image: linear-gradient(to bottom, rgba(0, 0, 0, 0.42) 0%, transparent 88%);
+  
+    img {
+      width: 88px;
+      height: 88px;
+      transform: scaleY(-1);
+      opacity: 0.5;
+      object-fit: contain;
+    }
+  }
+  
+  .pv_pk_ground {
+    width: 72%;
+    height: 8px;
+    margin-top: 2px;
+    border-radius: 50%;
+    background: radial-gradient(ellipse, rgba(74, 55, 40, 0.28) 0%, transparent 72%);
+  }
+  
+  .pv_pk_podium {
+    width: 82%;
+    height: 18px;
+    margin: -2px auto 0;
+    background: linear-gradient(180deg, $brown-dark 0%, $brown 52%, rgba(0, 0, 0, 0.22) 100%);
+    clip-path: polygon(10% 0, 90% 0, 100% 100%, 0 100%);
+  }
+  
+  .pv_pk_detail_head {
+    padding: 9px 10px;
+    border-radius: 10px 10px 0 0;
+    font-size: 12px;
+    font-weight: 600;
+    text-align: center;
+    overflow: hidden;
+    text-overflow: ellipsis;
+    white-space: nowrap;
+  }
+  
+  .pv_pk_detail_body {
+    display: grid;
+    grid-template-columns: 1fr 1fr;
+    gap: 6px;
+    padding: 12px 10px;
+    background: #fff;
+    border: 1px solid rgba(74, 55, 40, 0.1);
+    border-top: none;
+    border-radius: 0 0 10px 10px;
+    box-shadow: 0 4px 12px rgba(74, 55, 40, 0.08);
+  }
+  
+  .pv_pk_price_cell { text-align: center; }
+  
+  .pv_pk_price_label { display: block; font-size: 11px; color: rgba(74, 55, 40, 0.62); }
+  
+  .pv_pk_price_val { display: block; margin-top: 6px; font-size: 16px; font-weight: 700; color: $brown; }
+  
+  .pv_pk_price_cell--hot .pv_pk_price_val { color: #c0392b; }
+  
+  .pv_pk_vs {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    font-size: 28px;
+    font-weight: 700;
+    min-width: 36px;
+    margin-top: 68px;
+  
+    &--spacer { visibility: hidden; margin-top: 0; }
+  }
+  
+  .pv_pk_params {
+    display: grid;
+    grid-template-columns: 1fr auto 1fr;
+    gap: 12px;
+  }
+  
+  .pv_pk_param_head {
+    padding: 10px;
+    border-radius: 10px 10px 0 0;
+    box-shadow: 0 2px 10px rgba(74, 55, 40, 0.1);
+  }
+  
+  .pv_pk_action_group {
+    display: flex;
+    gap: 8px;
+    padding: 4px;
+    border-radius: 12px;
+    background: rgba(0, 0, 0, 0.16);
+  }
+  
+  .pv_pk_change, .pv_pk_confirm {
+    display: inline-flex;
+    align-items: center;
+    justify-content: center;
+    gap: 4px;
+    padding: 8px 10px;
+    border-radius: 9px;
+    font-size: 11px;
+  }
+  
+  .pv_pk_change {
+    color: rgba(255, 255, 255, 0.92);
+    background: rgba(255, 255, 255, 0.1);
+    border: 1px solid rgba(255, 255, 255, 0.24);
+  }
+  
+  .pv_pk_confirm {
+    flex: 1;
+    font-weight: 700;
+    color: #fff;
+    background: linear-gradient(135deg, var(--pk-show-gold, #{$gold}), var(--pk-show-accent, #{$orange}), var(--pk-show-primary, #{$brown}));
+  }
+  
+  .pv_pk_param_pills {
+    background: #fff;
+    border-radius: 0 0 8px 8px;
+    padding: 10px;
+  }
+  
+  .pv_pill {
+    display: flex;
+    justify-content: space-between;
+    padding: 8px 10px;
+    margin-bottom: 6px;
+    background: $row-bg;
+    border-radius: 20px;
+    font-size: 12px;
+  
+    .pv_pill_label { color: #666; }
+    .pv_pill_val { color: $brown; font-weight: 500; }
+  }
+  
+  .pv_product_page { padding: 10px 0 20px; }
+  
+  .pv_product_grid {
+    display: grid;
+    grid-template-columns: repeat(5, 1fr);
+    gap: 8px;
+    padding: 0 12px;
+  }
+  
+  .pv_product_card {
+    border-radius: 10px;
+    overflow: hidden;
+    box-shadow: 0 2px 8px rgba(74, 55, 40, 0.08);
+    display: flex;
+    flex-direction: column;
+  }
+  
+  .pv_product_card_img {
+    background: #fff;
+    padding: 8px 4px 4px;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+  
+    img { width: 100%; height: 72px; object-fit: contain; }
+  }
+  
+  .pv_product_card_info {
+    background: #F5EFE6;
+    padding: 6px 4px 8px;
+    text-align: center;
+    display: flex;
+    flex-direction: column;
+    gap: 2px;
+  }
+  
+  .pv_product_brand { font-size: 10px; color: rgba(74, 55, 40, 0.65); }
+  
+  .pv_product_model {
+    font-size: 11px;
+    font-weight: 600;
+    color: $brown;
+    overflow: hidden;
+    text-overflow: ellipsis;
+    white-space: nowrap;
+  }
+  
+  .pv_product_price_label { font-size: 10px; color: #c0392b; font-weight: 600; }
+  
+  .pv_search_overlay {
+    position: absolute;
+    inset: 0;
+    z-index: 20;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    padding: 80px 16px 24px;
+    box-sizing: border-box;
+    background: rgba(61, 46, 34, 0.48);
+    backdrop-filter: blur(4px);
+  }
+  
+  .pv_pk_search_panel {
+    width: 100%;
+    max-width: 640px;
+    max-height: 520px;
+    display: flex;
+    flex-direction: column;
+    border-radius: 18px;
+    overflow: hidden;
+    box-shadow: 0 20px 48px rgba(61, 46, 34, 0.28);
+    border: 1px solid rgba(255, 255, 255, 0.12);
+  }
+  
+  .pv_pk_search_head {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    padding: 16px 18px;
+  }
+  
+  .pv_pk_search_head_left { display: flex; align-items: center; gap: 10px; }
+  
+  .pv_pk_search_title { font-size: 17px; font-weight: 600; }
+  
+  .pv_pk_search_badge {
+    padding: 3px 10px;
+    border-radius: 999px;
+    font-size: 11px;
+    font-weight: 600;
+    color: $brown;
+    background: $gold;
+  }
+  
+  .pv_pk_search_close {
+    width: 32px;
+    height: 32px;
+    line-height: 32px;
+    text-align: center;
+    border-radius: 50%;
+    background: rgba(255, 255, 255, 0.12);
+    font-size: 20px;
+  }
+  
+  .pv_pk_search_body {
+    padding: 16px;
+    background: linear-gradient(180deg, #FFF9EC 0%, $panel 100%);
+    flex: 1;
+    min-height: 0;
+    display: flex;
+    flex-direction: column;
+  }
+  
+  .pv_pk_search_cat {
+    display: flex;
+    align-items: center;
+    gap: 8px;
+    margin-bottom: 12px;
+    font-size: 13px;
+  }
+  
+  .pv_pk_search_cat_label { color: rgba(74, 55, 40, 0.55); }
+  
+  .pv_pk_search_cat_name { font-weight: 600; color: $brown; }
+  
+  .pv_pk_search_bar {
+    display: flex;
+    align-items: center;
+    gap: 8px;
+    padding: 10px 14px;
+    border-radius: 14px;
+    border: 1px solid rgba(74, 55, 40, 0.1);
+    margin-bottom: 12px;
+    font-size: 13px;
+    color: rgba(74, 55, 40, 0.45);
+  }
+  
+  .pv_pk_search_table_head {
+    display: grid;
+    grid-template-columns: 1fr auto;
+    gap: 12px;
+    padding: 8px 12px;
+    font-size: 12px;
+    font-weight: 600;
+    color: rgba(74, 55, 40, 0.55);
+    border-bottom: 1px solid rgba(74, 55, 40, 0.08);
+  }
+  
+  .pv_pk_search_list {
+    flex: 1;
+    overflow: auto;
+    scrollbar-width: none;
+    -ms-overflow-style: none;
+    &::-webkit-scrollbar {
+      width: 0;
+      height: 0;
+      display: none;
+    }
+  }
+  
+  .pv_pk_search_item {
+    display: grid;
+    grid-template-columns: 1fr auto;
+    gap: 12px;
+    padding: 12px 14px;
+    font-size: 13px;
+    color: $brown;
+    border-bottom: 1px solid rgba(74, 55, 40, 0.06);
+  
+    &--active {
+      background: rgba(255, 140, 66, 0.12);
+      border-left: 3px solid $orange;
+    }
+  }
+  
+  .pv_pk_search_item_price { font-weight: 600; color: #c0392b; }
+  
+  .pv_pk_search_footer {
+    padding-top: 10px;
+    font-size: 12px;
+    color: rgba(74, 55, 40, 0.5);
+    text-align: center;
+  }
+
+
+  .form-panel {
+
+    flex: 1;
+
+    background: #fff;
+
+    padding: 20px;
+
+    border-radius: 12px;
+
+
+
+    .thumb { width: 80px; margin-left: 10px; vertical-align: middle; }
+
+    .color-select-item {
+      display: flex;
+      align-items: center;
+      flex-wrap: wrap;
+      gap: 10px;
+    }
+
+    .img-select-item {
+      display: flex;
+      align-items: center;
+      flex-wrap: wrap;
+      gap: 12px;
+      margin-top: 10px;
+    }
+
+    .img-default-preview { margin-top: 10px; }
+
+    .page-bg-default-preview {
+      display: flex;
+      align-items: center;
+      gap: 10px;
+    }
+
+    .page-bg-swatch {
+      width: 40px;
+      height: 24px;
+      border-radius: 4px;
+      border: 1px solid #ddd;
+    }
+
+    .thumb-block { border: 1px solid #eee; border-radius: 6px; }
+
+    .form-tip {
+      margin-left: 8px;
+      color: #999;
+      font-size: 12px;
+    }
+
+    .inline-label {
+      font-size: 13px;
+      color: #666;
+    }
+
+    .featured-form { border-top: 1px solid #eee; padding-top: 12px; margin-top: 12px; }
+
+    .featured-selected {
+      display: flex;
+      align-items: center;
+      gap: 12px;
+      margin: 0 0 12px 100px;
+      padding: 10px 12px;
+      background: #fafafa;
+      border: 1px solid #eee;
+      border-radius: 6px;
+    }
+
+    .featured-selected-img {
+      width: 56px;
+      height: 56px;
+      border-radius: 6px;
+      object-fit: cover;
+      flex-shrink: 0;
+    }
+
+    .featured-selected-ph { background: #ddd; }
+
+    .featured-selected-name { font-size: 14px; color: #333; font-weight: 500; }
+
+    .featured-selected-id { font-size: 12px; color: #999; margin-top: 4px; }
+
+    .sub-title { font-weight: bold; margin-bottom: 8px; }
+
+  }
+
+
+
+  .pz_head { font-size: 18px; font-weight: bold; margin-bottom: 12px; }
+
+  .pz_head--with-switch {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    gap: 16px;
+    flex-wrap: wrap;
+  }
+
+  .version-tag {
+    display: inline-block;
+    margin-left: 8px;
+    padding: 3px 10px;
+    border-radius: 4px;
+    font-size: 13px;
+    font-weight: 700;
+    color: #e6a23c;
+    background: #fdf6ec;
+    border: 1px solid #f5dab1;
+    vertical-align: middle;
+  }
+
+  .version-switch {
+    display: inline-flex;
+    align-items: center;
+    gap: 14px;
+    padding: 12px 20px;
+    border-radius: 10px;
+    border: 2px solid #e6a23c;
+    background: linear-gradient(135deg, #fdf6ec 0%, #fffbf5 100%);
+    box-shadow: 0 4px 14px rgba(230, 162, 60, 0.22);
+    transition: all 0.25s ease;
+    cursor: default;
+
+    &--active {
+      border-color: #e6a23c;
+      background: linear-gradient(135deg, #e6a23c 0%, #f0b85c 100%);
+      box-shadow: 0 6px 20px rgba(230, 162, 60, 0.4);
+
+      .version-switch__icon {
+        background: rgba(255, 255, 255, 0.25);
+        color: #fff;
+      }
+
+      .version-switch__label {
+        color: #fff;
+      }
+
+      .version-switch__status {
+        color: rgba(255, 255, 255, 0.88);
+      }
+    }
+
+    &__icon {
+      flex-shrink: 0;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      width: 40px;
+      height: 40px;
+      border-radius: 50%;
+      background: rgba(230, 162, 60, 0.15);
+      color: #e6a23c;
+      font-size: 20px;
+    }
+
+    &__body {
+      display: flex;
+      flex-direction: column;
+      gap: 2px;
+      min-width: 0;
+    }
+
+    &__label {
+      font-size: 15px;
+      font-weight: 700;
+      color: #e6a23c;
+      white-space: nowrap;
+      line-height: 1.3;
+    }
+
+    &__status {
+      font-size: 12px;
+      color: #909399;
+      white-space: nowrap;
+      line-height: 1.3;
+    }
+
+    &__badge {
+      flex-shrink: 0;
+      display: inline-flex;
+      align-items: center;
+      gap: 6px;
+      padding: 8px 16px;
+      border-radius: 20px;
+      font-size: 14px;
+      font-weight: 600;
+      color: #fff;
+      background: rgba(255, 255, 255, 0.25);
+      white-space: nowrap;
+    }
+
+    &__btn {
+      flex-shrink: 0;
+      padding: 10px 22px;
+      font-size: 14px;
+      font-weight: 700;
+      border-radius: 20px;
+      border: none;
+      box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
+
+      &--v3 {
+        background: #fff;
+        color: #e6a23c;
+
+        &:hover,
+        &:focus {
+          background: #fffbf5;
+          color: #e6a23c;
+        }
+      }
+    }
+  }
+
+}
+
+</style>
+
diff --git a/company_admin/src/views/system/data-permission.vue b/company_admin/src/views/system/data-permission.vue
index a9ce2c2..991dddb 100644
--- a/company_admin/src/views/system/data-permission.vue
+++ b/company_admin/src/views/system/data-permission.vue
@@ -17,7 +17,7 @@
     <template v-slot:table-wrap>
       <ul class="toolbar" v-permissions="['system:datapermission:create', 'system:datapermission:delete']">
         <li><el-button type="primary" @click="$refs.operaDataPermissionWindow.open('鏂板缓鏁版嵁鏉冮檺')" icon="el-icon-plus" v-permissions="['system:datapermission:create']">鏂板缓</el-button></li>
-        <li><el-button @click="deleteByIdInBatch" icon="el-icon-delete" v-permissions="['system:datapermission:delete']">鍒犻櫎</el-button></li>
+        <li><el-button type="danger" @click="deleteByIdInBatch" icon="el-icon-delete" v-permissions="['system:datapermission:delete']">鍒犻櫎</el-button></li>
       </ul>
       <el-table
         v-loading="isWorking.search"
diff --git a/company_admin/src/views/system/department.vue b/company_admin/src/views/system/department.vue
index 3c9b7c7..876f74d 100644
--- a/company_admin/src/views/system/department.vue
+++ b/company_admin/src/views/system/department.vue
@@ -4,7 +4,7 @@
     <template v-slot:table-wrap>
       <ul class="toolbar" v-permissions="['system:department:create', 'system:department:delete']">
         <li><el-button type="primary" @click="$refs.operaDepartmentWindow.open('鏂板缓閮ㄩ棬')" icon="el-icon-plus" v-permissions="['system:department:create']">鏂板缓</el-button></li>
-        <li><el-button @click="deleteByIdInBatch" icon="el-icon-delete" v-permissions="['system:department:delete']">鍒犻櫎</el-button></li>
+        <li><el-button type="danger" @click="deleteByIdInBatch" icon="el-icon-delete" v-permissions="['system:department:delete']">鍒犻櫎</el-button></li>
       </ul>
       <el-table
         v-loading="isWorking.search"
diff --git a/company_admin/src/views/system/dict.vue b/company_admin/src/views/system/dict.vue
index 5c67584..1513b6f 100644
--- a/company_admin/src/views/system/dict.vue
+++ b/company_admin/src/views/system/dict.vue
@@ -17,7 +17,7 @@
     <template v-slot:table-wrap>
       <ul class="toolbar" v-permissions="['system:dict:create', 'system:dict:delete']">
         <li><el-button type="primary" @click="$refs.operaDictWindow.open('鏂板缓瀛楀吀')" icon="el-icon-plus" v-permissions="['system:dict:create']">鏂板缓</el-button></li>
-        <li><el-button @click="deleteByIdInBatch" icon="el-icon-delete" v-permissions="['system:dict:delete']">鍒犻櫎</el-button></li>
+        <li><el-button type="danger" @click="deleteByIdInBatch" icon="el-icon-delete" v-permissions="['system:dict:delete']">鍒犻櫎</el-button></li>
       </ul>
       <el-table
         v-loading="isWorking.search"
diff --git a/company_admin/src/views/system/menu.vue b/company_admin/src/views/system/menu.vue
index 87bf4b3..7623f6b 100644
--- a/company_admin/src/views/system/menu.vue
+++ b/company_admin/src/views/system/menu.vue
@@ -4,7 +4,7 @@
     <template v-slot:table-wrap>
       <ul class="toolbar" v-permissions="['system:menu:create', 'system:menu:delete', 'system:menu:sort']">
         <li><el-button type="primary" @click="$refs.operaMenuWindow.open('鏂板缓涓�绾ц彍鍗�')" icon="el-icon-plus" v-permissions="['system:menu:create']">鏂板缓</el-button></li>
-        <li><el-button @click="deleteByIdInBatch" icon="el-icon-delete" v-permissions="['system:menu:delete']">鍒犻櫎</el-button></li>
+        <li><el-button type="danger" @click="deleteByIdInBatch" icon="el-icon-delete" v-permissions="['system:menu:delete']">鍒犻櫎</el-button></li>
         <li><el-button @click="sort('top')" :loading="isWorking.sort" icon="el-icon-sort-up" v-permissions="['system:menu:sort']">涓婄Щ</el-button></li>
         <li><el-button @click="sort('bottom')" :loading="isWorking.sort" icon="el-icon-sort-down" v-permissions="['system:menu:sort']">涓嬬Щ</el-button></li>
       </ul>
diff --git a/company_admin/src/views/system/permission.vue b/company_admin/src/views/system/permission.vue
index 76f35b3..97742fe 100644
--- a/company_admin/src/views/system/permission.vue
+++ b/company_admin/src/views/system/permission.vue
@@ -17,7 +17,7 @@
     <template v-slot:table-wrap>
       <ul class="toolbar" v-permissions="['system:permission:create', 'system:permission:delete']">
         <li><el-button type="primary" @click="$refs.operaPermissionWindow.open('鏂板缓绯荤粺鏉冮檺')" icon="el-icon-plus" v-permissions="['system:permission:create']">鏂板缓</el-button></li>
-        <li><el-button @click="deleteByIdInBatch" icon="el-icon-delete" v-permissions="['system:permission:delete']">鍒犻櫎</el-button></li>
+        <li><el-button type="danger" @click="deleteByIdInBatch" icon="el-icon-delete" v-permissions="['system:permission:delete']">鍒犻櫎</el-button></li>
       </ul>
       <el-table
         v-loading="isWorking.search"
diff --git a/company_admin/src/views/system/position.vue b/company_admin/src/views/system/position.vue
index b2ef9a8..f493e6a 100644
--- a/company_admin/src/views/system/position.vue
+++ b/company_admin/src/views/system/position.vue
@@ -4,7 +4,7 @@
     <template v-slot:table-wrap>
       <ul class="toolbar" v-permissions="['system:position:create', 'system:position:delete']">
         <li><el-button type="primary" @click="$refs.operaPositionWindow.open('鏂板缓宀椾綅')" icon="el-icon-plus" v-permissions="['system:position:create']">鏂板缓</el-button></li>
-        <li><el-button @click="deleteByIdInBatch" icon="el-icon-delete" v-permissions="['system:position:delete']">鍒犻櫎</el-button></li>
+        <li><el-button type="danger" @click="deleteByIdInBatch" icon="el-icon-delete" v-permissions="['system:position:delete']">鍒犻櫎</el-button></li>
       </ul>
       <el-table
         v-loading="isWorking.search"
diff --git a/company_admin/src/views/system/role.vue b/company_admin/src/views/system/role.vue
index 91cf09e..30934ff 100644
--- a/company_admin/src/views/system/role.vue
+++ b/company_admin/src/views/system/role.vue
@@ -17,7 +17,7 @@
     <template v-slot:table-wrap>
       <ul class="toolbar" v-permissions="['system:role:create', 'system:role:delete']">
         <li v-permissions="['system:role:create']"><el-button type="primary" @click="$refs.operaRoleWindow.open('鏂板缓瑙掕壊')" icon="el-icon-plus">鏂板缓</el-button></li>
-        <li v-permissions="['system:role:delete']"><el-button @click="deleteByIdInBatch" icon="el-icon-delete">鍒犻櫎</el-button></li>
+        <li v-permissions="['system:role:delete']"><el-button type="danger" @click="deleteByIdInBatch" icon="el-icon-delete">鍒犻櫎</el-button></li>
       </ul>
       <el-table
         v-loading="isWorking.search"
diff --git a/company_admin/src/views/system/user.vue b/company_admin/src/views/system/user.vue
index 5e4d7f5..d71ecfc 100644
--- a/company_admin/src/views/system/user.vue
+++ b/company_admin/src/views/system/user.vue
@@ -26,7 +26,7 @@
     <template v-slot:table-wrap>
       <ul class="toolbar" v-permissions="['system:user:create', 'system:user:delete']">
         <li v-permissions="['system:user:create']"><el-button icon="el-icon-plus" type="primary" @click="$refs.operaUserWindow.open('鏂板缓鐢ㄦ埛')">鏂板缓</el-button></li>
-        <li v-permissions="['system:user:delete']"><el-button icon="el-icon-delete" @click="deleteByIdInBatch">鍒犻櫎</el-button></li>
+        <li v-permissions="['system:user:delete']"><el-button type="danger" icon="el-icon-delete" @click="deleteByIdInBatch">鍒犻櫎</el-button></li>
       </ul>
       <el-table
         v-loading="isWorking.search"
diff --git a/h5/App.vue b/h5/App.vue
index aae8e9d..a0de63e 100644
--- a/h5/App.vue
+++ b/h5/App.vue
@@ -15,6 +15,57 @@
 <style lang="scss">
 	/*姣忎釜椤甸潰鍏叡css */
 	@import '@/uni_modules/uni-scss/index.scss';
+
+	/* #ifdef H5 */
+	$scrollbar-brown: #4A3728;
+	$scrollbar-cream: #E8DCC8;
+
+	html,
+	body,
+	#app,
+	uni-app,
+	uni-page,
+	uni-page-wrapper,
+	.uni-page-wrapper,
+	page {
+		scrollbar-width: thin;
+		scrollbar-color: rgba(74, 55, 40, 0.38) rgba(232, 220, 200, 0.45);
+	}
+
+	* {
+		scrollbar-width: thin;
+		scrollbar-color: rgba(74, 55, 40, 0.38) rgba(232, 220, 200, 0.45);
+	}
+
+	::-webkit-scrollbar {
+		width: 5px;
+		height: 5px;
+	}
+
+	::-webkit-scrollbar-track {
+		background: rgba(232, 220, 200, 0.35);
+		border-radius: 999px;
+	}
+
+	::-webkit-scrollbar-thumb {
+		background: linear-gradient(180deg, rgba(74, 55, 40, 0.42) 0%, rgba(74, 55, 40, 0.28) 100%);
+		border-radius: 999px;
+		border: 1px solid rgba(255, 255, 255, 0.22);
+		min-height: 28px;
+	}
+
+	::-webkit-scrollbar-thumb:hover {
+		background: linear-gradient(180deg, rgba(74, 55, 40, 0.58) 0%, rgba(74, 55, 40, 0.42) 100%);
+	}
+
+	::-webkit-scrollbar-thumb:active {
+		background: rgba(74, 55, 40, 0.62);
+	}
+
+	::-webkit-scrollbar-corner {
+		background: transparent;
+	}
+	/* #endif */
 </style>
 
 <!-- ,
diff --git a/h5/apis/index.js b/h5/apis/index.js
index 124950f..40e1568 100644
--- a/h5/apis/index.js
+++ b/h5/apis/index.js
@@ -53,4 +53,24 @@
 // 鑾峰彇褰撳墠鐧诲綍浼佷笟閰嶇疆淇℃伅1.0.2
 export function getByLoginNew() {
 	return get('business/webParam/getByLoginNew')
-}
\ No newline at end of file
+}
+
+// 鑾峰彇涓绘挱绔〉闈㈤厤缃�
+export function getByLoginAnchor() {
+	return get('business/webParam/getByLoginAnchor')
+}
+
+// H5 涓绘挱绔垵濮嬪寲鍏ㄩ噺鏁版嵁
+export function loadH5InitData() {
+	return get('business/h5/initData')
+}
+
+// 鐢熸垚璁㈠崟缂栧彿
+export function generateOrderNo() {
+	return get('business/order/generateOrderNo')
+}
+
+// 鎻愪氦棰勯�夎鍗�
+export function submitOrder(data) {
+	return post('business/order/submit', data)
+}
diff --git a/h5/components/bigImg.vue b/h5/components/bigImg.vue
index 2cf1eef..ec5c66c 100644
--- a/h5/components/bigImg.vue
+++ b/h5/components/bigImg.vue
@@ -1,21 +1,39 @@
 <template>
-	<view class="img" :style="{ opacity: opacity, zIndex: zindex }">
-		<view class="img_content">
+	<view class="img" :class="{ 'img--open': opacity === 1 }" :style="{ opacity: opacity, zIndex: zindex }" @click="close">
+		<view class="img_content" @click.stop>
 			<view class="img_content_tu">
-				<image class="left" src="@/static/ic_left@2x.png" mode="widthFix" @click="jian"></image>
+				<image
+					v-if="imgList.length > 1"
+					class="arrow"
+					src="@/static/ic_left@2x.png"
+					mode="widthFix"
+					@click="prev"></image>
 				<view class="img_content_tu_nr">
-					<swiper style="width: 100%; height: 100%;" @change="handlechange" :current="mycurrent" :indicator-dots="false"  :circular="true" :interval="1000" :duration="1000">
-						<swiper-item v-for="(item,index) in imgList" :key="index">
-							<view :class="['swiper-item',index == mycurrent ? 'active' : '']">
-								<image :src="item" style="width: 150%;height: 150%;" mode="aspectFit" />
+					<swiper
+						style="width: 100%; height: 100%;"
+						@change="handlechange"
+						:current="mycurrent"
+						:indicator-dots="imgList.length > 1"
+						indicator-color="rgba(255,255,255,0.35)"
+						indicator-active-color="#ffffff"
+						:circular="imgList.length > 1"
+						:duration="300">
+						<swiper-item v-for="(item, index) in imgList" :key="index">
+							<view class="swiper-item">
+								<image :src="item" class="preview-image" mode="aspectFit" />
 							</view>
 						</swiper-item>
 					</swiper>
 				</view>
-				<image class="right" src="@/static/ic_right@2x.png" mode="widthFix" @click="add"></image>
+				<image
+					v-if="imgList.length > 1"
+					class="arrow"
+					src="@/static/ic_right@2x.png"
+					mode="widthFix"
+					@click="next"></image>
 			</view>
-			<view class="img_content_close">
-				<image src="@/static/ic_close@2x.png" mode="widthFix" @click="close"></image>
+			<view class="img_content_close" @click="close">
+				<image src="@/static/ic_close@2x.png" mode="widthFix"></image>
 			</view>
 		</view>
 	</view>
@@ -37,20 +55,20 @@
 			}
 		},
 		methods: {
-			add() {
-				if (this.imgList.length - 1 === this.mycurrent) return
-				this.mycurrent++
+			next() {
+				if (this.imgList.length <= 1) return
+				this.mycurrent = (this.mycurrent + 1) % this.imgList.length
 			},
-			jian() {
-				if (this.mycurrent === 0) return
-				this.mycurrent--
+			prev() {
+				if (this.imgList.length <= 1) return
+				this.mycurrent = (this.mycurrent - 1 + this.imgList.length) % this.imgList.length
 			},
-			handlechange(e){
-				this.mycurrent=e.detail.current
+			handlechange(e) {
+				this.mycurrent = e.detail.current
 			},
 			open(i) {
-				this.mycurrent = i
-				this.zindex = 3
+				this.mycurrent = i || 0
+				this.zindex = 9999
 				this.opacity = 1
 			},
 			close() {
@@ -65,77 +83,76 @@
 	.img {
 		width: 100vw;
 		height: 100vh;
-		background: rgba(0,0,0,0.4);
+		background: rgba(0, 0, 0, 0.65);
 		position: fixed;
-		transition: .2s;
+		transition: opacity 0.2s;
 		top: 0;
 		left: 0;
 		display: flex;
 		align-items: center;
 		justify-content: center;
-		
+		pointer-events: none;
+
+		&.img--open {
+			pointer-events: auto;
+		}
+
 		.img_content {
 			display: flex;
 			flex-direction: column;
+			pointer-events: auto;
+
 			.img_content_tu {
 				display: flex;
 				align-items: center;
+
 				.img_content_tu_nr {
 					width: 600px;
 					height: 600px;
-					padding: 30px;
+					padding: 24px;
 					box-sizing: border-box;
-					background: rgba(255,255,255,0.9);
+					background: rgba(255, 255, 255, 0.95);
 					border-radius: 8px;
-					margin: 0 32px;
+					margin: 0 24px;
 					display: flex;
 					align-items: center;
-					flex-wrap: nowrap;
-					.swiper-item{
+					justify-content: center;
+
+					.swiper-item {
 						width: 100%;
-						border-radius: 30rpx;
-						overflow: hidden;
-						// 鍍忚繖绉嶅寮犺疆鎾浘鍚屾椂鍑虹幇鍦ㄤ竴灞忕殑鎯呭喌涓嬪氨涓嶈鎸囧畾width浜嗭紝涓嶇劧浣犱細鍙戠幇previous-margin鍜� next-margin浼氬嚭鐜版兂涓嶅埌鐨勬晥鏋�
-						// 濡傛灉鎯宠璁剧疆瀹芥瘡涓�寮犺疆鎾浘鐨勫搴︼紝鍙渶瑕佽缃畃revious-margin鍜宯ext-margin灏卞彲浠ヤ簡锛屾兂瑕佽缃珮搴︾洿鎺ユ敼涓嬮潰鐨刪eight灏卞彲浠ヤ簡
-						// width: 450rpx;
 						height: 100%;
-						// transform: scale(1);
-						// transition: all 0.5s ease;
-						text-align: center;
-						// transition: all 0.5s ease-in-out;
+						display: flex;
+						align-items: center;
+						justify-content: center;
 					}
-					.img_content_tu_nr_item {
+
+					.preview-image {
 						width: 100%;
-						height: 600px;
-						image {
-							width: 100%;
-							height: 100%;
-						}
+						height: 100%;
 					}
 				}
-				.left {
-					width: 80px;
-					height: 80px;
-					cursor: pointer;
-				}
-				.right {
-					width: 80px;
-					height: 80px;
+
+				.arrow {
+					width: 64px;
+					height: 64px;
+					flex-shrink: 0;
 					cursor: pointer;
 				}
 			}
+
 			.img_content_close {
 				width: 100%;
 				display: flex;
 				align-items: center;
 				justify-content: center;
-				margin-top: 40px;
+				margin-top: 32px;
+				cursor: pointer;
+
 				image {
-					width: 60px;
-					height: 60px;
-					cursor: pointer;
+					width: 56px;
+					height: 56px;
 				}
 			}
 		}
 	}
-</style>
\ No newline at end of file
+</style>
diff --git a/h5/components/search.vue b/h5/components/search.vue
index 5d6613b..70a9ea2 100644
--- a/h5/components/search.vue
+++ b/h5/components/search.vue
@@ -1,57 +1,99 @@
 <template>
-	<view class="search" v-if="show" @click="close">
+	<view class="search_root">
+	<!-- 棣栭〉鎼滅储锛堜繚鐣欏師閫昏緫锛� -->
+	<view class="search search--legacy" v-if="show && status === 0" @click="close">
 		<view class="search_box" @click.stop="test">
-			<view class="search_box_item" :style="{background: !search ? 'rgba(13, 30, 65, 0.70)' : search.bgType === 1 ? percentage(search.bgColor, search.bgAlpha) : 'rgba(13, 30, 65, 0.70)'}" v-if="status === 0">
+			<view class="search_box_item" :style="searchBarBgStyle">
 				<view class="icon">
 					<image src="@/static/ic_search@2x.png" mode="widthFix"></image>
 				</view>
 				<view class="search_box_item_right">
-					<text v-if="name1">{{name1}}</text>
+					<text v-if="name1">{{ name1 }}</text>
 					<input type="text" class="search_box_item_right_ipt" :focus="focus" v-model="category" @input="inputCategory(name1 ? 2 : 1)" placeholder-class="placeholder" placeholder="鎼滅储" />
-				</view>
-			</view>
-			<view class="search_box_item" :style="{background: !search ? 'rgba(13, 30, 65, 0.70)' : search.bgType === 1 ? percentage(search.bgColor, search.bgAlpha) : 'rgba(13, 30, 65, 0.70)'}" v-else>
-				<view class="icon">
-					<image src="@/static/ic_search@2x.png" mode="widthFix"></image>
-				</view>
-				<view class="search_box_item_right">
-					<text>{{categoryName}}</text>
-					<input type="text" focus v-model="category" @input="inputCategory(name1 ? 2 : 1)" placeholder-class="placeholder" placeholder="鎼滅储" />
 				</view>
 			</view>
 			<view class="search_box_item_xl" v-if="searchData && searchData.length > 0">
 				<view class="search_box_item_xl_item" v-for="(item, i) in searchData" :key="i">
 					<view :class="index === i ? 'search_box_item_xl_item_name active' : 'search_box_item_xl_item_name'" @click="clickItem(item, i)" @mouseenter="aaa(i)" @mouseleave="bbb">
-  
-						<span :style="{color: fontColorIndex === i ? fontColor : ''}">{{item.name}}</span>
+						<span :style="{ color: fontColorIndex === i ? fontColor : '' }">{{ item.name }}</span>
 					</view>
 					<span v-if="item.price">锟{ item.price }}</span>
 				</view>
 			</view>
-			
-			<!-- <view class="search_box_item" v-if="status == 0">
-				<view class="icon">
-					<image src="@/static/ic_search@2x.png" mode="widthFix"></image>
-				</view>
-				<input type="text" :focus="status === 0" v-model="category" @input="inputCategory" placeholder-class="placeholder" placeholder="鎼滅储" />
-			</view>
-			<view class="search_box_item">
-				<view class="icon">
-					<image src="@/static/ic_search@2x.png" mode="widthFix"></image>
-				</view>
-				<input type="text" :focus="status !== 0" v-model="name" @input="inputName" placeholder-class="placeholder" placeholder="鎼滅储" />
-			</view>
-			<view class="search_box_item_xl" v-if="searchData && searchData.length > 0">
-				<view class="search_box_item_xl_item" v-for="(item, index) in searchData" :key="index">
-					<view class="search_box_item_xl_item_name" @click="clickItem(item, index)">{{ item.name }}</view><span v-if="item.price">锟{ item.price }}</span>
-				</view>
-			</view> -->
 		</view>
+	</view>
+
+	<!-- PK / 鍟嗗搧鍒楄〃 閫夋嫨鍟嗗搧 -->
+	<view class="pk_search" v-if="show && status !== 0" @click="close">
+		<view class="pk_search_panel" @click.stop="test">
+			<view class="pk_search_head" :style="pkSearchHeadStyle">
+				<view class="pk_search_head_left">
+					<text class="pk_search_title">閫夋嫨鍟嗗搧</text>
+					<text class="pk_search_badge" v-if="pkSideText">{{ pkSideText }}</text>
+				</view>
+				<view class="pk_search_close" @click.stop="close">
+					<image src="@/static/ic_close@2x.png" mode="widthFix"></image>
+				</view>
+			</view>
+
+			<view class="pk_search_body">
+				<view class="pk_search_cat" v-if="categoryName">
+					<text class="pk_search_cat_label">鍝佺被</text>
+					<text class="pk_search_cat_name">{{ categoryName }}</text>
+				</view>
+
+				<view class="pk_search_bar" :style="searchBarBgStyle">
+					<image class="pk_search_bar_icon" src="@/static/ic_search@2x.png" mode="widthFix"></image>
+					<view class="pk_search_bar_divider"></view>
+					<input
+						type="text"
+						class="pk_search_bar_input"
+						:focus="focus"
+						v-model="category"
+						@input="inputCategory(1)"
+						placeholder-class="pk_search_placeholder"
+						placeholder="鎼滅储鍨嬪彿" />
+				</view>
+
+				<view class="pk_search_table_head" v-if="searchData.length">
+					<text class="col col_name">鍟嗗搧鍨嬪彿</text>
+					<text class="col col_price">鎸囧浠�</text>
+				</view>
+
+				<view class="pk_search_list" v-if="searchData.length">
+					<view
+						class="pk_search_item"
+						v-for="(item, i) in searchData"
+						:key="i"
+						:class="{ 'pk_search_item--active': index === i }"
+						@click="clickItem(item, i)"
+						@mouseenter="aaa(i)"
+						@mouseleave="bbb">
+						<text class="pk_search_item_name">{{ item.name }}</text>
+						<text class="pk_search_item_price" v-if="item.price">楼{{ item.price }}</text>
+					</view>
+				</view>
+
+				<view class="pk_search_empty" v-else>
+					<text>{{ category ? '鏈壘鍒板尮閰嶅晢鍝�' : '璇ュ搧绫讳笅鏆傛棤鍟嗗搧' }}</text>
+				</view>
+
+				<view class="pk_search_footer" v-if="searchData.length">
+					<text>鍏� {{ searchData.length }} 浠跺晢鍝�</text>
+				</view>
+			</view>
+		</view>
+	</view>
 	</view>
 </template>
 
 <script>
 	import { listForH5 } from '@/apis/index.js'
+	import { filterGoods } from '@/utils/goodsFilter.js'
+
+	const DEFAULT_THEME_BG = '#4A3728'
+	const DEFAULT_THEME_COLOR = '#FFFFFF'
+
 	export default {
 		data() {
 			return {
@@ -60,11 +102,10 @@
 				name: '',
 				show: false,
 				type: '',
-				typeName: '', // left right
+				typeName: '',
 				searchData: [],
 				name1: '',
 				name2: '',
-				
 				index: 0,
 				focus: true,
 				fontColorIndex: ''
@@ -73,39 +114,73 @@
 		props: {
 			categoryList: {
 				type: Array,
-				default: []
+				default: () => []
 			},
 			shopList: {
 				type: Array,
-				default: []
+				default: () => []
+			},
+			allGoods: {
+				type: Array,
+				default: () => []
 			},
 			status: {
 				type: Number
 			},
 			search: {
-				type: Object | null
+				type: Object,
+				default: null
+			},
+			conMark: {
+				type: Object,
+				default: null
 			},
 			categoryName: {
-				type: Number | String
+				type: [Number, String]
 			},
 			categoryid: {
-				type: Number | String
+				type: [Number, String]
 			},
-		 
-			fontColor:{
-				type: Number | String
+			fontColor: {
+				type: [Number, String]
+			},
+			excludeGoodsId: {
+				type: [Number, String],
+				default: ''
+			}
+		},
+		computed: {
+			pkSideText() {
+				if (this.typeName === 'left') return 'PK 宸︿晶'
+				if (this.typeName === 'right') return 'PK 鍙充晶'
+				return ''
+			},
+			searchBarBgStyle() {
+				const s = this.search || {}
+				const defaultBg = 'rgba(255, 255, 255, 0.92)'
+				if (!s || Number(s.bgType) !== 1) {
+					return { background: defaultBg }
+				}
+				return {
+					background: this.percentage(s.bgColor || '#FFFFFF', s.bgAlpha || 100)
+				}
+			},
+			pkSearchHeadStyle() {
+				const theme = this.resolveThemeConfig()
+				return {
+					background: Number(theme.bgType) === 1 ? (theme.bgColor || DEFAULT_THEME_BG) : DEFAULT_THEME_BG,
+					color: Number(theme.colorType) === 1 ? (theme.color || DEFAULT_THEME_COLOR) : DEFAULT_THEME_COLOR
+				}
 			}
 		},
 		watch: {
-			show: {
-				handler(news, old) {
-					if (news) {
-						this.category = ''
-						this.name = ''
-						this.searchData = []
-						this.name1 = ''
-						this.name2 = ''
-					}
+			show(val) {
+				if (val) {
+					this.category = ''
+					this.name = ''
+					this.searchData = []
+					this.name1 = ''
+					this.name2 = ''
 				}
 			}
 		},
@@ -117,38 +192,50 @@
 				this.fontColorIndex = ''
 			},
 			percentage(bgColor, alpha) {
-				let res = +(alpha * 2.55).toFixed(0)
+				const res = +(alpha * 2.55).toFixed(0)
 				return bgColor + res.toString(16)
+			},
+			resolveThemeConfig() {
+				const cfg = this.conMark
+				if (!cfg) {
+					return { bgType: 0, bgColor: DEFAULT_THEME_BG, colorType: 0, color: DEFAULT_THEME_COLOR }
+				}
+				if (cfg.theme) return cfg.theme
+				const t = cfg.table || {}
+				const h = cfg.header || {}
+				return {
+					bgType: Number(t.headerBgType) === 1 ? 1 : (Number(h.bgType) === 1 ? 1 : 0),
+					bgColor: t.headerBg || h.bgColor || DEFAULT_THEME_BG,
+					colorType: Number(t.headerColorType) === 1 ? 1 : (Number(h.colorType) === 1 ? 1 : 0),
+					color: t.headerColor || h.color || DEFAULT_THEME_COLOR
+				}
 			},
 			confirm() {
 				if (this.type === 2) {
-					console.log('type')
 					this.name = this.searchData[this.index].name
 					this.$emit('result', this.searchData[this.index])
 					this.show = false
 					this.index = 0
 					return
-				} else if (this.categoryName) {
-					console.log('categoryName')
-					this.name = this.searchData[this.index].name
-					this.$emit('result', this.searchData[this.index])
-					this.show = false
-					this.index = 0
-					return
-				} else {
-					this.focus = false
-					console.log('else')
-					this.name1 = this.searchData[this.index].name
-					this.category = ''
-					this.categoryId = this.searchData[this.index].id
-					this.searchData = [];
-					this.$nextTick(() => {
-						setTimeout(() =>{
-							this.focus = true
-						}, 500)
-					})
-					this.index = 0
 				}
+				if (this.categoryName) {
+					this.name = this.searchData[this.index].name
+					this.$emit('result', this.searchData[this.index])
+					this.show = false
+					this.index = 0
+					return
+				}
+				this.focus = false
+				this.name1 = this.searchData[this.index].name
+				this.category = ''
+				this.categoryId = this.searchData[this.index].id
+				this.searchData = []
+				this.$nextTick(() => {
+					setTimeout(() => {
+						this.focus = true
+					}, 500)
+				})
+				this.index = 0
 			},
 			changeTop() {
 				if (this.index === 0) return
@@ -159,12 +246,22 @@
 				this.index += 1
 			},
 			getShop(keyword) {
-				listForH5({
-					categoryId: this.categoryid || this.categoryId,
-					keyword
-				}).then(res => {
-					this.searchData = res.data
-				})
+				const categoryId = this.categoryid || this.categoryId
+				let list = []
+				if (this.allGoods && this.allGoods.length) {
+					list = filterGoods(this.allGoods, { categoryId, keyword })
+				} else {
+					listForH5({ categoryId, keyword }).then(res => {
+						this.searchData = this.filterExcludedGoods(res.data || [])
+					})
+					return
+				}
+				this.searchData = this.filterExcludedGoods(list)
+			},
+			filterExcludedGoods(list) {
+				const arr = Array.isArray(list) ? list : []
+				if (this.excludeGoodsId == null || this.excludeGoodsId === '') return arr
+				return arr.filter(item => String(item.id) !== String(this.excludeGoodsId))
 			},
 			inputName() {
 				if (this.status === 1 || this.status === 2) {
@@ -177,33 +274,31 @@
 							return item
 						}
 					})
-					arr = arr.filter(Boolean);
-					return arr;
-				} else if (this.category && this.name) {
+					arr = arr.filter(Boolean)
+					return arr
+				}
+				if (this.category && this.name) {
 					this.type = 2
 					let arr = this.shopList.map(item => {
 						if (item.categoryName == this.category) {
 							return item
 						}
 					})
-					arr = arr.filter(Boolean);
+					arr = arr.filter(Boolean)
 					let arrOne = arr.map(item => {
 						if (item.name.indexOf(this.name) != -1 || item.pinyin.indexOf(this.name) != -1 || item.shortPinyin.indexOf(this.name) != -1) {
 							return item
 						}
 					})
-					arrOne = arrOne.filter(Boolean);
-					this.searchData = arrOne;
+					arrOne = arrOne.filter(Boolean)
+					this.searchData = arrOne
 				} else {
-					this.searchData = [];
+					this.searchData = []
 				}
 			},
 			inputCategory(type) {
-				// this.name = ''
-				// this.type = 1
 				if (this.status === 0) {
 					this.type = type
-					// 鍝佺被鎼滅储
 					if (type === 1) {
 						if (!this.category) {
 							this.searchData = []
@@ -214,101 +309,72 @@
 								return item
 							}
 						})
-						arr = arr.filter(Boolean);
-						this.searchData = arr;
+						arr = arr.filter(Boolean)
+						this.searchData = arr
 					} else if (type === 2) {
 						if (!this.category) {
 							this.searchData = []
 							return
 						}
 						this.getShop(this.category)
-						// let arr = this.shopList.map(item => {
-						// 	if (item.categoryName == this.name1) {
-						// 		return item
-						// 	}
-						// })
-						// arr = arr.filter(Boolean);
-						// let arrOne = arr.map(item => {
-						// 	if (item.name.indexOf(this.category) != -1 || item.pinyin.indexOf(this.category) != -1 || item.shortPinyin.indexOf(this.category) != -1) {
-						// 		return item
-						// 	}
-						// })
-						// arrOne = arrOne.filter(Boolean);
-						// this.searchData = arrOne;
 					} else {
 						this.searchData = []
 					}
 				} else {
-					if (!this.category) {
-						this.searchData = []
-						return
-					}
-					
-					this.getShop(this.category)
-					
-					// let arr = this.shopList.map(item => {
-					// 	if (item.categoryName == this.categoryName) {
-					// 		return item
-					// 	}
-					// })
-					// arr = arr.filter(Boolean);
-					// let arrOne = arr.map(item => {
-					// 	if (item.name.indexOf(this.category) != -1 || item.pinyin.indexOf(this.category) != -1 || item.shortPinyin.indexOf(this.category) != -1) {
-					// 		return item
-					// 	}
-					// })
-					// arrOne = arrOne.filter(Boolean);
-					// this.searchData = arrOne;
+					this.getShop(this.category || '')
 				}
-				
-				
-				// if (this.category && !this.name) {
-				// 	let arr = this.categoryList.map(item => {
-				// 		if (item.name.indexOf(this.category) != -1 || item.pinyin.indexOf(this.category) != -1 || item.shortPinyin.indexOf(this.category) != -1) {
-				// 			return item
-				// 		}
-				// 	})
-				// 	arr = arr.filter(Boolean);
-				// 	this.searchData = arr;
-				// } else {
-				// 	this.searchData = [];
-				// }
-				// console.log(this.searchData)
 			},
 			clickItem(item, index) {
 				if (this.type === 2) {
-					console.log('type')
 					this.name = item.name
 					this.$emit('result', item)
 					this.show = false
 					this.index = 0
 					return
-				} else if (this.categoryName) {
-					console.log('categoryName')
-					this.name = item.name
-					this.$emit('result', item)
-					this.show = false
-					this.index = 0
-					return
-				} else {
-					console.log('else')
-					this.focus = false
-					this.name1 = item.name
-					this.category = ''
-					this.categoryId = item.id
-					// this.category = item.name
-					this.searchData = [];
-					this.$nextTick(() => {
-						setTimeout(() => {
-							this.focus = true
-						}, 800)
-					})
-					this.index = 0
 				}
+				if (this.categoryName || this.status !== 0) {
+					this.name = item.name
+					this.$emit('result', item)
+					this.show = false
+					this.index = 0
+					return
+				}
+				this.focus = false
+				this.name1 = item.name
+				this.category = ''
+				this.categoryId = item.id
+				this.searchData = []
+				this.$nextTick(() => {
+					setTimeout(() => {
+						this.focus = true
+					}, 800)
+				})
+				this.index = 0
 			},
 			open(type) {
 				this.typeName = type
+				this.category = ''
+				this.name = ''
+				this.searchData = []
+				this.name1 = ''
+				this.name2 = ''
+				this.index = 0
 				this.show = true
+				this.focus = false
+				this.$nextTick(() => {
+					this.focus = true
+					if (this.status !== 0) {
+						this.loadCategoryGoods('')
+					}
+				})
+			},
+			loadCategoryGoods(keyword) {
+				const categoryId = this.categoryid || this.categoryId
+				if (!categoryId) {
+					this.searchData = []
+					return
+				}
+				this.getShop(keyword || '')
 			},
 			close() {
 				this.show = false
@@ -321,9 +387,285 @@
 </script>
 
 <style lang="scss" scoped>
-	.search {
+	$brown: #4A3728;
+	$brown-dark: #3D2E22;
+	$cream: #E8DCC8;
+	$panel-bg: #F7F0E6;
+	$orange: #FF8C42;
+	$gold: #FFD88A;
+	$search-icon-filter: brightness(0) saturate(100%) invert(23%) sepia(14%) saturate(1067%) hue-rotate(349deg) brightness(95%) contrast(91%);
+
+	/* ===== 鏍瑰鍣� ===== */
+	.search_root {
+		display: contents;
+	}
+
+	/* ===== PK 閫夋嫨鍟嗗搧寮规 ===== */
+	.pk_search {
 		position: fixed;
-		z-index: 4;
+		z-index: 1000;
+		inset: 0;
+		display: flex;
+		align-items: center;
+		justify-content: center;
+		padding: 24px 16px;
+		box-sizing: border-box;
+		background: rgba(61, 46, 34, 0.48);
+		backdrop-filter: blur(4px);
+	}
+
+	.pk_search_panel {
+		width: 100%;
+		max-width: 640px;
+		max-height: min(78vh, 680px);
+		display: flex;
+		flex-direction: column;
+		border-radius: 18px;
+		overflow: hidden;
+		box-shadow: 0 20px 48px rgba(61, 46, 34, 0.28);
+		border: 1px solid rgba(255, 255, 255, 0.12);
+	}
+
+	.pk_search_head {
+		display: flex;
+		align-items: center;
+		justify-content: space-between;
+		padding: 16px 18px;
+		flex-shrink: 0;
+	}
+
+	.pk_search_head_left {
+		display: flex;
+		align-items: center;
+		gap: 10px;
+		min-width: 0;
+	}
+
+	.pk_search_title {
+		font-size: 17px;
+		font-weight: 600;
+		line-height: 1.2;
+	}
+
+	.pk_search_badge {
+		padding: 3px 10px;
+		border-radius: 999px;
+		font-size: 11px;
+		font-weight: 600;
+		color: $brown;
+		background: $gold;
+		line-height: 1.3;
+		flex-shrink: 0;
+	}
+
+	.pk_search_close {
+		width: 32px;
+		height: 32px;
+		display: flex;
+		align-items: center;
+		justify-content: center;
+		border-radius: 50%;
+		background: rgba(255, 255, 255, 0.12);
+		cursor: pointer;
+		flex-shrink: 0;
+		transition: background 0.15s;
+
+		&:hover {
+			background: rgba(255, 255, 255, 0.22);
+		}
+
+		image {
+			width: 18px;
+			height: 18px;
+			opacity: 0.92;
+		}
+	}
+
+	.pk_search_body {
+		flex: 1;
+		min-height: 0;
+		display: flex;
+		flex-direction: column;
+		padding: 16px 16px 14px;
+		background: linear-gradient(180deg, #FFF9EC 0%, $panel-bg 100%);
+	}
+
+	.pk_search_cat {
+		display: flex;
+		align-items: center;
+		gap: 8px;
+		margin-bottom: 12px;
+		padding: 8px 12px;
+		border-radius: 10px;
+		background: rgba(255, 255, 255, 0.72);
+		border: 1px solid rgba(74, 55, 40, 0.08);
+	}
+
+	.pk_search_cat_label {
+		font-size: 12px;
+		color: rgba(74, 55, 40, 0.55);
+		flex-shrink: 0;
+	}
+
+	.pk_search_cat_name {
+		font-size: 14px;
+		font-weight: 600;
+		color: $brown;
+		line-height: 1.3;
+	}
+
+	.pk_search_bar {
+		display: flex;
+		align-items: center;
+		gap: 8px;
+		padding: 10px 14px;
+		margin-bottom: 12px;
+		border-radius: 24px;
+		box-shadow: 0 2px 10px rgba(74, 55, 40, 0.08);
+		border: 1px solid rgba(74, 55, 40, 0.08);
+		flex-shrink: 0;
+	}
+
+	.pk_search_bar_icon {
+		width: 15px;
+		height: 15px;
+		flex-shrink: 0;
+		filter: $search-icon-filter;
+	}
+
+	.pk_search_bar_divider {
+		width: 1px;
+		height: 16px;
+		background: rgba(74, 55, 40, 0.15);
+		flex-shrink: 0;
+	}
+
+	.pk_search_bar_input {
+		flex: 1;
+		min-width: 0;
+		height: 24px;
+		font-size: 14px;
+		color: $brown;
+		background: transparent;
+		border: none;
+		outline: none;
+	}
+
+	.pk_search_placeholder {
+		color: rgba(74, 55, 40, 0.38);
+		font-size: 14px;
+	}
+
+	.pk_search_table_head {
+		display: grid;
+		grid-template-columns: 1fr auto;
+		gap: 12px;
+		padding: 8px 14px;
+		margin-bottom: 6px;
+		border-radius: 10px;
+		background: rgba(74, 55, 40, 0.08);
+		flex-shrink: 0;
+
+		.col {
+			font-size: 12px;
+			font-weight: 600;
+			color: $brown;
+			opacity: 0.85;
+		}
+
+		.col_price {
+			text-align: right;
+			min-width: 72px;
+		}
+	}
+
+	.pk_search_list {
+		flex: 1;
+		min-height: 0;
+		max-height: 320px;
+		overflow-y: auto;
+		border-radius: 12px;
+		background: #fff;
+		border: 1px solid rgba(74, 55, 40, 0.08);
+		box-shadow: 0 6px 18px rgba(74, 55, 40, 0.08);
+
+		&::-webkit-scrollbar {
+			width: 5px;
+		}
+
+		&::-webkit-scrollbar-thumb {
+			background: rgba(74, 55, 40, 0.28);
+			border-radius: 999px;
+		}
+	}
+
+	.pk_search_item {
+		display: grid;
+		grid-template-columns: 1fr auto;
+		gap: 12px;
+		align-items: center;
+		padding: 12px 14px;
+		cursor: pointer;
+		border-bottom: 1px solid rgba(74, 55, 40, 0.06);
+		transition: background 0.12s;
+
+		&:last-child {
+			border-bottom: none;
+		}
+
+		&:hover,
+		&--active {
+			background: rgba(255, 216, 138, 0.22);
+		}
+	}
+
+	.pk_search_item_name {
+		font-size: 14px;
+		color: $brown;
+		line-height: 1.35;
+		word-break: break-all;
+	}
+
+	.pk_search_item_price {
+		font-size: 13px;
+		font-weight: 600;
+		color: $orange;
+		min-width: 72px;
+		text-align: right;
+		flex-shrink: 0;
+	}
+
+	.pk_search_empty {
+		flex: 1;
+		min-height: 180px;
+		display: flex;
+		align-items: center;
+		justify-content: center;
+		border-radius: 12px;
+		background: rgba(255, 255, 255, 0.72);
+		border: 1px dashed rgba(74, 55, 40, 0.14);
+
+		text {
+			font-size: 14px;
+			color: rgba(74, 55, 40, 0.45);
+		}
+	}
+
+	.pk_search_footer {
+		margin-top: 10px;
+		text-align: center;
+		flex-shrink: 0;
+
+		text {
+			font-size: 12px;
+			color: rgba(74, 55, 40, 0.5);
+		}
+	}
+
+	/* ===== 棣栭〉 legacy 鎼滅储 ===== */
+	.search--legacy {
+		position: fixed;
+		z-index: 1000;
 		top: 0;
 		left: 0;
 		width: 100vw;
@@ -331,50 +673,45 @@
 		display: flex;
 		align-items: center;
 		justify-content: center;
+
 		.search_box {
 			width: 660px;
 			height: auto;
 			display: flex;
 			flex-direction: column;
+
 			.search_box_item_xl::-webkit-scrollbar {
-			    width: 6px;
-				background: rgba(255,255,255,0.5);
-				border-radius: 4px;
+				width: 5px;
+				background: rgba(232, 220, 200, 0.35);
+				border-radius: 999px;
 			}
+
 			.search_box_item_xl::-webkit-scrollbar-thumb {
-			    box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
-				background: rgba(255,255,255,0.8);
+				background: linear-gradient(180deg, rgba(74, 55, 40, 0.42) 0%, rgba(74, 55, 40, 0.28) 100%);
+				border-radius: 999px;
 			}
+
 			.search_box_item_xl {
 				width: 100%;
 				max-height: 60px;
 				min-height: 240px;
 				overflow-y: scroll;
-				background: rgba(5,35,102,0.7);
+				background: rgba(5, 35, 102, 0.7);
 				border-radius: 8px;
-				// display: flex;
-				// flex-direction: column;
+
 				.search_box_item_xl_item {
 					width: 100%;
 					height: 40px;
-					// line-height: 52px;
-					// font-size: 28px;
-					// font-family: PingFangSC-Regular, PingFang SC;
-					// font-weight: 400;
-					// color: #FFFFFF;
 					padding: 0 24px;
 					cursor: pointer;
 					box-sizing: border-box;
 					display: flex;
 					align-items: center;
-					// &:hover {
-					// 	span {
-					// 		color: #FFF200;
-					// 	}
-					// }
-					  .active {
+
+					.active {
 						color: #FFF200 !important;
-					}  
+					}
+
 					.search_box_item_xl_item_name {
 						flex: 1;
 						height: 100%;
@@ -384,75 +721,72 @@
 						white-space: nowrap;
 						text-overflow: ellipsis;
 						font-size: 16px;
-						font-family: PingFangSC-Regular, PingFang SC;
-						font-weight: 400;
 						color: #FFFFFF;
-						// &:hover {
-						// 	color: #FFF200;
-						// }
 					}
+
 					span {
 						flex-shrink: 0;
 						font-size: 16px;
-						font-family: PingFangSC-Regular, PingFang SC;
-						font-weight: 400;
 						color: #FFFFFF;
 						margin-left: 10px;
 					}
 				}
 			}
+
 			.search_box_item {
 				width: 100%;
 				height: 72px;
-				background: rgba(5,35,102,0.7);
+				background: rgba(5, 35, 102, 0.7);
 				border-radius: 32px;
 				display: flex;
 				align-items: center;
 				padding: 0 30px;
 				box-sizing: border-box;
 				margin-bottom: 16px;
-				&:last-child {
-					margin-bottom: 0 !important;
-				}
+
 				.search_box_item_right {
 					display: flex;
 					align-items: center;
+					flex: 1;
+					min-width: 0;
+
 					text {
 						flex-shrink: 0;
 						font-size: 16px;
-						font-family: PingFangSC-Regular, PingFang SC;
-						font-weight: 400;
 						color: #FFFFFF;
 						margin-right: 10px;
 					}
-					input {
+
+					input,
+					.search_box_item_right_ipt {
 						flex: 1;
+						min-width: 0;
 						height: 100%;
 						font-size: 16px;
-						font-family: PingFangSC-Regular, PingFang SC;
-						font-weight: 400;
 						color: #FFFFFF;
+						background: transparent;
+						border: none;
+						outline: none;
 					}
 				}
+
 				.icon {
 					width: 20px;
 					height: 20px;
 					flex-shrink: 0;
 					margin-right: 18px;
+
 					image {
 						width: 100%;
 						height: 100%;
 					}
 				}
+
 				.placeholder {
-					height: 62px;
-					line-height: 62px;
+					color: rgba(255, 255, 255, 0.5);
 					font-size: 16px;
-					font-family: PingFangSC-Regular, PingFang SC;
-					font-weight: 400;
-					color: rgba(255,255,255,0.5);
 				}
 			}
 		}
 	}
-</style>
\ No newline at end of file
+</style>
diff --git a/h5/pages.json b/h5/pages.json
index 4143ade..86d9894 100644
--- a/h5/pages.json
+++ b/h5/pages.json
@@ -9,6 +9,14 @@
 			}
 		},
 		{
+			"path": "pages/index_3/index",
+			"style": {
+				"navigationBarTitleText": "棰勯�夋竻鍗曠郴缁�",
+				"navigationStyle": "custom",
+				"enablePullDownRefresh": true
+			}
+		},
+		{
 			"path": "pages/index_1/index",
 			"style": {
 				"navigationBarTitleText": "棰勯�夋竻鍗曠郴缁�",
diff --git a/h5/pages/index_2/index.vue b/h5/pages/index_2/index.vue
index d8b3355..9e1ca04 100644
--- a/h5/pages/index_2/index.vue
+++ b/h5/pages/index_2/index.vue
@@ -288,6 +288,7 @@
 	import search from '@/components/search.vue'
 	import searchShopList from '@/components/searchShopList.vue'
 	import { categoryList, goodsList, brandList, h5Image, logout, getByLoginNew, listForH5 } from '@/apis/index.js'
+	import { ANCHOR_PAGE_V3, getAnchorHomePath, normalizeAnchorPageVersion } from '@/utils/anchorHome.js'
 	import { mapState } from 'vuex'
 	export default {
 		data() {
@@ -364,10 +365,20 @@
 		onLoad() {
 			getByLoginNew({})
 				.then(res => {
-					if (!res.data) {
+					const data = (res && res.data) || res
+					if (!data) {
 						return
 					}
-					this.configuration = JSON.parse(res.data.newParam)
+					const version = normalizeAnchorPageVersion(data.anchorPageVersion)
+					if (version === ANCHOR_PAGE_V3) {
+						uni.redirectTo({ url: getAnchorHomePath(version) })
+						return
+					}
+					const paramStr = data.newParam
+					if (!paramStr) {
+						return
+					}
+					this.configuration = JSON.parse(paramStr)
 					this.fontColor = this.configuration.pull.selType === 1 ? this.percentage(this.configuration.pull.selColor, this.configuration.pull.selAlpha) : 'rgba(255, 220, 108, 1)'
 					if (this.configuration.bgImg.type === 0) {
 						this.bgImg = require('@/static/mb.png')
diff --git a/h5/pages/index_3/index.vue b/h5/pages/index_3/index.vue
new file mode 100644
index 0000000..c748635
--- /dev/null
+++ b/h5/pages/index_3/index.vue
@@ -0,0 +1,3101 @@
+<template>
+	<view class="page">
+		<view class="page_loading" v-if="pageLoading">
+			<view class="loading_spinner"></view>
+			<text class="loading_text">鏁版嵁鍔犺浇涓�...</text>
+		</view>
+
+		<template v-else>
+		<view class="page_bg" v-if="configuration && bgImg" :style="{ backgroundImage: 'url(' + bgImg + ')' }"></view>
+		<view class="page_bg" v-else-if="configuration" :style="{ background: mainBg }"></view>
+		<view class="page_bg default-bg" v-else></view>
+
+		<view class="page_content" :style="contentStyle">
+			<!-- 椤堕儴 Banner -->
+			<view class="banner_wrap">
+				<view class="banner" :class="{ 'banner--custom': hasCustomTopImg }">
+					<image :src="topImg" mode="widthFix" class="banner_img"></image>
+				</view>
+				<view class="user_bar user_bar--banner">
+					<text>{{ username }}</text>
+					<image src="@/static/ic_logout@2x.png" mode="widthFix" @click="loginOut" @mouseenter="playHoverSound"></image>
+				</view>
+			</view>
+
+			<view class="nav_back" v-if="status > 0" @click="onClickSound(fanhuiPage)" @mouseenter="playHoverSound">
+				<image src="@/static/ic_left@2x.png" mode="widthFix"></image>
+			</view>
+
+			<!-- 鎻愪氦璁㈠崟 + 棰勭畻鏍� + 绮惧搧浼橀�夛細鍏ㄩ〉闈㈠父椹� -->
+			<view class="home_sticky_header" :style="{ background: mainBg }">
+				<view class="submit_row">
+					<view
+						class="submit_btn submit_btn--icon"
+						:style="submitBtnStyle"
+						@mouseenter="playHoverSound"
+						@click="onClickSound(submitOrderHandler)">
+						<image
+							class="submit_icon_img"
+							:src="submitOrderIconUrl"
+							mode="aspectFit"></image>
+						<text>鎻愪氦璁㈠崟</text>
+					</view>
+				</view>
+
+				<view class="home_top_section" :class="{ 'home_top_section--compact': status === 2 }" :style="homeTopSectionStyle">
+					<view class="order_bar">
+						<view class="order_left">
+							<text class="order_label">鐢ㄦ埛棰勭畻</text>
+							<text class="order_currency">楼</text>
+							<input type="digit" v-model="userBudget" placeholder="0" class="budget_input" />
+							<view class="remain_inline">
+								<text class="remain_label">鍓╀綑缁忚垂</text>
+								<text class="remain_val">楼 {{ remainingBudget }}</text>
+							</view>
+						</view>
+						<view class="order_right">
+							<view class="order_no_row">
+								<text class="order_label">璁㈠崟缂栧彿</text>
+								<text class="order_no">{{ orderNo }}</text>
+							</view>
+						</view>
+					</view>
+
+					<view class="featured" v-if="featuredList.length">
+						<view class="featured_left">
+							<text class="featured_title">绮惧搧浼橀��</text>
+							<text class="featured_sub">瀹炴祴澶氱淮缁煎悎璇勫畾</text>
+						</view>
+						<view class="featured_products">
+							<view
+								class="featured_item"
+								v-for="(feat, fi) in featuredList"
+								:key="fi"
+								@mouseenter="playHoverSound">
+								<view class="featured_img_wrap">
+									<image
+										v-if="featuredImgUrl(feat)"
+										:src="featuredImgUrl(feat)"
+										mode="aspectFit"
+										class="featured_img"></image>
+									<view v-else class="featured_img_placeholder"></view>
+								</view>
+								<view class="featured_info">
+									<text class="featured_name">{{ featuredTitle(feat) }}</text>
+									<view class="featured_tag_row">
+										<text class="tag tag_dim" v-if="feat.tags && feat.tags[0]">{{ feat.tags[0] }}</text>
+										<text
+											class="tag"
+											:class="fi === 0 ? 'tag_blue' : 'tag_green'"
+											v-if="feat.tags && feat.tags[1]">{{ feat.tags[1] }}</text>
+									</view>
+								</view>
+							</view>
+						</view>
+					</view>
+
+					<view class="table_head_wrap" v-if="status === 0">
+						<view class="shop_table_head">
+							<view
+								class="col col_cat category_dropdown_anchor"
+								@mouseenter="onCategoryHeadEnter"
+								@mouseleave="onCategoryHeadLeave"
+								@click="onCategoryHeadClick">
+								<text>鍝佺被</text>
+								<text class="arrow">鈻�</text>
+								<view
+									class="category_picker"
+									v-if="showCategoryPicker"
+									:style="dropdownStyle"
+									@click.stop>
+									<view
+										class="category_picker_item"
+										v-for="cat in availableCategories"
+										:key="cat.id"
+										@click="onClickSound(() => addCategoryRow(cat))"
+										@mouseenter="playHoverSound">
+										<image v-if="cat.imgurl" :src="cat.prefixUrl + cat.imgurl" mode="aspectFit"></image>
+										<text>{{ cat.name }}</text>
+									</view>
+									<view class="category_picker_empty" v-if="!availableCategories.length">鏆傛棤鍙坊鍔犲搧绫�</view>
+								</view>
+							</view>
+							<view class="col col_model">鍨嬪彿</view>
+							<view class="col col_zd">鏃楄埌浠�</view>
+							<view class="col col_price">鎸囧浠�</view>
+						</view>
+					</view>
+
+					<view class="table_head_wrap product_list_head_wrap" v-if="status === 1">
+						<view class="shop_table_head product_shop_table_head">
+							<view
+								class="col col_cat product_brand_col category_dropdown_anchor"
+								@mouseenter="onBrandHeadEnter"
+								@mouseleave="onBrandHeadLeave"
+								@click="onBrandHeadClick">
+								<text>鍝佺墝</text>
+								<text class="arrow">鈻�</text>
+								<text class="brand_echo_inline">{{ shopPageBrand.name || '鍏ㄩ儴' }}</text>
+								<view
+									class="brand_picker"
+									v-if="showBrandPicker && brandData.length"
+									:style="dropdownStyle"
+									@click.stop>
+									<view
+										class="brand_picker_item"
+										v-for="(brand, bi) in brandData"
+										:key="brand.id || 'all-' + bi"
+										:class="{ active: String(shopPageBrand.id) === String(brand.id) }"
+										@click="onClickSound(() => selectBrand(brand))"
+										@mouseenter="playHoverSound">
+										{{ brand.name }}
+									</view>
+								</view>
+							</view>
+							<view class="col col_model product_search_col">
+								<view class="product_search_inline" :style="searchBarStyle">
+									<image class="search_icon" src="@/static/ic_search@2x.png" mode="aspectFit"></image>
+									<view class="search_divider"></view>
+									<input type="text" v-model="title" placeholder="鎼滅储鍨嬪彿" @input="onProductSearch" />
+								</view>
+							</view>
+							<view class="col col_zd product_cat_col product_cat_col--span">
+								<view class="product_cat_inner">
+									<image
+										v-if="productListCategoryImg"
+										:src="productListCategoryImg"
+										mode="aspectFit"
+										class="product_cat_icon"></image>
+									<text class="cat_line">{{ productListCategoryText }}</text>
+								</view>
+							</view>
+						</view>
+					</view>
+				</view>
+			</view>
+
+			<!-- 棣栭〉琛ㄦ牸 -->
+			<template v-if="status === 0">
+				<view class="shop_table" :style="{ background: mainBg }">
+					<view class="shop_table_body">
+						<view class="shop_table_empty" v-if="!shopList.length">
+							<text>鏆傛棤鍝佺被鏁版嵁</text>
+						</view>
+						<view
+							class="shop_row"
+							v-for="(row, index) in shopList"
+							:key="index"
+							:id="'shop_row_' + index"
+							:class="{ expanded: hoverRowIndex === index }"
+							@mouseenter="onRowHover(index, true)"
+							@mouseleave="onRowHover(index, false)"
+							@click="onRowTap(index)">
+
+							<!-- 鏅�氳 -->
+							<view class="shop_row_main" v-if="hoverRowIndex !== index">
+								<view class="row_cat_cell col col_cat" @click.stop="onClickSound(() => openProductList(index))" @mouseenter="playHoverSound">
+									<image v-if="row.categoryImgurl" :src="row.categoryImgurl" mode="aspectFit"></image>
+									<text class="cell_ellipsis">{{ row.categoryName }}</text>
+								</view>
+								<view class="row_info_group">
+									<view class="col col_model" :class="{ col_model_link: row.name }" @click.stop="onClickSound(() => onModelClick(index))">
+										<text class="cell_ellipsis">{{ row.name || '' }}</text>
+									</view>
+									<view class="col col_zd">
+										<text class="cell_ellipsis">{{ row.zdPrice || '' }}</text>
+									</view>
+									<view class="col col_price">
+										<input type="digit" v-model="row.price" placeholder="" />
+									</view>
+								</view>
+							</view>
+
+							<!-- 鎮诞灞曞紑锛堝弬鑰冩埅鍥撅級 -->
+							<view class="shop_row_expand" v-else>
+								<view class="expand_cat_card" @click.stop="onClickSound(() => openProductList(index))" @mouseenter="playHoverSound">
+									<view class="row_delete" @click.stop="onClickSound(() => removeRow(index))" @mouseenter="playHoverSound">
+										<text>脳</text>
+									</view>
+									<image v-if="row.categoryImgurl" :src="row.categoryImgurl" mode="aspectFit" class="expand_cat_img"></image>
+									<text class="expand_cat_name">{{ row.categoryName }}</text>
+								</view>
+								<view class="expand_panel">
+									<view class="expand_data_row" v-if="row.name">
+										<text class="expand_model expand_model_link" @click.stop="onClickSound(() => openRowProductPk(index))">{{ row.name }}</text>
+										<text class="expand_zd">{{ row.zdPrice }}</text>
+										<input type="digit" v-model="row.price" class="expand_price_input" />
+									</view>
+									<view class="sub_cat_grid" v-if="row.children && row.children.length">
+										<view
+											class="sub_cat_icon_item"
+											v-for="sub in row.children.slice(0, 5)"
+											:key="sub.id"
+											@click.stop="onClickSound(() => selectSubCategory(index, sub))"
+											@mouseenter="playHoverSound">
+											<image :src="subIcon(sub, row)" mode="aspectFit"></image>
+											<text>{{ sub.name }}</text>
+										</view>
+									</view>
+									<view class="expand_search_wrap">
+										<view class="expand_search" :style="searchBarStyle">
+											<image class="search_icon" src="@/static/ic_search@2x.png" mode="aspectFit"></image>
+											<input type="text" v-model="row.modelVal" placeholder="鎼滅储鍟嗗搧鍚嶇О/鍨嬪彿" @input="searchModel(row)" />
+										</view>
+										<view class="model_suggest_list" v-if="hasModelKeyword(row) && modelOptions(row).length">
+											<view
+												class="model_suggest_item"
+												v-for="(g, gi) in modelOptions(row)"
+												:key="g.id || gi"
+												@click.stop="onClickSound(() => selectGoodsFromSearch(index, g))"
+												@mouseenter="playHoverSound">
+												{{ g.name }}
+											</view>
+										</view>
+										<view class="model_suggest_empty" v-else-if="hasModelKeyword(row)">鏆傛棤鍖归厤鍟嗗搧</view>
+									</view>
+								</view>
+							</view>
+						</view>
+					</view>
+				</view>
+			</template>
+
+			<!-- 鍟嗗搧鍒楄〃 -->
+			<template v-if="status === 1">
+				<view class="product_page" :style="{ background: mainBg }">
+					<view class="product_grid">
+						<view
+							class="product_card"
+							v-for="(item, pi) in displayProducts"
+							:key="item.id || pi"
+							@click="onClickSound(() => clickProduct(item))"
+							@mouseenter="playHoverSound">
+							<view class="product_card_img">
+								<image :src="productImg(item)" mode="aspectFit"></image>
+							</view>
+							<view class="product_card_info">
+								<text class="product_brand">{{ item.brandName || '' }}</text>
+								<text class="product_model">{{ item.name }}</text>
+								<text class="product_price_label">鎸囧浠� 楼 {{ item.price || '' }}</text>
+							</view>
+						</view>
+						<view class="product_card placeholder" v-if="!displayProducts.length">
+							<text>鏆傛棤鍟嗗搧</text>
+						</view>
+					</view>
+				</view>
+			</template>
+
+			<!-- PK -->
+			<template v-if="status === 2">
+				<view class="pk_page" :style="pkShowcaseTheme">
+					<view class="pk_cards">
+						<view class="pk_side" @mouseenter="pkHoverSide = 'left'" @mouseleave="pkHoverSide = ''">
+							<view class="pk_delete" v-if="pkHoverSide === 'left' && leftShop.name" @click.stop="onClickSound(clearLeftShop)" @mouseenter="playHoverSound">脳</view>
+							<template v-if="leftShop.name">
+								<view class="pk_showcase">
+									<view class="pk_showcase_glow"></view>
+									<view class="pk_showcase_frame">
+										<view class="pk_showcase_shine"></view>
+										<view class="pk_showcase_visual">
+											<view
+												class="pk_showcase_img_card"
+												@click.stop="onClickSound(() => openPkImagePreview('left'))"
+												@mouseenter="playHoverSound">
+												<image
+													class="pk_showcase_img"
+													:src="pkImgUrl(leftShop)"
+													mode="aspectFit"></image>
+											</view>
+											<view class="pk_showcase_reflect" aria-hidden="true">
+												<image
+													class="pk_showcase_reflect_img"
+													:src="pkImgUrl(leftShop)"
+													mode="aspectFit"></image>
+											</view>
+											<view class="pk_showcase_ground" aria-hidden="true"></view>
+										</view>
+									</view>
+								</view>
+								<view class="pk_podium"></view>
+								<view class="pk_detail">
+									<view class="pk_detail_head" :style="tableHeaderStyle">{{ leftShop.name }}</view>
+									<view class="pk_detail_body">
+										<view class="pk_price_cell">
+											<text class="pk_price_label">鏈熼棿浠�</text>
+											<text class="pk_price_val">楼{{ leftShop.zdPrice }}</text>
+										</view>
+										<view class="pk_price_cell pk_price_cell--hot">
+											<text class="pk_price_label">鎸囧浠�</text>
+											<text class="pk_price_val">楼{{ leftShop.price }}</text>
+										</view>
+									</view>
+								</view>
+							</template>
+							<view class="pk_empty" v-else @click="onClickSound(() => openSearch('left'))" @mouseenter="playHoverSound">
+								<text>+ 閫夋嫨鍟嗗搧</text>
+							</view>
+						</view>
+
+						<view class="pk_vs" :style="{ color: pkVsColor }">VS</view>
+
+						<view class="pk_side" @mouseenter="pkHoverSide = 'right'" @mouseleave="pkHoverSide = ''">
+							<view class="pk_delete" v-if="pkHoverSide === 'right' && rightShop.name" @click.stop="onClickSound(clearRightShop)" @mouseenter="playHoverSound">脳</view>
+							<template v-if="rightShop.name">
+								<view class="pk_showcase">
+									<view class="pk_showcase_glow"></view>
+									<view class="pk_showcase_frame">
+										<view class="pk_showcase_shine"></view>
+										<view class="pk_showcase_visual">
+											<view
+												class="pk_showcase_img_card"
+												@click.stop="onClickSound(() => openPkImagePreview('right'))"
+												@mouseenter="playHoverSound">
+												<image
+													class="pk_showcase_img"
+													:src="pkImgUrl(rightShop)"
+													mode="aspectFit"></image>
+											</view>
+											<view class="pk_showcase_reflect" aria-hidden="true">
+												<image
+													class="pk_showcase_reflect_img"
+													:src="pkImgUrl(rightShop)"
+													mode="aspectFit"></image>
+											</view>
+											<view class="pk_showcase_ground" aria-hidden="true"></view>
+										</view>
+									</view>
+								</view>
+								<view class="pk_podium"></view>
+								<view class="pk_detail">
+									<view class="pk_detail_head" :style="tableHeaderStyle">{{ rightShop.name }}</view>
+									<view class="pk_detail_body">
+										<view class="pk_price_cell">
+											<text class="pk_price_label">鏈熼棿浠�</text>
+											<text class="pk_price_val">楼{{ rightShop.zdPrice }}</text>
+										</view>
+										<view class="pk_price_cell pk_price_cell--hot">
+											<text class="pk_price_label">鎸囧浠�</text>
+											<text class="pk_price_val">楼{{ rightShop.price }}</text>
+										</view>
+									</view>
+								</view>
+							</template>
+							<view class="pk_empty" v-else @click="onClickSound(() => openSearch('right'))" @mouseenter="playHoverSound">
+								<text>+ 閫夋嫨鍟嗗搧</text>
+							</view>
+						</view>
+					</view>
+
+					<view class="pk_params" v-if="leftShop.name || rightShop.name">
+						<view class="pk_param_col">
+							<template v-if="leftShop.name">
+								<view class="pk_param_head" :class="{ 'pk_param_head--solo': !pkcontent }" :style="tableHeaderStyle">
+									<view class="pk_action_group">
+										<view class="pk_change" @click.stop="onClickSound(() => openSearch('left'))" @mouseenter="playHoverSound">
+											<text class="pk_btn_ico">鈫�</text>
+											<text class="pk_btn_txt">鎹釜鍟嗗搧</text>
+										</view>
+										<view class="pk_confirm pk_confirm--inhead" @click="onClickSound(confirmLeftToRow)" @mouseenter="playHoverSound">
+											<text class="pk_btn_ico pk_btn_ico--check">鉁�</text>
+											<text class="pk_btn_txt">閫夌敤宸︿晶</text>
+										</view>
+									</view>
+								</view>
+								<view class="pk_param_pills" v-if="pkcontent">
+									<view class="pill" v-for="(p, pi) in param1" :key="'m-' + pi">
+										<text class="pill_label">{{ p.name }}</text>
+										<text class="pill_val">{{ p.val }}</text>
+									</view>
+									<view class="pill pill_diff" v-for="(p, pi) in (leftShop.param2 || [])" :key="'u-' + pi">
+										<text class="pill_label">{{ p.name }}</text>
+										<text class="pill_val">{{ p.val }}</text>
+									</view>
+								</view>
+							</template>
+						</view>
+						<view class="pk_vs pk_vs--spacer" aria-hidden="true">VS</view>
+						<view class="pk_param_col">
+							<template v-if="rightShop.name">
+								<view class="pk_param_head" :class="{ 'pk_param_head--solo': !pkcontent }" :style="tableHeaderStyle">
+									<view class="pk_action_group">
+										<view class="pk_change" @click.stop="onClickSound(() => openSearch('right'))" @mouseenter="playHoverSound">
+											<text class="pk_btn_ico">鈫�</text>
+											<text class="pk_btn_txt">鎹釜鍟嗗搧</text>
+										</view>
+										<view class="pk_confirm pk_confirm--inhead" @click="onClickSound(confirmRightToRow)" @mouseenter="playHoverSound">
+											<text class="pk_btn_ico pk_btn_ico--check">鉁�</text>
+											<text class="pk_btn_txt">閫夌敤鍙充晶</text>
+										</view>
+									</view>
+								</view>
+								<view class="pk_param_pills" v-if="pkcontent">
+									<view class="pill" v-for="(p, pi) in param2" :key="'m-' + pi">
+										<text class="pill_label">{{ p.name }}</text>
+										<text class="pill_val">{{ p.val }}</text>
+									</view>
+									<view class="pill pill_diff" v-for="(p, pi) in (rightShop.param2 || [])" :key="'u-' + pi">
+										<text class="pill_label">{{ p.name }}</text>
+										<text class="pill_val">{{ p.val }}</text>
+									</view>
+								</view>
+							</template>
+						</view>
+					</view>
+				</view>
+			</template>
+
+			<search
+				ref="searchRef"
+				:search="configuration ? configuration.search : null"
+				:conMark="configuration"
+				:fontColor="accentColor"
+				:categoryList="categoryList"
+				:shopList="shopPageData"
+				:allGoods="allGoods"
+				:categoryid="currentCategoryId"
+				:status="status"
+				:categoryName="currentCategoryName"
+				:excludeGoodsId="pkSearchExcludeGoodsId"
+				@result="result" />
+			<bigImg ref="pkBigImg" :imgList="pkPreviewImgList" />
+		</view>
+		</template>
+	</view>
+</template>
+
+<script>
+import { mapState } from 'vuex'
+import search from '@/components/search.vue'
+import bigImg from '@/components/bigImg.vue'
+import { playHoverSound, playClickSound, setupSoundUnlock } from '@/utils/sound.js'
+import { ANCHOR_PAGE_V3, getAnchorHomePath, normalizeAnchorPageVersion } from '@/utils/anchorHome.js'
+import { filterGoods, filterBrandsByCategory, matchKeyword } from '@/utils/goodsFilter.js'
+import {
+	loadH5InitData,
+	generateOrderNo,
+	submitOrder,
+	logout
+} from '@/apis/index.js'
+
+const DEFAULT_MAIN = '#E8DCC8'
+const DEFAULT_HEADER = '#4A3728'
+const DEFAULT_ROW = '#FFFFFF'
+
+export default {
+	components: { search, bigImg },
+	data() {
+		return {
+			status: 0,
+			pageLoading: true,
+			configuration: null,
+			topImg: require('@/static/banner.png'),
+			submitOrderIcon: require('@/static/ic_submit_order@2x.png'),
+			bgImg: '',
+			webParamPath: '',
+
+			allGoods: [],
+			allBrands: [],
+
+			shopList: [],
+			categoryList: [],
+			userBudget: 0,
+			orderNo: '',
+			sessionCreateTime: null,
+
+			showCategoryPicker: false,
+			categoryPickerTimer: null,
+			showBrandPicker: false,
+			brandPickerTimer: null,
+			hoverRowIndex: -1,
+			activeRowIndex: -1,
+
+			currentCategoryId: '',
+			currentCategoryName: '',
+			currentSubCategoryId: '',
+			currentSubCategoryName: '',
+
+			leftShop: {},
+			rightShop: {},
+			param1: [],
+			param2: [],
+			pkcontent: false,
+			pkHoverSide: '',
+			pkPreviewImgList: [],
+			pkSearchSide: '',
+
+			shopPageData: [],
+			shopPageDataSou: [],
+			shopPageBrand: { id: '', name: '' },
+			brandData: [],
+			title: '',
+
+			fontColor: '#FFDC6C'
+		}
+	},
+	computed: {
+		...mapState(['User']),
+		remainingBudget() {
+			const budget = Number(this.userBudget) || 0
+			const used = this.shopList.reduce((sum, row) => sum + (Number(row.price) || 0), 0)
+			return (budget - used).toFixed(2)
+		},
+		contentStyle() {
+			const w = this.configuration ? this.configuration.rangeSize : 750
+			return { maxWidth: w + 'px', minWidth: w + 'px' }
+		},
+		mainBg() {
+			if (!this.configuration) return DEFAULT_MAIN
+			const pageBg = this.configuration.pageBg
+			if (pageBg) {
+				const mode = Number(pageBg.mode) || 0
+				if (mode === 1) {
+					return this.percentage(pageBg.bgColor || DEFAULT_MAIN, pageBg.alpha || 100)
+				}
+				return DEFAULT_MAIN
+			}
+			const main = this.configuration.main
+			if (!main) return DEFAULT_MAIN
+			if (Number(main.bgType) === 0) return DEFAULT_MAIN
+			return this.percentage(main.bgColor || DEFAULT_MAIN, main.alpha || 100)
+		},
+		themeStyle() {
+			const theme = this.resolveThemeConfig()
+			return {
+				background: Number(theme.bgType) === 1 ? (theme.bgColor || DEFAULT_HEADER) : DEFAULT_HEADER,
+				color: Number(theme.colorType) === 1 ? (theme.color || '#FFFFFF') : '#FFFFFF'
+			}
+		},
+		homeTopSectionStyle() {
+			const { background: bg, color } = this.themeStyle
+			return {
+				background: bg,
+				color,
+				'--home-top-bg': bg
+			}
+		},
+		headerStyle() {
+			return this.themeStyle
+		},
+		tableHeaderStyle() {
+			return this.themeStyle
+		},
+		rowStyle() {
+			const t = (this.configuration && this.configuration.table) || {}
+			return { background: t.rowBg || DEFAULT_ROW }
+		},
+		dropdownStyle() {
+			return { background: this.themeStyle.background }
+		},
+		searchBarStyle() {
+			const s = (this.configuration && this.configuration.search) || {}
+			const defaultBg = 'rgba(255, 255, 255, 0.92)'
+			if (Number(s.bgType) !== 1) {
+				return { background: defaultBg }
+			}
+			return {
+				background: this.percentage(s.bgColor || '#FFFFFF', s.bgAlpha || 100)
+			}
+		},
+		productCardStyle() {
+			const p = (this.configuration && this.configuration.productList) || {}
+			return {
+				background: p.bgType === 1 ? this.percentage(p.bgColor || DEFAULT_ROW, p.bgAlpha || 100) : DEFAULT_ROW
+			}
+		},
+		pkVsColor() {
+			return (this.configuration && this.configuration.pk && this.configuration.pk.vsColor) || '#FF8C42'
+		},
+		pkThemeColor() {
+			const pk = (this.configuration && this.configuration.pk) || {}
+			if (Number(pk.bgColorType) === 1) {
+				return pk.bgColor || '#FF8C42'
+			}
+			if (pk.bgColorType == null && pk.vsColor) {
+				return pk.vsColor
+			}
+			return '#FF8C42'
+		},
+		pkShowcaseTheme() {
+			const primary = this.themeStyle.background
+			const accent = this.pkThemeColor
+			const gold = this.fontColor || '#FFD88A'
+			return {
+				'--pk-show-primary': primary,
+				'--pk-show-accent': accent,
+				'--pk-show-gold': gold,
+				'--pk-show-primary-soft': this.hexToRgba(primary, 0.22),
+				'--pk-show-accent-soft': this.hexToRgba(accent, 0.38),
+				'--pk-show-gold-soft': this.hexToRgba(gold, 0.5),
+				'--pk-show-accent-faint': this.hexToRgba(accent, 0.12)
+			}
+		},
+		accentColor() {
+			return this.fontColor
+		},
+		pkSearchExcludeGoodsId() {
+			if (this.pkSearchSide === 'left' && this.rightShop && this.rightShop.id != null) {
+				return this.rightShop.id
+			}
+			if (this.pkSearchSide === 'right' && this.leftShop && this.leftShop.id != null) {
+				return this.leftShop.id
+			}
+			return ''
+		},
+		topLevelCategories() {
+			return (this.categoryList || []).filter(c => c.parentId == null || c.parentId === 0)
+		},
+		availableCategories() {
+			return this.topLevelCategories
+		},
+		username() {
+			return (this.User && this.User.username) ? this.User.username : ''
+		},
+		featuredList() {
+			const featured = this.configuration && this.configuration.featured
+			if (!Array.isArray(featured)) return []
+			return featured.slice(0, 2)
+		},
+		displayProducts() {
+			return Array.isArray(this.shopPageDataSou) ? this.shopPageDataSou : []
+		},
+		productListCategoryImg() {
+			const row = this.activeRowIndex >= 0 ? this.shopList[this.activeRowIndex] : null
+			return (row && row.categoryImgurl) ? row.categoryImgurl : ''
+		},
+		productListCategoryText() {
+			const level1 = (this.currentCategoryName || '').trim()
+			const level2 = (this.currentSubCategoryName || '').trim()
+			if (level1 && level2) return `${level1} / ${level2}`
+			return level1 || level2
+		},
+		listTitle() {
+			if (this.currentSubCategoryId) {
+				const row = this.shopList[this.activeRowIndex]
+				return row ? (row.subCategoryName || row.categoryName) : '鍟嗗搧鍒楄〃'
+			}
+			return this.currentCategoryName || '鍟嗗搧鍒楄〃'
+		},
+		hasCustomTopImg() {
+			const top = this.configuration && this.configuration.topImg
+			if (!top) return false
+			const type = Number(top.type)
+			return type === 1 && !!(top.imgurl || top.img)
+		},
+		submitOrderIconUrl() {
+			return this.submitOrderIcon || require('@/static/ic_submit_order@2x.png')
+		},
+		submitBtnStyle() {
+			const DEFAULT_SUBMIT_COLOR = '#4A3728'
+			const bgCfg = (this.configuration && this.configuration.submitBtnBg) || {}
+			const colorCfg = (this.configuration && this.configuration.submitBtnColor) || {}
+			const style = {
+				color: Number(colorCfg.colorType) === 1
+					? (colorCfg.color || DEFAULT_SUBMIT_COLOR)
+					: DEFAULT_SUBMIT_COLOR
+			}
+			const defaultBg = '#FFFFFF'
+			if (Number(bgCfg.bgType) !== 1) {
+				style.background = defaultBg
+			} else {
+				style.background = this.percentage(bgCfg.bgColor || defaultBg, bgCfg.bgAlpha || 100)
+			}
+			return style
+		}
+	},
+	onLoad() {
+		setupSoundUnlock()
+		this.sessionCreateTime = new Date()
+		this.loadAllData()
+	},
+	methods: {
+		playHoverSound,
+		playClickSound,
+		onClickSound(fn) {
+			playClickSound()
+			if (typeof fn === 'function') fn()
+		},
+
+		percentage(bgColor, alpha) {
+			const res = +(Number(alpha) * 2.55).toFixed(0)
+			const hex = res.toString(16).padStart(2, '0')
+			return (bgColor || DEFAULT_HEADER) + hex
+		},
+
+		resolveThemeConfig() {
+			const cfg = this.configuration
+			if (!cfg) {
+				return { bgType: 0, bgColor: DEFAULT_HEADER, colorType: 0, color: '#FFFFFF' }
+			}
+			if (cfg.theme) return cfg.theme
+			const t = cfg.table || {}
+			const h = cfg.header || {}
+			return {
+				bgType: Number(t.headerBgType) === 1 ? 1 : (Number(h.bgType) === 1 ? 1 : 0),
+				bgColor: t.headerBg || h.bgColor || DEFAULT_HEADER,
+				colorType: Number(t.headerColorType) === 1 ? 1 : (Number(h.colorType) === 1 ? 1 : 0),
+				color: t.headerColor || h.color || '#FFFFFF'
+			}
+		},
+
+		hexToRgba(hex, alpha) {
+			if (!hex) return `rgba(74, 55, 40, ${alpha})`
+			let h = String(hex).replace('#', '')
+			if (h.length === 3) {
+				h = h.split('').map(c => c + c).join('')
+			}
+			const n = parseInt(h, 16)
+			if (Number.isNaN(n)) return `rgba(74, 55, 40, ${alpha})`
+			return `rgba(${(n >> 16) & 255}, ${(n >> 8) & 255}, ${n & 255}, ${alpha})`
+		},
+
+		loadAllData() {
+			this.pageLoading = true
+			loadH5InitData().then(res => {
+				if (!res || !res.data) return
+				const version = normalizeAnchorPageVersion(res.data.anchorPageVersion)
+				if (version !== ANCHOR_PAGE_V3) {
+					uni.redirectTo({ url: getAnchorHomePath(version) })
+					return
+				}
+				this.applyInitData(res.data)
+			}).catch(() => {
+				uni.showToast({ title: '鏁版嵁鍔犺浇澶辫触', icon: 'none' })
+			}).finally(() => {
+				this.pageLoading = false
+			})
+		},
+
+		applyInitData(data) {
+			if (data.anchorParam) {
+				try {
+					this.configuration = JSON.parse(data.anchorParam)
+				} catch (e) {
+					console.error('anchorParam parse error', e)
+					this.configuration = null
+				}
+			}
+			this.applyBannerImages(data.resourcePath || '')
+			this.webParamPath = data.resourcePath || ''
+			this.categoryList = Array.isArray(data.categories) ? data.categories : []
+			this.allGoods = Array.isArray(data.goodsList) ? data.goodsList : []
+			this.allBrands = Array.isArray(data.brandList) ? data.brandList : []
+			this.orderNo = data.orderNo || ''
+			this.initShopListFromCategories()
+		},
+
+		resolveMediaUrl(raw, feat) {
+			if (!raw && feat) {
+				raw = feat.imgaddr || feat.imgurl || feat.img || ''
+			}
+			if (!raw) return ''
+			const str = String(raw)
+			const base = this.webParamPath || ''
+
+			if (/^https?:\/\//i.test(str)) {
+				const secondHttp = str.indexOf('http', str.indexOf('://') + 3)
+				if (secondHttp > 0) {
+					return str.substring(secondHttp)
+				}
+				return str
+			}
+
+			if (base && str.indexOf(base) === 0) {
+				return str
+			}
+
+			if (feat && feat.imgaddr && !feat.imgurl && !feat.img) {
+				return base ? base + String(feat.imgaddr).replace(/^\//, '') : feat.imgaddr
+			}
+
+			return base ? base + str.replace(/^\//, '') : str
+		},
+
+		resolveConfigImg(imgCfg, resourcePath, fallback) {
+			if (!imgCfg) return fallback
+			const type = Number(imgCfg.type)
+			if (Number.isNaN(type) || type === 0) return fallback
+			if (imgCfg.imgurl) {
+				if (/^https?:\/\//i.test(imgCfg.imgurl)) {
+					return imgCfg.imgurl
+				}
+				if (resourcePath) {
+					return resourcePath + String(imgCfg.imgurl).replace(/^\//, '')
+				}
+				return imgCfg.imgurl
+			}
+			if (resourcePath && imgCfg.img) {
+				return resourcePath + imgCfg.img
+			}
+			return fallback
+		},
+
+		applyBannerImages(resourcePath) {
+			const defaultBanner = require('@/static/banner.png')
+			const webParamPath = resourcePath || ''
+			const defaultSubmitIcon = require('@/static/ic_submit_order@2x.png')
+			if (!this.configuration) {
+				this.topImg = defaultBanner
+				this.submitOrderIcon = defaultSubmitIcon
+				return
+			}
+			this.topImg = this.resolveConfigImg(this.configuration.topImg, webParamPath, defaultBanner)
+			this.submitOrderIcon = this.resolveConfigImg(
+				this.configuration.submitBtnIcon,
+				webParamPath,
+				defaultSubmitIcon
+			)
+			this.bgImg = ''
+			const pageBg = this.configuration.pageBg
+			if (pageBg && Number(pageBg.mode) === 2) {
+				const resolved = this.resolveConfigImg(
+					{ type: 1, img: pageBg.img, imgurl: pageBg.imgurl },
+					webParamPath,
+					''
+				)
+				if (resolved) this.bgImg = resolved
+				return
+			}
+			const adImg = this.configuration.adImg
+			if (adImg && Number(adImg.type) === 1) {
+				const adResolved = this.resolveConfigImg(adImg, webParamPath, '')
+				if (adResolved) this.bgImg = adResolved
+			}
+		},
+
+		initShopListFromCategories() {
+			this.shopList = this.topLevelCategories.map(cat => this.createShopRow(cat))
+		},
+
+		loadOrderNo() {
+			generateOrderNo().then(res => {
+				this.orderNo = res.data || ''
+			})
+		},
+
+		goodsForRow(row, keyword) {
+			return filterGoods(this.allGoods, {
+				categoryId: row.categoryId,
+				subCategoryId: row.subCategoryId || '',
+				keyword: keyword || ''
+			})
+		},
+
+		createShopRow(cat, subCat) {
+			const children = (cat.children || []).slice(0, 5)
+			return {
+				categoryId: cat.id,
+				categoryName: cat.name,
+				categoryImgurl: cat.imgurl ? cat.prefixUrl + cat.imgurl : '',
+				subCategoryId: subCat ? subCat.id : '',
+				subCategoryName: subCat ? subCat.name : '',
+				name: '',
+				goodsId: '',
+				zdPrice: '',
+				price: '',
+				hoverActive: false,
+				modelVal: '',
+				modelSearchList: [],
+				children,
+				prefixUrl: cat.prefixUrl || ''
+			}
+		},
+
+		onCategoryHeadEnter() {
+			this.playHoverSound()
+			if (this.categoryPickerTimer) {
+				clearTimeout(this.categoryPickerTimer)
+				this.categoryPickerTimer = null
+			}
+			this.showCategoryPicker = true
+		},
+
+		onCategoryHeadLeave() {
+			this.categoryPickerTimer = setTimeout(() => {
+				this.showCategoryPicker = false
+			}, 180)
+		},
+
+		onCategoryHeadClick() {
+			const isTouch = typeof window !== 'undefined' &&
+				('ontouchstart' in window || (typeof navigator !== 'undefined' && navigator.maxTouchPoints > 0))
+			if (isTouch) {
+				this.showCategoryPicker = !this.showCategoryPicker
+			}
+		},
+
+		addCategoryRow(cat) {
+			this.shopList.push(this.createShopRow(cat))
+			this.showCategoryPicker = false
+			if (this.categoryPickerTimer) {
+				clearTimeout(this.categoryPickerTimer)
+				this.categoryPickerTimer = null
+			}
+			this.scrollToLastShopRow()
+		},
+
+		scrollToLastShopRow() {
+			const lastIndex = this.shopList.length - 1
+			if (lastIndex < 0) return
+			this.$nextTick(() => {
+				setTimeout(() => {
+					const rowId = 'shop_row_' + lastIndex
+					if (typeof document !== 'undefined') {
+						const el = document.getElementById(rowId)
+						if (el && el.scrollIntoView) {
+							el.scrollIntoView({ behavior: 'smooth', block: 'center' })
+							return
+						}
+					}
+					uni.pageScrollTo({
+						selector: '#' + rowId,
+						duration: 300
+					})
+				}, 80)
+			})
+		},
+
+		removeRow(index) {
+			this.shopList.splice(index, 1)
+			if (this.hoverRowIndex === index) this.hoverRowIndex = -1
+		},
+
+		onRowHover(index, active) {
+			this.hoverRowIndex = active ? index : -1
+		},
+
+		onRowTap(index) {
+			const isTouch = typeof window !== 'undefined' &&
+				('ontouchstart' in window || (typeof navigator !== 'undefined' && navigator.maxTouchPoints > 0))
+			if (isTouch) {
+				this.hoverRowIndex = this.hoverRowIndex === index ? -1 : index
+			}
+		},
+
+		hasModelKeyword(row) {
+			return !!(row && row.modelVal && String(row.modelVal).trim())
+		},
+
+		searchModel(row) {
+			const kw = (row.modelVal || '').trim()
+			if (!kw) {
+				row.modelSearchList = []
+				this.$forceUpdate()
+				return
+			}
+			row.modelSearchList = this.goodsForRow(row, kw)
+			this.$forceUpdate()
+		},
+
+		modelOptions(row) {
+			if (!this.hasModelKeyword(row)) return []
+			return row.modelSearchList || []
+		},
+
+		selectGoodsFromSearch(index, goods) {
+			this.applyGoodsToRow(index, goods)
+			this.hoverRowIndex = -1
+		},
+
+		selectSubCategory(index, sub) {
+			const row = this.shopList[index]
+			if (!row || !sub) return
+			this.openProductList(index, sub)
+		},
+
+		onModelClick(index) {
+			const row = this.shopList[index]
+			if (row && row.name) {
+				this.openRowProductPk(index)
+			}
+		},
+
+		openRowProductPk(index) {
+			const row = this.shopList[index]
+			if (!row || !row.name) return
+			let goods = null
+			if (row.goodsId && this.allGoods && this.allGoods.length) {
+				goods = this.allGoods.find(g => String(g.id) === String(row.goodsId))
+			}
+			if (goods) {
+				goods = JSON.parse(JSON.stringify(goods))
+				goods.price = row.price
+				goods.zdPrice = row.zdPrice
+			} else {
+				goods = {
+					id: row.goodsId,
+					name: row.name,
+					zdPrice: row.zdPrice,
+					price: row.price,
+					prefixUrl: row.prefixUrl,
+					imgurl: row.imgurl,
+					multifileList: row.multifileList,
+					goodsParamList: row.goodsParamList,
+					brandId: row.brandId,
+					brandName: row.brandName,
+					categoryId: row.categoryId
+				}
+			}
+			this.activeRowIndex = index
+			this.currentCategoryId = row.categoryId
+			this.currentCategoryName = row.categoryName
+			this.currentSubCategoryId = row.subCategoryId || ''
+			this.currentSubCategoryName = row.subCategoryName || ''
+			this.leftShop = goods
+			this.rightShop = {}
+			this.param1 = []
+			this.param2 = []
+			this.pkcontent = false
+			this.hoverRowIndex = -1
+			this.status = 2
+		},
+
+		openProductList(index, subCat = null) {
+			const row = this.shopList[index]
+			if (!row) return
+			row.subCategoryId = ''
+			row.subCategoryName = ''
+			this.activeRowIndex = index
+			this.currentCategoryId = row.categoryId
+			this.currentCategoryName = row.categoryName
+			this.currentSubCategoryId = subCat ? subCat.id : ''
+			this.currentSubCategoryName = subCat ? subCat.name : ''
+			this.title = ''
+			this.shopPageBrand = { id: '', name: '鍏ㄩ儴' }
+			this.showBrandPicker = false
+			this.leftShop = {}
+			this.rightShop = {}
+			this.pkcontent = false
+			this.hoverRowIndex = -1
+			this.status = 1
+			this.loadBrands(row.categoryId)
+			this.loadProducts()
+		},
+
+		onBrandHeadEnter() {
+			if (this.brandPickerTimer) {
+				clearTimeout(this.brandPickerTimer)
+				this.brandPickerTimer = null
+			}
+			this.showBrandPicker = true
+		},
+
+		onBrandHeadLeave() {
+			this.brandPickerTimer = setTimeout(() => {
+				this.showBrandPicker = false
+			}, 180)
+		},
+
+		onBrandHeadClick() {
+			const isTouch = typeof window !== 'undefined' &&
+				('ontouchstart' in window || (typeof navigator !== 'undefined' && navigator.maxTouchPoints > 0))
+			if (isTouch) {
+				this.showBrandPicker = !this.showBrandPicker
+			}
+		},
+
+		loadBrands(categoryId) {
+			const list = filterBrandsByCategory(this.allBrands, this.allGoods, categoryId)
+			this.brandData = [{ id: '', name: '鍏ㄩ儴' }, ...list]
+		},
+
+		loadProducts() {
+			const row = this.shopList[this.activeRowIndex]
+			if (!row) {
+				this.shopPageData = []
+				this.shopPageDataSou = []
+				return
+			}
+			const list = filterGoods(this.allGoods, {
+				categoryId: row.categoryId,
+				subCategoryId: this.currentSubCategoryId || '',
+				brandId: (this.shopPageBrand && this.shopPageBrand.id) || ''
+			})
+			this.shopPageData = list
+			this.filterProducts()
+		},
+
+		selectBrand(brand) {
+			this.shopPageBrand = { id: brand.id, name: brand.name }
+			this.showBrandPicker = false
+			if (this.brandPickerTimer) {
+				clearTimeout(this.brandPickerTimer)
+				this.brandPickerTimer = null
+			}
+			this.loadProducts()
+		},
+
+		onProductSearch() {
+			this.$nextTick(() => {
+				this.filterProducts()
+			})
+		},
+
+		filterProducts() {
+			const source = Array.isArray(this.shopPageData) ? this.shopPageData : []
+			const kw = (this.title || '').trim()
+			if (!kw) {
+				this.shopPageDataSou = source.slice()
+				return
+			}
+			this.shopPageDataSou = source.filter(item => matchKeyword(item, kw))
+		},
+
+		clickProduct(item) {
+			this.leftShop = JSON.parse(JSON.stringify(item))
+			this.rightShop = {}
+			this.param1 = []
+			this.param2 = []
+			this.pkcontent = false
+			this.currentCategoryId = item.categoryId || ''
+			this.currentCategoryName = item.categoryName || ''
+			this.status = 2
+		},
+
+		applyGoodsToRow(index, goods) {
+			const row = this.shopList[index]
+			if (!row || !goods) return
+			row.name = goods.name
+			row.goodsId = goods.id
+			row.zdPrice = goods.zdPrice
+			row.price = goods.price
+			row.modelVal = ''
+			row.modelSearchList = []
+			Object.assign(row, {
+				multifileList: goods.multifileList,
+				goodsParamList: goods.goodsParamList,
+				prefixUrl: goods.prefixUrl,
+				imgurl: goods.imgurl,
+				brandId: goods.brandId,
+				brandName: goods.brandName
+			})
+		},
+
+		confirmLeftToRow() {
+			if (this.activeRowIndex >= 0 && this.leftShop.name) {
+				this.applyGoodsToRow(this.activeRowIndex, this.leftShop)
+			}
+			this.status = 0
+			this.resetPkState()
+		},
+
+		confirmRightToRow() {
+			if (this.activeRowIndex >= 0 && this.rightShop.name) {
+				this.applyGoodsToRow(this.activeRowIndex, this.rightShop)
+			}
+			this.status = 0
+			this.resetPkState()
+		},
+
+		mergePkParams() {
+			const leftParamList = this.leftShop.goodsParamList || []
+			const rightParamList = this.rightShop.goodsParamList || []
+			const leftList1 = []
+			const leftList2 = []
+			const rightList1 = []
+			const rightList2 = []
+
+			leftParamList.forEach(item => {
+				const matched = rightParamList.find(r => r.name === item.name)
+				if (matched) {
+					leftList1.push(item)
+					rightList1.push(matched)
+				} else {
+					leftList2.push(item)
+				}
+			})
+			rightParamList.forEach(item => {
+				if (!rightList1.find(r => r.name === item.name)) {
+					rightList2.push(item)
+				}
+			})
+
+			this.param1 = leftList1
+			this.param2 = rightList1
+			this.$set(this.leftShop, 'param1', leftList1)
+			this.$set(this.leftShop, 'param2', leftList2)
+			this.$set(this.rightShop, 'param1', rightList1)
+			this.$set(this.rightShop, 'param2', rightList2)
+		},
+
+		tryAutoPkCompare() {
+			if (!this.leftShop.name || !this.rightShop.name) {
+				this.pkcontent = false
+				return
+			}
+			if (this.isSamePkGoods(this.leftShop, this.rightShop)) {
+				this.pkcontent = false
+				uni.showToast({ title: '宸﹀彸涓嶈兘閫夋嫨鍚屼竴鍟嗗搧', icon: 'none' })
+				return
+			}
+			this.mergePkParams()
+			this.pkcontent = true
+		},
+
+		openSearch(side) {
+			this.pkSearchSide = side
+			const cat = this.resolvePkSearchCategory()
+			if (cat.id) {
+				this.currentCategoryId = cat.id
+				this.currentCategoryName = cat.name
+			}
+			if (this.$refs.searchRef && typeof this.$refs.searchRef.open === 'function') {
+				this.$refs.searchRef.open(side)
+			}
+		},
+
+		isSamePkGoods(a, b) {
+			if (!a || !b || !a.name || !b.name) return false
+			if (a.id != null && b.id != null) return String(a.id) === String(b.id)
+			return a.name === b.name
+		},
+
+		resolvePkSearchCategory() {
+			if (this.activeRowIndex >= 0 && this.shopList[this.activeRowIndex]) {
+				const row = this.shopList[this.activeRowIndex]
+				return {
+					id: row.categoryId || '',
+					name: row.categoryName || ''
+				}
+			}
+			if (this.leftShop && this.leftShop.categoryId) {
+				return {
+					id: this.leftShop.categoryId,
+					name: this.leftShop.categoryName || this.currentCategoryName || ''
+				}
+			}
+			return {
+				id: this.currentCategoryId || '',
+				name: this.currentCategoryName || ''
+			}
+		},
+
+		result(val) {
+			const side = (this.$refs.searchRef && this.$refs.searchRef.typeName) || this.pkSearchSide
+			const otherShop = side === 'left' ? this.rightShop : this.leftShop
+			if (this.isSamePkGoods(val, otherShop)) {
+				uni.showToast({ title: '宸﹀彸涓嶈兘閫夋嫨鍚屼竴鍟嗗搧', icon: 'none' })
+				return
+			}
+			const goods = JSON.parse(JSON.stringify(val))
+			if (side === 'left') {
+				this.leftShop = goods
+			} else if (side === 'right') {
+				this.rightShop = goods
+			}
+			this.pkSearchSide = ''
+			this.tryAutoPkCompare()
+		},
+
+		pkImgUrl(shop) {
+			const urls = this.getPkImageUrls(shop)
+			return urls[0] || ''
+		},
+
+		getPkImageUrls(shop) {
+			if (!shop) return []
+			const prefix = shop.prefixUrl || ''
+			const list = shop.multifileList || []
+			if (list.length) {
+				return list.map(f => prefix + (f.fileurl || '')).filter(Boolean)
+			}
+			if (shop.imgurl) return [prefix + shop.imgurl]
+			return []
+		},
+
+		openPkImagePreview(side) {
+			const shop = side === 'left' ? this.leftShop : this.rightShop
+			if (!shop || !shop.name) return
+			this.pkPreviewImgList = this.getPkImageUrls(shop)
+			if (!this.pkPreviewImgList.length) return
+			this.$nextTick(() => {
+				if (this.$refs.pkBigImg) {
+					this.$refs.pkBigImg.open(0)
+				}
+			})
+		},
+
+		clearLeftShop() {
+			this.leftShop = {}
+			this.pkcontent = false
+		},
+
+		clearRightShop() {
+			this.rightShop = {}
+			this.param1 = []
+			this.param2 = []
+			this.pkcontent = false
+		},
+
+		resetPkState() {
+			this.leftShop = {}
+			this.rightShop = {}
+			this.param1 = []
+			this.param2 = []
+			this.pkcontent = false
+			this.activeRowIndex = -1
+		},
+
+		productImg(item) {
+			if (item.imgurl) return (item.prefixUrl || '') + item.imgurl
+			return ''
+		},
+
+		subIcon(sub, row) {
+			if (sub.imgurl) return (sub.prefixUrl || row.prefixUrl || '') + sub.imgurl
+			return row.categoryImgurl || ''
+		},
+
+		featuredTitle(feat) {
+			if (!feat) return ''
+			if (feat.goodsId && this.allGoods && this.allGoods.length) {
+				const goods = this.allGoods.find(g => String(g.id) === String(feat.goodsId))
+				if (goods && goods.name) return goods.name
+			}
+			return feat.title || ''
+		},
+
+		featuredImgUrl(feat) {
+			if (!feat) return ''
+			if (feat.goodsId && this.allGoods && this.allGoods.length) {
+				const goods = this.allGoods.find(g => String(g.id) === String(feat.goodsId))
+				if (goods && goods.imgurl) {
+					return (goods.prefixUrl || '') + goods.imgurl
+				}
+			}
+			if (feat.prefixUrl && feat.imgurl && !/^https?:\/\//i.test(feat.imgurl)) {
+				return feat.prefixUrl + feat.imgurl
+			}
+			if (feat.imgaddr) {
+				return this.resolveMediaUrl(feat.imgaddr, feat)
+			}
+			return this.resolveMediaUrl(feat.imgurl || feat.img, feat)
+		},
+
+		formatDateTime(date) {
+			const d = date instanceof Date ? date : new Date(date)
+			const pad = n => String(n).padStart(2, '0')
+			return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`
+		},
+
+		submitOrderHandler() {
+			const selectedRows = this.shopList.filter(row => {
+				return row.goodsId && String(row.name || '').trim()
+			})
+
+			if (!selectedRows.length) {
+				uni.showToast({ title: '璇疯嚦灏戦�夋嫨涓�涓晢鍝佸瀷鍙�', icon: 'none' })
+				return
+			}
+
+			const items = selectedRows.map(row => ({
+				categoryId: row.categoryId,
+				categoryName: row.categoryName,
+				subCategoryId: row.subCategoryId || null,
+				subCategoryName: row.subCategoryName || '',
+				goodsId: row.goodsId,
+				goodsName: row.name,
+				zdPrice: row.zdPrice,
+				price: row.price
+			}))
+
+			if (!this.orderNo) {
+				uni.showToast({ title: '璁㈠崟缂栧彿鏈敓鎴�', icon: 'none' })
+				return
+			}
+
+			submitOrder({
+				orderNo: this.orderNo,
+				userBudget: Number(this.userBudget) || 0,
+				sessionCreateTime: this.formatDateTime(this.sessionCreateTime),
+				items
+			}).then(() => {
+				uni.showToast({ title: '鎻愪氦鎴愬姛', icon: 'success' })
+				this.resetPage()
+			})
+		},
+
+		resetPage() {
+			this.userBudget = 0
+			this.sessionCreateTime = new Date()
+			this.status = 0
+			this.showCategoryPicker = false
+			this.hoverRowIndex = -1
+			this.resetPkState()
+			this.title = ''
+			this.shopPageData = []
+			this.shopPageDataSou = []
+			this.loadOrderNo()
+			this.initShopListFromCategories()
+		},
+
+		fanhuiPage() {
+			if (this.status === 2) {
+				this.status = this.shopPageData.length ? 1 : 0
+				this.resetPkState()
+			} else if (this.status === 1) {
+				this.status = 0
+				this.title = ''
+				this.showBrandPicker = false
+				this.shopPageData = []
+				this.shopPageDataSou = []
+				this.activeRowIndex = -1
+				this.currentSubCategoryName = ''
+			}
+		},
+
+		loginOut() {
+			logout({}).then(() => {
+				this.$store.commit('clean')
+				uni.redirectTo({ url: '/pages/login/login' })
+			})
+		}
+	}
+}
+</script>
+
+<style lang="scss" scoped>
+$brown: #4A3728;
+$brown-dark: #3D2E22;
+$cream: #E8DCC8;
+$row-bg: #FFFFFF;
+$panel-bg: #F7F0E6;
+$orange: #FF8C42;
+$search-icon-filter: brightness(0) saturate(100%) invert(23%) sepia(14%) saturate(1067%) hue-rotate(349deg) brightness(95%) contrast(91%);
+
+.page_loading {
+	position: fixed;
+	inset: 0;
+	z-index: 999;
+	display: flex;
+	flex-direction: column;
+	align-items: center;
+	justify-content: center;
+	background: $cream;
+
+	.loading_spinner {
+		width: 40px;
+		height: 40px;
+		border: 3px solid rgba(74, 55, 40, 0.15);
+		border-top-color: $brown;
+		border-radius: 50%;
+		animation: spin 0.8s linear infinite;
+		margin-bottom: 16px;
+	}
+
+	.loading_text {
+		font-size: 14px;
+		color: $brown;
+		opacity: 0.85;
+	}
+}
+
+@keyframes spin {
+	to { transform: rotate(360deg); }
+}
+
+.page {
+	width: 100vw;
+	min-height: 100vh;
+	position: relative;
+	display: flex;
+	justify-content: center;
+}
+
+.page_bg {
+	position: fixed;
+	top: 0;
+	left: 0;
+	width: 100vw;
+	height: 100vh;
+	background-size: cover;
+	background-repeat: no-repeat;
+	background-position: center;
+	z-index: 0;
+
+	&.default-bg {
+		background-color: $cream;
+	}
+}
+
+.page_content {
+	position: relative;
+	z-index: 1;
+	width: 100%;
+	padding: 0 0 24px;
+	box-sizing: border-box;
+}
+
+.banner_wrap {
+	position: relative;
+	margin-bottom: 0;
+
+	.banner {
+		position: relative;
+		width: 100%;
+		overflow: hidden;
+		background: transparent;
+		min-height: auto;
+
+		.banner_img {
+			width: 100%;
+			display: block;
+		}
+	}
+}
+
+.submit_row {
+	padding: 10px 14px 8px;
+	background: transparent;
+}
+
+.home_sticky_header {
+	position: sticky;
+	top: 0;
+	z-index: 30;
+	width: 100%;
+	box-sizing: border-box;
+}
+
+.submit_btn {
+	display: inline-flex;
+	align-items: center;
+	gap: 5px;
+	padding: 7px 16px;
+	border-radius: 18px;
+	font-size: 12px;
+	font-weight: 600;
+	cursor: pointer;
+	box-shadow: 0 2px 8px rgba(74, 55, 40, 0.1);
+	transition: transform 0.15s;
+
+	&:hover {
+		transform: scale(1.03);
+	}
+
+	.submit_icon_img {
+		width: 16px;
+		height: 16px;
+		flex-shrink: 0;
+		display: block;
+	}
+
+	&.submit_btn--icon {
+		display: inline-flex;
+		align-items: center;
+		gap: 5px;
+	}
+}
+
+.user_bar {
+	display: flex;
+	align-items: center;
+	justify-content: flex-end;
+	gap: 8px;
+	font-size: 12px;
+	color: $brown;
+
+	image {
+		width: 20px;
+		height: 20px;
+		cursor: pointer;
+		opacity: 0.75;
+	}
+
+	&.user_bar--banner {
+		position: absolute;
+		right: 12px;
+		top: 12px;
+		z-index: 4;
+	}
+}
+
+.order_bar {
+	display: flex;
+	justify-content: space-between;
+	align-items: stretch;
+	padding: 4px 4px 12px;
+	margin: 0;
+	font-size: 14px;
+	border-radius: 0;
+
+	.order_left {
+		display: flex;
+		align-items: center;
+		gap: 4px;
+		flex-wrap: nowrap;
+		flex: 1;
+		min-width: 0;
+	}
+
+	.order_right {
+		display: flex;
+		align-items: center;
+		justify-content: flex-end;
+		flex-shrink: 0;
+		margin-left: 8px;
+	}
+
+	.order_no_row {
+		display: flex;
+		align-items: center;
+		gap: 6px;
+	}
+
+	.remain_inline {
+		display: flex;
+		align-items: center;
+		gap: 6px;
+		margin-left: 10px;
+		font-size: 12px;
+		opacity: 0.85;
+		flex-shrink: 0;
+		white-space: nowrap;
+
+		.remain_val {
+			font-weight: 600;
+			color: #FFD88A;
+		}
+	}
+
+	.order_label {
+		opacity: 0.9;
+		font-size: 13px;
+	}
+
+	.order_currency {
+		font-weight: 700;
+		font-size: 15px;
+	}
+
+	.budget_input {
+		width: 12.5ch;
+		min-width: 12.5ch;
+		height: 28px;
+		background: transparent;
+		border: none;
+		padding: 0 2px;
+		color: inherit;
+		font-size: 16px;
+		font-weight: 700;
+		text-align: left;
+		flex-shrink: 0;
+	}
+
+	.order_no {
+		font-weight: 600;
+		font-size: 14px;
+		letter-spacing: 0.3px;
+	}
+}
+
+.home_top_section {
+	position: relative;
+	margin: 8px 12px 0;
+	padding: 12px 12px 0;
+	border-radius: 16px 16px 0 0;
+	overflow: visible;
+	z-index: 1;
+	--home-top-bg: #4A3728;
+
+	&::after {
+		content: '';
+		position: absolute;
+		top: -18px;
+		left: -24px;
+		right: -24px;
+		bottom: -18px;
+		border-radius: 24px 24px 0 0;
+		background: radial-gradient(
+			ellipse 88% 92% at 50% 48%,
+			var(--home-top-bg) 0%,
+		 rgba(74, 55, 40, 0.55) 55%,
+		 rgba(74, 55, 40, 0.15) 78%,
+		 transparent 100%
+		);
+		filter: blur(10px);
+		z-index: -1;
+		pointer-events: none;
+	}
+
+	.order_bar,
+	.featured,
+	.table_head_wrap {
+		position: relative;
+		z-index: 1;
+	}
+
+	.order_bar,
+	.shop_table_head {
+		color: inherit;
+		background: transparent;
+	}
+
+	.product_list_head_wrap {
+		margin-top: 0;
+	}
+
+	&.home_top_section--compact {
+		padding-bottom: 12px;
+		border-radius: 16px;
+
+		&::after {
+			bottom: 0;
+			border-radius: 24px;
+		}
+
+		.featured {
+			margin-bottom: 0;
+		}
+	}
+}
+
+.featured {
+	display: flex;
+	align-items: center;
+	background: linear-gradient(180deg, #FFF9EC 0%, #FAF0E0 100%);
+	padding: 14px 14px 12px;
+	margin: 0 0 10px;
+	border-radius: 20px;
+	box-shadow: 0 2px 10px rgba(0, 0, 0, 0.12);
+	border: none;
+
+	.featured_left {
+		flex-shrink: 0;
+		width: 218px;
+		min-width: 218px;
+		display: flex;
+		text-align:center;
+		flex-direction: column;
+		justify-content: center;
+		padding-right: 12px;
+		border-right: 1px solid rgba(74, 55, 40, 0.08);
+		margin-right: 10px;
+	}
+
+	.featured_title {
+		display: block;
+		font-size: 22px;
+		font-weight: 700;
+		color: #333;
+		font-family: Georgia, 'Times New Roman', serif;
+		letter-spacing: 1px;
+		line-height: 1.15;
+		margin-bottom: 8px;
+		white-space: nowrap;
+	}
+
+	.featured_sub {
+		display: block;
+		font-size: 10px;
+		color: #aaa;
+		line-height: 1.45;
+		white-space: nowrap;
+	}
+
+	.featured_products {
+		flex: 1;
+		display: flex;
+		align-items: stretch;
+		min-width: 0;
+	}
+
+	.featured_item {
+		flex: 1;
+		display: flex;
+		align-items: center;
+		gap: 8px;
+		padding: 2px 6px;
+		min-width: 0;
+
+		&:not(:last-child) {
+			border-right: 1px solid rgba(74, 55, 40, 0.08);
+			margin-right: 6px;
+			padding-right: 10px;
+		}
+	}
+
+	.featured_img_wrap {
+		flex-shrink: 0;
+		width: 44px;
+		height: 72px;
+		display: flex;
+		align-items: center;
+		justify-content: center;
+	}
+
+	.featured_img {
+		width: 44px;
+		height: 72px;
+	}
+
+	.featured_img_placeholder {
+		width: 36px;
+		height: 60px;
+		background: rgba(74, 55, 40, 0.06);
+		border-radius: 4px;
+	}
+
+	.featured_info {
+		flex: 1;
+		min-width: 0;
+		display: flex;
+		flex-direction: column;
+		justify-content: center;
+		gap: 6px;
+	}
+
+	.featured_name {
+		display: block;
+		font-size: 12px;
+		font-weight: 600;
+		color: #333;
+		line-height: 1.35;
+		overflow: hidden;
+		text-overflow: ellipsis;
+		white-space: nowrap;
+	}
+
+	.featured_tag_row {
+		display: flex;
+		align-items: center;
+		gap: 8px;
+		flex-wrap: wrap;
+	}
+
+	.tag {
+		font-size: 11px;
+		line-height: 1.2;
+
+		&.tag_dim {
+			color: #888;
+		}
+
+		&.tag_blue {
+			color: #4A8FD4;
+			font-weight: 500;
+		}
+
+		&.tag_green {
+			color: #3CB371;
+			font-weight: 500;
+		}
+	}
+}
+
+.table_head_wrap {
+	position: relative;
+	border-radius: 0;
+	overflow: visible;
+	z-index: 3;
+}
+
+.home_top_section .shop_table_head {
+	display: grid;
+	grid-template-columns: 1.1fr 2fr 0.9fr 0.9fr;
+	padding: 11px 14px 12px;
+	font-size: 13px;
+	font-weight: 600;
+	border-radius: 0 !important;
+	box-shadow: none;
+	margin: 0;
+	background: transparent;
+
+	.col {
+		display: flex;
+		align-items: center;
+	}
+
+	.col_cat {
+		justify-content: flex-start;
+		gap: 4px;
+		cursor: pointer;
+		padding-left: 2px;
+
+		.arrow {
+			font-size: 9px;
+			opacity: 0.85;
+		}
+	}
+
+	.category_dropdown_anchor {
+		position: relative;
+		z-index: 12;
+	}
+
+	.col_model {
+		justify-content: flex-start;
+		text-align: left;
+	}
+
+	.col_zd,
+	.col_price {
+		justify-content: flex-end;
+		text-align: right;
+	}
+}
+
+.home_top_section .category_picker {
+	position: absolute;
+	top: calc(100% + 2px);
+	left: -26px;
+	min-width: 168px;
+	z-index: 20;
+	max-height: 240px;
+	overflow-y: auto;
+	border-radius: 0 0 6px 6px;
+	padding: 6px 0;
+	box-shadow: 0 8px 20px rgba(0, 0, 0, 0.22);
+
+	.category_picker_item {
+		display: flex;
+		align-items: center;
+		gap: 10px;
+		padding: 10px 12px;
+		color: #fff;
+		cursor: pointer;
+		border-radius: 0;
+
+		&:hover {
+			background: rgba(255, 255, 255, 0.1);
+		}
+
+		image {
+			width: 28px;
+			height: 28px;
+		}
+	}
+
+	.category_picker_empty {
+		padding: 16px;
+		text-align: center;
+		color: rgba(255, 255, 255, 0.7);
+		font-size: 13px;
+	}
+}
+
+.home_top_section .product_list_head_wrap {
+	.product_shop_table_head {
+		.col {
+			min-width: 0;
+		}
+	}
+
+	.product_cat_col {
+		justify-content: flex-end;
+		width: 100%;
+		padding-right: 0;
+		text-align: right;
+
+		&.product_cat_col--span {
+			grid-column: 3 / -1;
+		}
+	}
+
+	.product_cat_inner {
+		display: inline-flex;
+		align-items: center;
+		justify-content: flex-end;
+		gap: 6px;
+		max-width: 100%;
+		margin-left: auto;
+		min-width: 0;
+	}
+
+	.product_cat_icon {
+		width: 22px;
+		height: 22px;
+		flex-shrink: 0;
+	}
+
+	.cat_line {
+		font-size: 13px;
+		font-weight: 600;
+		line-height: 1.2;
+		text-align: right;
+		overflow: hidden;
+		text-overflow: ellipsis;
+		white-space: nowrap;
+		min-width: 0;
+	}
+
+	.product_search_col {
+		justify-content: flex-start;
+		padding: 0 4px;
+	}
+
+	.product_search_inline {
+		flex: 1;
+		display: flex;
+		align-items: center;
+		gap: 6px;
+		border-radius: 16px;
+		padding: 5px 10px;
+		min-width: 0;
+
+		.search_icon {
+			width: 13px;
+			height: 13px;
+			flex-shrink: 0;
+			display: block;
+			opacity: 1;
+			filter: $search-icon-filter;
+		}
+
+		.search_divider {
+			width: 1px;
+			height: 14px;
+			background: rgba(74, 55, 40, 0.12);
+			flex-shrink: 0;
+		}
+
+		input {
+			flex: 1;
+			min-width: 0;
+			font-size: 12px;
+			color: $brown;
+			background: transparent;
+		}
+	}
+
+	.product_brand_col {
+		justify-content: flex-start;
+		gap: 4px;
+		cursor: pointer;
+		flex-wrap: wrap;
+
+		.brand_echo_inline {
+			font-size: 12px;
+			font-weight: 500;
+			opacity: 0.9;
+			margin-left: 4px;
+			max-width: 56px;
+			overflow: hidden;
+			text-overflow: ellipsis;
+			white-space: nowrap;
+		}
+
+		.arrow {
+			font-size: 9px;
+			opacity: 0.85;
+		}
+
+		.brand_picker {
+			left: -26px;
+			right: auto;
+		}
+	}
+
+	.product_brand_echo {
+		justify-content: flex-end;
+		font-size: 12px;
+		font-weight: 500;
+		opacity: 0.9;
+		overflow: hidden;
+		text-overflow: ellipsis;
+		white-space: nowrap;
+	}
+
+	.brand_picker {
+		position: absolute;
+		top: calc(100% + 4px);
+		right: 0;
+		min-width: 120px;
+		max-height: 220px;
+		overflow-y: auto;
+		border-radius: 8px;
+		padding: 6px 0;
+		box-shadow: 0 8px 20px rgba(0, 0, 0, 0.22);
+		z-index: 20;
+
+		.brand_picker_item {
+			padding: 10px 14px;
+			font-size: 13px;
+			color: #fff;
+			cursor: pointer;
+			white-space: nowrap;
+
+			&:hover,
+			&.active {
+				background: rgba(255, 255, 255, 0.12);
+			}
+		}
+	}
+}
+
+.shop_table {
+	position: relative;
+	margin: 10px 12px 10px;
+	border-radius: 0;
+	overflow: visible;
+	padding: 0;
+	background: transparent;
+	box-shadow: none;
+}
+
+.nav_back {
+	position: fixed;
+	left: 16px;
+	top: 50%;
+	transform: translateY(-50%);
+	width: 56px;
+	height: 56px;
+	min-width: 56px;
+	min-height: 56px;
+	display: flex;
+	align-items: center;
+	justify-content: center;
+	cursor: pointer;
+	z-index: 50;
+	box-sizing: border-box;
+	padding: 8px;
+
+	image {
+		width: 36px;
+		height: 36px;
+		display: block;
+	}
+}
+
+.shop_table_body {
+	position: relative;
+	min-height: 48px;
+	padding: 0 10px 10px;
+	overflow: visible;
+
+	.shop_table_empty {
+		padding: 28px 16px;
+		text-align: center;
+		color: #999;
+		font-size: 13px;
+		background: #fff;
+	}
+
+	.shop_row {
+		position: relative;
+		margin-bottom: 10px;
+		transition: transform 0.2s, box-shadow 0.2s;
+		overflow: visible;
+
+		&:last-child {
+			margin-bottom: 0;
+		}
+
+		&.expanded {
+			z-index: 5;
+		}
+	}
+
+	.shop_row_main {
+		display: flex;
+		gap: 10px;
+		align-items: stretch;
+		padding: 0;
+		font-size: 13px;
+		color: $brown;
+		background: transparent;
+		border: none;
+		min-height: 52px;
+
+		.row_cat_cell,
+		.row_info_group {
+			background: #fff;
+			border-radius: 14px;			
+			box-shadow: 0 2px 8px rgba(74, 55, 40, 0.08);
+			min-width: 0;
+		}
+
+		.row_cat_cell {
+			width: 108px;
+			flex-shrink: 0;
+			box-sizing: border-box;
+			display: flex;
+			flex-direction: row;
+			flex-wrap: nowrap;
+			align-items: center;
+			justify-content: flex-start;
+			gap: 6px;
+			padding: 10px 8px;
+			cursor: pointer;
+			overflow: hidden;
+
+			image {
+				width: 20px;
+				height: 20px;
+				flex-shrink: 0;
+			}
+
+			.cell_ellipsis {
+				flex: 1;
+				min-width: 0;
+				font-size: 12px;
+				font-weight: 600;
+				text-align: left;
+				line-height: 1.2;
+				white-space: nowrap;
+				overflow: hidden;
+				text-overflow: ellipsis;
+			}
+		}
+
+		.row_info_group {
+			flex: 1;
+			display: grid;
+			grid-template-columns: 2fr 0.9fr 0.9fr;
+			gap: 10px;
+			align-items: center;
+			padding: 10px;
+			overflow: hidden;
+
+			> .col {
+				min-width: 0;
+				overflow: hidden;
+			}
+		}
+
+		.cell_ellipsis {
+			display: block;
+			width: 100%;
+			overflow: hidden;
+			text-overflow: ellipsis;
+			white-space: nowrap;
+		}
+
+		.col_model {
+			justify-content: flex-start;
+
+			.cell_ellipsis {
+				text-align: left;
+				padding: 0 2px;
+			}
+
+			&.col_model_link {
+				cursor: pointer;
+			}
+		}
+
+		.col_zd {
+			justify-content: flex-end;
+
+			.cell_ellipsis {
+				text-align: right;
+			}
+		}
+
+		.col_price {
+			justify-content: flex-end;
+
+			input {
+				width: 100%;
+				max-width: 72px;
+				min-width: 0;
+				height: 32px;
+				text-align: center;
+				background: $panel-bg;
+				border-radius: 8px;
+				font-size: 13px;
+				color: $brown;
+				overflow: hidden;
+				text-overflow: ellipsis;
+				white-space: nowrap;
+			}
+		}
+	}
+
+	.shop_row_expand {
+		display: flex;
+		gap: 10px;
+		align-items: stretch;
+		padding: 0;
+		animation: expandIn 0.2s ease;
+		overflow: visible;
+	}
+
+	.expand_cat_card {
+		position: relative;
+		width: 88px;
+		flex-shrink: 0;
+		background: #fff;
+		border-radius: 14px;
+		padding: 16px 10px 12px;
+		display: flex;
+		flex-direction: column;
+		align-items: center;
+		justify-content: center;
+		box-shadow: 0 4px 16px rgba(74, 55, 40, 0.12);
+		cursor: pointer;
+	}
+
+	.expand_cat_img {
+		width: 48px;
+		height: 48px;
+		margin-bottom: 8px;
+	}
+
+	.expand_cat_name {
+		font-size: 13px;
+		font-weight: 600;
+		color: $brown;
+		text-align: center;
+		line-height: 1.3;
+	}
+
+	.expand_panel {
+		flex: 1;
+		background: #fff;
+		border-radius: 14px;
+		padding: 14px;
+		box-shadow: 0 4px 16px rgba(74, 55, 40, 0.12);
+		min-width: 0;
+		overflow: visible;
+	}
+
+	.expand_data_row {
+		display: grid;
+		grid-template-columns: 2fr 1fr 1fr;
+		gap: 8px;
+		align-items: center;
+		margin-bottom: 12px;
+		padding-bottom: 10px;
+		border-bottom: 1px solid rgba(74, 55, 40, 0.08);
+		font-size: 13px;
+		color: $brown;
+
+		.expand_model {
+			overflow: hidden;
+			text-overflow: ellipsis;
+			white-space: nowrap;
+			font-weight: 500;
+
+			&.expand_model_link {
+				cursor: pointer;
+			}
+		}
+
+		.expand_zd {
+			text-align: center;
+		}
+
+		.expand_price_input {
+			height: 30px;
+			text-align: center;
+			background: $panel-bg;
+			border-radius: 8px;
+			font-size: 13px;
+			color: $brown;
+		}
+	}
+
+	.sub_cat_grid {
+		display: flex;
+		flex-wrap: wrap;
+		gap: 12px;
+		margin-bottom: 14px;
+		justify-content: flex-start;
+	}
+
+	.sub_cat_icon_item {
+		display: flex;
+		flex-direction: column;
+		align-items: center;
+		width: 56px;
+		cursor: pointer;
+		transition: transform 0.15s;
+
+		&:hover {
+			transform: scale(1.06);
+		}
+
+		image {
+			width: 40px;
+			height: 40px;
+			margin-bottom: 4px;
+		}
+
+		text {
+			font-size: 11px;
+			color: $brown;
+			text-align: center;
+			line-height: 1.2;
+		}
+	}
+
+	.expand_search_wrap {
+		position: relative;
+		z-index: 2;
+	}
+
+	.expand_search {
+		display: flex;
+		align-items: center;
+		gap: 10px;
+		border-radius: 24px;
+		padding: 10px 16px;
+
+		.search_icon {
+			width: 14px;
+			height: 14px;
+			flex-shrink: 0;
+			display: block;
+			opacity: 1;
+			filter: $search-icon-filter;
+		}
+
+		input {
+			flex: 1;
+			font-size: 14px;
+			color: $brown;
+			background: transparent;
+		}
+	}
+
+	.model_suggest_empty {
+		position: absolute;
+		top: calc(100% + 4px);
+		left: 0;
+		right: 0;
+		margin-top: 0;
+		padding: 10px 14px;
+		font-size: 13px;
+		color: #999;
+		text-align: center;
+		background: #fff;
+		border-radius: 8px;
+		box-shadow: 0 8px 20px rgba(74, 55, 40, 0.16);
+		border: 1px solid rgba(74, 55, 40, 0.08);
+		z-index: 20;
+	}
+
+	.model_suggest_list {
+		position: absolute;
+		top: calc(100% + 4px);
+		left: 0;
+		right: 0;
+		margin-top: 0;
+		max-height: 160px;
+		overflow-y: auto;
+		border-radius: 8px;
+		background: #fff;
+		box-shadow: 0 8px 20px rgba(74, 55, 40, 0.16);
+		border: 1px solid rgba(74, 55, 40, 0.08);
+		z-index: 20;
+	}
+
+	.model_suggest_item {
+		padding: 10px 14px;
+		font-size: 13px;
+		color: $brown;
+		cursor: pointer;
+		border-bottom: 1px solid rgba(74, 55, 40, 0.06);
+
+		&:last-child {
+			border-bottom: none;
+		}
+
+		&:hover {
+			background: rgba(255, 255, 255, 0.6);
+		}
+	}
+
+	.col {
+		text-align: center;
+		display: flex;
+		align-items: center;
+		justify-content: center;
+	}
+
+	.col_model {
+		justify-content: flex-start;
+	}
+
+	.col_zd,
+	.col_price {
+		justify-content: flex-end;
+		text-align: right;
+	}
+
+	.col_zd {
+		font-weight: 500;
+	}
+
+	.row_delete {
+		position: absolute;
+		right: -6px;
+		top: -6px;
+		width: 22px;
+		height: 22px;
+		background: $orange;
+		color: #fff;
+		border-radius: 50%;
+		display: flex;
+		align-items: center;
+		justify-content: center;
+		cursor: pointer;
+		font-size: 16px;
+		line-height: 1;
+		z-index: 4;
+		box-shadow: 0 2px 6px rgba(255, 140, 66, 0.4);
+	}
+}
+
+@keyframes expandIn {
+	from {
+		opacity: 0.6;
+		transform: scale(0.98);
+	}
+	to {
+		opacity: 1;
+		transform: scale(1);
+	}
+}
+
+.product_page {
+	padding: 10px 0 20px;
+
+	.product_grid {
+		display: grid;
+		grid-template-columns: repeat(5, 1fr);
+		gap: 8px;
+		padding: 0 12px;
+	}
+
+	.product_card {
+		border-radius: 10px;
+		overflow: hidden;
+		cursor: pointer;
+		transition: transform 0.15s;
+		background: #fff;
+		box-shadow: 0 2px 8px rgba(74, 55, 40, 0.08);
+		display: flex;
+		flex-direction: column;
+
+		&:hover {
+			transform: translateY(-2px);
+		}
+
+		.product_card_img {
+			background: #fff;
+			padding: 8px 4px 4px;
+			display: flex;
+			align-items: center;
+			justify-content: center;
+
+			image {
+				width: 100%;
+				height: 72px;
+			}
+		}
+
+		.product_card_info {
+			background: #F5EFE6;
+			padding: 6px 4px 8px;
+			text-align: center;
+			flex: 1;
+			display: flex;
+			flex-direction: column;
+			justify-content: center;
+			gap: 2px;
+		}
+
+		.product_brand {
+			display: block;
+			font-size: 11px;
+			font-weight: 600;
+			color: $brown;
+			overflow: hidden;
+			text-overflow: ellipsis;
+			white-space: nowrap;
+		}
+
+		.product_model {
+			display: block;
+			font-size: 10px;
+			color: #666;
+			line-height: 1.25;
+			overflow: hidden;
+			text-overflow: ellipsis;
+			white-space: nowrap;
+		}
+
+		.product_price_label {
+			display: block;
+			font-size: 10px;
+			color: $brown;
+			margin-top: 2px;
+			overflow: hidden;
+			text-overflow: ellipsis;
+			white-space: nowrap;
+		}
+
+		&.placeholder {
+			grid-column: 1 / -1;
+			padding: 40px;
+			color: #999;
+			background: #fff;
+			align-items: center;
+			justify-content: center;
+		}
+	}
+}
+
+.pk_page {
+	padding-top: 32px;
+	padding-bottom: 24px;
+	margin: 0 12px;
+	padding-left: 12px;
+	padding-right: 12px;
+	box-sizing: border-box;
+	max-width: 100%;
+
+	.pk_cards,
+	.pk_params {
+		display: grid;
+		grid-template-columns: 1fr auto 1fr;
+		gap: 12px;
+		align-items: stretch;
+	}
+
+	.pk_cards {
+		margin-bottom: 20px;
+		align-items: start;
+	}
+
+	.pk_side {
+		min-width: 0;
+		position: relative;
+		display: flex;
+		flex-direction: column;
+		align-items: stretch;
+	}
+
+	.pk_delete {
+		position: absolute;
+		right: 4px;
+		top: 4px;
+		width: 26px;
+		height: 26px;
+		background: $orange;
+		color: #fff;
+		border-radius: 50%;
+		display: flex;
+		align-items: center;
+		justify-content: center;
+		cursor: pointer;
+		font-size: 20px;
+		z-index: 4;
+	}
+
+	.pk_showcase {
+		position: relative;
+		border-radius: 14px;
+		padding: 3px;
+		background: linear-gradient(
+			135deg,
+			var(--pk-show-accent, #{$orange}) 0%,
+			var(--pk-show-gold, #FFD88A) 42%,
+			var(--pk-show-primary, #{$brown}) 100%
+		);
+		box-shadow:
+			0 0 0 1px rgba(255, 255, 255, 0.38) inset,
+			0 8px 24px var(--pk-show-accent-soft, rgba(255, 140, 66, 0.28)),
+			0 4px 14px var(--pk-show-primary-soft, rgba(74, 55, 40, 0.18));
+		overflow: hidden;
+	}
+
+	.pk_showcase_glow {
+		position: absolute;
+		inset: 0;
+		border-radius: 14px;
+		pointer-events: none;
+		background:
+			radial-gradient(circle at 18% 12%, var(--pk-show-gold-soft, rgba(255, 216, 138, 0.45)) 0%, transparent 42%),
+			radial-gradient(circle at 82% 88%, var(--pk-show-accent-soft, rgba(255, 140, 66, 0.35)) 0%, transparent 46%);
+		animation: pk_showcase_glow 3.2s ease-in-out infinite;
+	}
+
+	.pk_showcase_frame {
+		position: relative;
+		z-index: 1;
+		border-radius: 11px;
+		padding: 14px 10px 10px;
+		text-align: center;
+		min-height: 168px;
+		display: flex;
+		align-items: center;
+		justify-content: center;
+		overflow: hidden;
+		background:
+			radial-gradient(circle at 50% 22%, var(--pk-show-accent-faint, rgba(255, 140, 66, 0.12)) 0%, transparent 58%),
+			linear-gradient(
+				165deg,
+				rgba(255, 255, 255, 0.98) 0%,
+				rgba(255, 251, 244, 0.94) 45%,
+				var(--pk-show-primary-soft, rgba(74, 55, 40, 0.1)) 100%
+			);
+		border: 1px solid rgba(255, 255, 255, 0.72);
+		box-shadow: inset 0 0 28px var(--pk-show-gold-soft, rgba(255, 216, 138, 0.22));
+	}
+
+	.pk_showcase_visual {
+		position: relative;
+		z-index: 2;
+		display: flex;
+		flex-direction: column;
+		align-items: center;
+		width: 100%;
+	}
+
+	.pk_showcase_img_card {
+		position: relative;
+		padding: 7px;
+		border-radius: 18px;
+		cursor: pointer;
+		background:
+			linear-gradient(180deg, #fff 0%, #fffaf3 100%) padding-box,
+			linear-gradient(
+				145deg,
+				var(--pk-show-gold, #FFD88A) 0%,
+				var(--pk-show-accent, #{$orange}) 48%,
+				var(--pk-show-primary, #{$brown}) 100%
+			) border-box;
+		border: 2px solid transparent;
+		box-shadow:
+			0 10px 24px var(--pk-show-primary-soft, rgba(74, 55, 40, 0.2)),
+			0 4px 10px var(--pk-show-accent-soft, rgba(255, 140, 66, 0.18)),
+			inset 0 1px 0 rgba(255, 255, 255, 0.95);
+		transition: transform 0.28s ease, box-shadow 0.28s ease;
+
+		&:hover {
+			transform: translateY(-4px) scale(1.03);
+			box-shadow:
+				0 16px 32px var(--pk-show-primary-soft, rgba(74, 55, 40, 0.26)),
+				0 8px 18px var(--pk-show-accent-soft, rgba(255, 140, 66, 0.28)),
+				inset 0 1px 0 rgba(255, 255, 255, 1);
+		}
+	}
+
+	.pk_showcase_img {
+		position: relative;
+		z-index: 1;
+		display: block;
+		width: 102px;
+		height: 102px;
+		border-radius: 14px;
+		background: #fff;
+		object-fit: contain;
+	}
+
+	.pk_showcase_reflect {
+		width: 102px;
+		height: 34px;
+		margin-top: 3px;
+		overflow: hidden;
+		border-radius: 0 0 14px 14px;
+		-webkit-mask-image: linear-gradient(to bottom, rgba(0, 0, 0, 0.42) 0%, transparent 88%);
+		mask-image: linear-gradient(to bottom, rgba(0, 0, 0, 0.42) 0%, transparent 88%);
+		pointer-events: none;
+	}
+
+	.pk_showcase_reflect_img {
+		display: block;
+		width: 102px;
+		height: 102px;
+		border-radius: 14px;
+		transform: scaleY(-1);
+		opacity: 0.55;
+		object-fit: contain;
+	}
+
+	.pk_showcase_ground {
+		width: 72%;
+		height: 10px;
+		margin-top: 2px;
+		border-radius: 50%;
+		background: radial-gradient(
+			ellipse at center,
+			var(--pk-show-primary-soft, rgba(74, 55, 40, 0.32)) 0%,
+			transparent 72%
+		);
+		pointer-events: none;
+	}
+
+	.pk_showcase_shine {
+		position: absolute;
+		top: -45%;
+		left: -25%;
+		width: 55%;
+		height: 90%;
+		pointer-events: none;
+		background: linear-gradient(120deg, transparent 0%, rgba(255, 255, 255, 0.62) 48%, transparent 100%);
+		transform: rotate(14deg);
+		animation: pk_showcase_shine 4.5s ease-in-out infinite;
+	}
+
+	.pk_podium {
+		width: 82%;
+		height: 22px;
+		margin: -2px auto 0;
+		background: linear-gradient(
+			180deg,
+			var(--pk-show-primary, #{$brown-dark}) 0%,
+			var(--pk-show-accent, #{$brown}) 52%,
+		 rgba(0, 0, 0, 0.22) 100%
+		);
+		clip-path: polygon(10% 0, 90% 0, 100% 100%, 0 100%);
+		box-shadow:
+			0 6px 16px var(--pk-show-primary-soft, rgba(74, 55, 40, 0.22)),
+			0 0 14px var(--pk-show-accent-soft, rgba(255, 140, 66, 0.28)),
+			inset 0 1px 0 var(--pk-show-gold-soft, rgba(255, 216, 138, 0.4)),
+			inset 0 -2px 6px rgba(0, 0, 0, 0.18);
+		position: relative;
+		z-index: 1;
+	}
+
+	@keyframes pk_showcase_glow {
+		0%, 100% { opacity: 0.62; }
+		50% { opacity: 1; }
+	}
+
+	@keyframes pk_showcase_shine {
+		0%, 100% { transform: rotate(14deg) translateX(0); opacity: 0.45; }
+		50% { transform: rotate(14deg) translateX(18%); opacity: 0.85; }
+	}
+
+	.pk_detail {
+		width: 100%;
+		margin-top: 0;
+	}
+
+	.pk_detail_head {
+		padding: 9px 10px;
+		border-radius: 10px 10px 0 0;
+		font-size: 12px;
+		font-weight: 600;
+		text-align: center;
+		overflow: hidden;
+		text-overflow: ellipsis;
+		white-space: nowrap;
+	}
+
+	.pk_detail_body {
+		display: grid;
+		grid-template-columns: 1fr 1fr;
+		gap: 6px;
+		padding: 12px 10px;
+		background: #fff;
+		border: 1px solid rgba(74, 55, 40, 0.1);
+		border-top: none;
+		border-radius: 0 0 10px 10px;
+		box-shadow: 0 4px 12px rgba(74, 55, 40, 0.08);
+	}
+
+	.pk_price_cell {
+		text-align: center;
+		min-width: 0;
+
+		.pk_price_label {
+			display: block;
+			font-size: 11px;
+			color: rgba(74, 55, 40, 0.62);
+			line-height: 1.3;
+		}
+
+		.pk_price_val {
+			display: block;
+			margin-top: 6px;
+			font-size: 17px;
+			font-weight: 700;
+			color: $brown;
+			line-height: 1.2;
+			word-break: break-all;
+		}
+
+		&--hot .pk_price_val {
+			color: #c0392b;
+		}
+	}
+
+	.pk_action_group {
+		display: flex;
+		align-items: stretch;
+		gap: 8px;
+		padding: 4px;
+		border-radius: 12px;
+		background: rgba(0, 0, 0, 0.16);
+		box-shadow:
+			inset 0 1px 0 rgba(255, 255, 255, 0.12),
+			0 1px 4px rgba(0, 0, 0, 0.12);
+	}
+
+	.pk_btn_ico {
+		flex-shrink: 0;
+		font-size: 14px;
+		line-height: 1;
+		opacity: 0.92;
+
+		&--check {
+			font-size: 12px;
+			font-weight: 700;
+		}
+	}
+
+	.pk_btn_txt {
+		line-height: 1.2;
+		white-space: nowrap;
+	}
+
+	.pk_change {
+		display: inline-flex;
+		align-items: center;
+		justify-content: center;
+		gap: 5px;
+		flex: 0 1 auto;
+		min-width: 0;
+		padding: 9px 11px;
+		border-radius: 9px;
+		font-size: 11px;
+		font-weight: 500;
+		text-align: center;
+		cursor: pointer;
+		color: rgba(255, 255, 255, 0.92);
+		background: rgba(255, 255, 255, 0.1);
+		border: 1px solid rgba(255, 255, 255, 0.24);
+		box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.14);
+		transition: background 0.2s ease, border-color 0.2s ease, transform 0.2s ease, box-shadow 0.2s ease;
+
+		.pk_btn_ico {
+			display: inline-block;
+			transform: rotate(-12deg);
+		}
+
+		&:hover {
+			background: rgba(255, 255, 255, 0.18);
+			border-color: rgba(255, 255, 255, 0.4);
+			transform: translateY(-1px);
+			box-shadow:
+				inset 0 1px 0 rgba(255, 255, 255, 0.22),
+				0 3px 8px rgba(0, 0, 0, 0.12);
+		}
+	}
+
+	.pk_confirm {
+		display: inline-flex;
+		align-items: center;
+		justify-content: center;
+		gap: 5px;
+		flex: 1;
+		min-width: 0;
+		padding: 9px 12px;
+		border-radius: 9px;
+		font-size: 12px;
+		font-weight: 700;
+		letter-spacing: 0.03em;
+		text-align: center;
+		cursor: pointer;
+		transition: transform 0.2s ease, filter 0.2s ease, box-shadow 0.2s ease;
+
+		&--inhead {
+			color: #fff;
+			background: linear-gradient(
+				135deg,
+				var(--pk-show-gold, #FFD88A) 0%,
+				var(--pk-show-accent, #{$orange}) 52%,
+				var(--pk-show-primary, #{$brown}) 100%
+			);
+			border: 1px solid rgba(255, 255, 255, 0.38);
+			box-shadow:
+				0 3px 12px var(--pk-show-accent-soft, rgba(255, 140, 66, 0.42)),
+				inset 0 1px 0 rgba(255, 255, 255, 0.48);
+			text-shadow: 0 1px 2px rgba(61, 46, 34, 0.28);
+
+			&:hover {
+				filter: brightness(1.07);
+				transform: translateY(-1px);
+				box-shadow:
+					0 5px 16px var(--pk-show-accent-soft, rgba(255, 140, 66, 0.52)),
+					inset 0 1px 0 rgba(255, 255, 255, 0.55);
+			}
+		}
+	}
+
+	.pk_empty {
+		min-height: 280px;
+		display: flex;
+		align-items: center;
+		justify-content: center;
+		color: $brown;
+		cursor: pointer;
+		border: 2px dashed rgba(74, 55, 40, 0.3);
+		border-radius: 12px;
+		background: rgba(255, 255, 255, 0.45);
+		font-size: 14px;
+	}
+
+	.pk_vs {
+		display: flex;
+		align-items: center;
+		justify-content: center;
+		font-size: 28px;
+		font-weight: 700;
+		flex-shrink: 0;
+		min-width: 36px;
+		padding: 0 2px;
+		align-self: start;
+		margin-top: 68px;
+
+		&.pk_vs--spacer {
+			visibility: hidden;
+			pointer-events: none;
+			margin-top: 0;
+		}
+	}
+
+	.pk_params {
+		min-width: 0;
+	}
+
+	.pk_param_col {
+		min-width: 0;
+	}
+
+	.pk_param_head {
+		padding: 10px;
+		border-radius: 10px 10px 0 0;
+		overflow: hidden;
+		box-shadow: 0 2px 10px rgba(74, 55, 40, 0.1);
+
+		&--solo {
+			border-radius: 10px;
+		}
+	}
+
+	.pk_param_pills {
+		background: #fff;
+		border-radius: 0 0 8px 8px;
+		padding: 10px;
+	}
+
+	.pill {
+		display: flex;
+		justify-content: space-between;
+		padding: 8px 10px;
+		margin-bottom: 6px;
+		background: $row-bg;
+		border-radius: 20px;
+		font-size: 12px;
+
+		.pill_label {
+			color: #666;
+		}
+
+		.pill_val {
+			color: $brown;
+			font-weight: 500;
+		}
+
+		&.pill_diff {
+			background: $cream;
+		}
+	}
+}
+</style>
diff --git a/h5/pages/login/login.vue b/h5/pages/login/login.vue
index 2eb63c9..5fa4396 100644
--- a/h5/pages/login/login.vue
+++ b/h5/pages/login/login.vue
@@ -26,7 +26,7 @@
 
 <script>
 	import { loginH5, getByLogin, getUserInfo } from '@/apis/index.js'
-	
+	import { getAnchorHomePath, normalizeAnchorPageVersion } from '@/utils/anchorHome.js'
 	export default {
 		data() {
 			return {
@@ -49,9 +49,11 @@
 					let user = await getUserInfo()
 					this.$store.commit('setUser', user.data)
 					let result = await getByLogin()
-					this.$store.commit('setConfig', result.data)
+					const config = result.data || result
+					this.$store.commit('setConfig', config)
 					uni.showToast({ title: '鐧诲綍鎴愬姛', icon: 'success', duration: 2000 });
-					uni.redirectTo({ url: '/pages/index_2/index' });
+					const homePath = getAnchorHomePath(normalizeAnchorPageVersion(config.anchorPageVersion))
+					uni.redirectTo({ url: homePath });
 				})
 			}
 		}
diff --git a/h5/static/ic_submit_order@2x.png b/h5/static/ic_submit_order@2x.png
new file mode 100644
index 0000000..c4a0adf
--- /dev/null
+++ b/h5/static/ic_submit_order@2x.png
Binary files differ
diff --git a/h5/static/sounds/click.mp3 b/h5/static/sounds/click.mp3
new file mode 100644
index 0000000..9b6561c
--- /dev/null
+++ b/h5/static/sounds/click.mp3
Binary files differ
diff --git a/h5/static/sounds/hover.mp3 b/h5/static/sounds/hover.mp3
new file mode 100644
index 0000000..c7d7797
--- /dev/null
+++ b/h5/static/sounds/hover.mp3
Binary files differ
diff --git a/h5/utils/anchorHome.js b/h5/utils/anchorHome.js
new file mode 100644
index 0000000..026badc
--- /dev/null
+++ b/h5/utils/anchorHome.js
@@ -0,0 +1,16 @@
+export const ANCHOR_PAGE_V2 = 'v2'
+export const ANCHOR_PAGE_V3 = 'v3'
+
+export function normalizeAnchorPageVersion (version) {
+  return String(version || '').toLowerCase() === ANCHOR_PAGE_V3 ? ANCHOR_PAGE_V3 : ANCHOR_PAGE_V2
+}
+
+export function getAnchorHomePath (version) {
+  return normalizeAnchorPageVersion(version) === ANCHOR_PAGE_V3
+    ? '/pages/index_3/index'
+    : '/pages/index_2/index'
+}
+
+export function isAnchorV3 (version) {
+  return normalizeAnchorPageVersion(version) === ANCHOR_PAGE_V3
+}
diff --git a/h5/utils/goodsFilter.js b/h5/utils/goodsFilter.js
new file mode 100644
index 0000000..06b59ea
--- /dev/null
+++ b/h5/utils/goodsFilter.js
@@ -0,0 +1,43 @@
+/**
+ * 鏈湴鍟嗗搧/鍝佺墝杩囨护锛堝熀浜庡叏閲忕紦瀛橈級
+ */
+
+export function matchKeyword(item, keyword) {
+	if (!keyword) return true
+	const kw = String(keyword).trim()
+	if (!kw) return true
+	return (item.name && item.name.indexOf(kw) !== -1) ||
+		(item.pinyin && item.pinyin.indexOf(kw) !== -1) ||
+		(item.shortPinyin && item.shortPinyin.indexOf(kw) !== -1)
+}
+
+export function filterGoods(allGoods, options = {}) {
+	const { categoryId, subCategoryId, brandId, keyword } = options
+	const list = Array.isArray(allGoods) ? allGoods : []
+	return list.filter(item => {
+		if (categoryId != null && categoryId !== '' && String(item.categoryId) !== String(categoryId)) {
+			return false
+		}
+		if (subCategoryId != null && subCategoryId !== '' && String(item.subCategoryId) !== String(subCategoryId)) {
+			return false
+		}
+		if (brandId != null && brandId !== '' && String(item.brandId) !== String(brandId)) {
+			return false
+		}
+		return matchKeyword(item, keyword)
+	})
+}
+
+export function filterBrandsByCategory(allBrands, allGoods, categoryId) {
+	if (!categoryId) {
+		return Array.isArray(allBrands) ? allBrands : []
+	}
+	const goods = Array.isArray(allGoods) ? allGoods : []
+	const brands = Array.isArray(allBrands) ? allBrands : []
+	const brandIds = new Set(
+		goods
+			.filter(g => String(g.categoryId) === String(categoryId) && g.brandId != null && g.brandId !== '')
+			.map(g => String(g.brandId))
+	)
+	return brands.filter(b => brandIds.has(String(b.id)))
+}
diff --git a/h5/utils/request.js b/h5/utils/request.js
index e5f375a..cfe5b37 100644
--- a/h5/utils/request.js
+++ b/h5/utils/request.js
@@ -5,7 +5,7 @@
  * @param {string} method 璇锋眰鏂规硶
  */ 
 const request = (url, data, method) => {
-	if (url !== 'business/goods/listForH5' && url !== 'business/brand/list') {
+	if (url !== 'business/goods/listForH5' && url !== 'business/brand/list' && url !== 'business/h5/initData') {
 		uni.showLoading({ title: '璇锋眰涓�', mask: true });
 	}
 	return new Promise((resole, reject) => {
@@ -43,7 +43,7 @@
 				reject()
 			},
 			complete() {
-				if (url !== 'business/goods/listForH5' && url !== 'business/brand/list') {
+				if (url !== 'business/goods/listForH5' && url !== 'business/brand/list' && url !== 'business/h5/initData') {
 					uni.hideLoading();
 				}
 			}
diff --git a/h5/utils/sound.js b/h5/utils/sound.js
new file mode 100644
index 0000000..66dc44f
--- /dev/null
+++ b/h5/utils/sound.js
@@ -0,0 +1,87 @@
+let hoverAudio = null
+let clickAudio = null
+let unlocked = false
+let unlockListenerAdded = false
+
+function getHoverAudio() {
+  if (!hoverAudio) {
+    hoverAudio = uni.createInnerAudioContext()
+    hoverAudio.src = '/static/sounds/hover.mp3'
+  }
+  return hoverAudio
+}
+
+function getClickAudio() {
+  if (!clickAudio) {
+    clickAudio = uni.createInnerAudioContext()
+    clickAudio.src = '/static/sounds/click.mp3'
+  }
+  return clickAudio
+}
+
+/** 瀹夊叏鎾斁锛屽悶鎺� autoplay 绛栫暐瀵艰嚧鐨� Promise  rejection */
+function safePlay(audio) {
+  try {
+    audio.stop()
+    const ret = audio.play()
+    if (ret && typeof ret.then === 'function') {
+      ret.catch(() => {})
+    }
+  } catch (e) {}
+}
+
+/** 棣栨鐢ㄦ埛鐐瑰嚮/瑙︽懜鍚庤В閿侀煶棰戯紙hover 涓嶇畻鐢ㄦ埛鎵嬪娍锛� */
+function primeAudio() {
+  ;[getHoverAudio(), getClickAudio()].forEach(audio => {
+    try {
+      audio.volume = 0
+      const ret = audio.play()
+      const finish = () => {
+        audio.stop()
+        audio.volume = 1
+      }
+      if (ret && typeof ret.then === 'function') {
+        ret.then(finish).catch(() => { audio.volume = 1 })
+      } else {
+        setTimeout(finish, 30)
+      }
+    } catch (e) {
+      audio.volume = 1
+    }
+  })
+}
+
+export function setupSoundUnlock() {
+  if (unlockListenerAdded || typeof document === 'undefined') return
+  unlockListenerAdded = true
+  const onUnlock = () => {
+    if (unlocked) return
+    unlocked = true
+    primeAudio()
+    document.removeEventListener('click', onUnlock, true)
+    document.removeEventListener('touchstart', onUnlock, true)
+    document.removeEventListener('keydown', onUnlock, true)
+  }
+  document.addEventListener('click', onUnlock, true)
+  document.addEventListener('touchstart', onUnlock, true)
+  document.addEventListener('keydown', onUnlock, true)
+}
+
+function ensureUnlocked() {
+  if (!unlocked) {
+    unlocked = true
+    primeAudio()
+  }
+}
+
+export function playHoverSound() {
+  if (!unlocked) return
+  safePlay(getHoverAudio())
+}
+
+export function playClickSound() {
+  ensureUnlocked()
+  safePlay(getClickAudio())
+}
+
+setupSoundUnlock()
diff --git a/server/company/src/main/java/com/doumee/api/business/CategoryController.java b/server/company/src/main/java/com/doumee/api/business/CategoryController.java
index 9b7ea1c..ea61da3 100644
--- a/server/company/src/main/java/com/doumee/api/business/CategoryController.java
+++ b/server/company/src/main/java/com/doumee/api/business/CategoryController.java
@@ -187,4 +187,41 @@
         return ApiResponse.success(categoryService.findListSaaS(category));
     }
 
+    @ApiOperation("鏍戝舰鍒楄〃")
+    @PostMapping("/tree")
+    @RequiresPermissions("business:category:query")
+    public ApiResponse<List<Category>> findTree(@RequestBody Category category) {
+        return ApiResponse.success(categoryService.findTree(category));
+    }
+
+    @ApiOperation("瀛愮被鍒垪琛�")
+    @GetMapping("/children/{parentId}")
+    public ApiResponse<List<Category>> findChildren(@PathVariable Integer parentId) {
+        return ApiResponse.success(categoryService.findChildren(parentId));
+    }
+
+    @PreventRepeat
+    @ApiOperation("鏂板缓瀛愮被鍒�")
+    @PostMapping("/createSub")
+    @RequiresPermissions("business:category:create")
+    public ApiResponse createSub(@RequestBody Category category) {
+        return ApiResponse.success(categoryService.createSubCategory(category));
+    }
+
+    @ApiOperation("鏇存柊瀛愮被鍒�")
+    @PostMapping("/updateSub")
+    @RequiresPermissions("business:category:update")
+    public ApiResponse updateSub(@RequestBody Category category) {
+        categoryService.updateSubCategory(category);
+        return ApiResponse.success(null);
+    }
+
+    @ApiOperation("鍒犻櫎瀛愮被鍒�")
+    @GetMapping("/deleteSub/{id}")
+    @RequiresPermissions("business:category:delete")
+    public ApiResponse deleteSub(@PathVariable Integer id) {
+        categoryService.deleteSubCategory(id);
+        return ApiResponse.success(null);
+    }
+
 }
diff --git a/server/company/src/main/java/com/doumee/api/business/PreselectOrderController.java b/server/company/src/main/java/com/doumee/api/business/PreselectOrderController.java
new file mode 100644
index 0000000..b14b752
--- /dev/null
+++ b/server/company/src/main/java/com/doumee/api/business/PreselectOrderController.java
@@ -0,0 +1,53 @@
+package com.doumee.api.business;
+
+import com.doumee.api.BaseController;
+import com.doumee.core.model.ApiResponse;
+import com.doumee.core.model.PageData;
+import com.doumee.core.model.PageWrap;
+import com.doumee.dao.business.model.PreselectOrder;
+import com.doumee.service.business.PreselectOrderService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletResponse;
+
+@Api(tags = "棰勯�夎鍗�")
+@RestController
+@RequestMapping("/business/order")
+public class PreselectOrderController extends BaseController {
+
+    @Autowired
+    private PreselectOrderService preselectOrderService;
+
+    @ApiOperation("鍒嗛〉鏌ヨ")
+    @PostMapping("/page")
+    @RequiresPermissions("business:order:query")
+    public ApiResponse<PageData<PreselectOrder>> findPage(@RequestBody PageWrap<PreselectOrder> pageWrap) {
+        return ApiResponse.success(preselectOrderService.findPage(pageWrap));
+    }
+
+    @ApiOperation("璁㈠崟璇︽儏")
+    @GetMapping("/{id}")
+    @RequiresPermissions("business:order:query")
+    public ApiResponse<PreselectOrder> findById(@PathVariable Integer id) {
+        return ApiResponse.success(preselectOrderService.findDetail(id));
+    }
+
+    @ApiOperation("鍒犻櫎璁㈠崟")
+    @GetMapping("/delete/{id}")
+    @RequiresPermissions("business:order:delete")
+    public ApiResponse deleteById(@PathVariable Integer id) {
+        preselectOrderService.deleteById(id);
+        return ApiResponse.success(null);
+    }
+
+    @ApiOperation("瀵煎嚭璁㈠崟鏄庣粏")
+    @GetMapping("/exportDetail/{id}")
+    @RequiresPermissions("business:order:exportExcel")
+    public void exportDetail(@PathVariable Integer id, HttpServletResponse response) {
+        preselectOrderService.exportDetail(id, response);
+    }
+}
diff --git a/server/company/src/main/java/com/doumee/api/business/WebParamController.java b/server/company/src/main/java/com/doumee/api/business/WebParamController.java
index c3c4b46..75f7bb0 100644
--- a/server/company/src/main/java/com/doumee/api/business/WebParamController.java
+++ b/server/company/src/main/java/com/doumee/api/business/WebParamController.java
@@ -58,4 +58,24 @@
         return ApiResponse.success("鎿嶄綔鎴愬姛");
     }
 
+    @ApiOperation("鑾峰彇涓绘挱绔〉闈㈤厤缃�")
+    @GetMapping("/getByLoginAnchor")
+    public ApiResponse<WebParam> getByLoginAnchor() {
+        return ApiResponse.success(webParamService.findOneAnchor());
+    }
+
+    @ApiOperation("鏇存柊涓绘挱绔〉闈㈤厤缃�")
+    @PostMapping("/renewAnchorUpdate")
+    public ApiResponse renewAnchorUpdate(@RequestBody UpdateWebParamDTO updateWebParamDTO) {
+        webParamService.renewAnchorUpdate(updateWebParamDTO);
+        return ApiResponse.success("鎿嶄綔鎴愬姛");
+    }
+
+    @ApiOperation("鏇存柊涓绘挱绔敓鏁堥〉闈㈢増鏈�")
+    @PostMapping("/updateAnchorPageVersion")
+    public ApiResponse updateAnchorPageVersion(@RequestBody UpdateWebParamDTO updateWebParamDTO) {
+        webParamService.updateAnchorPageVersion(updateWebParamDTO.getAnchorPageVersion());
+        return ApiResponse.success("鎿嶄綔鎴愬姛");
+    }
+
 }
diff --git a/server/db/business.anchor_page_version.sql b/server/db/business.anchor_page_version.sql
new file mode 100644
index 0000000..c1e907d
--- /dev/null
+++ b/server/db/business.anchor_page_version.sql
@@ -0,0 +1,3 @@
+-- 涓绘挱绔〉闈㈢増鏈細v2=index_2锛寁3=index_3锛屽巻鍙叉暟鎹粯璁� v2
+ALTER TABLE `web_param`
+  ADD COLUMN `anchor_page_version` VARCHAR(8) NOT NULL DEFAULT 'v2' COMMENT '涓绘挱绔敓鏁堢増鏈� v2|v3' AFTER `anchor_param`;
diff --git a/server/db/business.menu.anchor_v3.sql b/server/db/business.menu.anchor_v3.sql
new file mode 100644
index 0000000..f354d33
--- /dev/null
+++ b/server/db/business.menu.anchor_v3.sql
@@ -0,0 +1,3 @@
+-- 鑿滃崟闇�鍦ㄧ郴缁熻彍鍗曠鐞嗕腑閰嶇疆锛屼互涓嬩负鍙傝�僑QL锛坧arent_id 璇锋寜瀹為檯涓氬姟鑿滃崟鐖惰妭鐐硅皟鏁达級
+-- INSERT INTO `SYSTEM_MENU`(`NAME`, `PATH`, `PARENT_ID`, `SORT`, `ICON`, `TYPE`, `CREATE_USER`, `CREATE_TIME`, `DELETED`) VALUES ('椤甸潰閰嶇疆鏂扮増', '/business/pageConfigurationNew', 0, 10, 'el-icon-picture-outline', 1, 1, CURRENT_TIMESTAMP, 0);
+-- INSERT INTO `SYSTEM_MENU`(`NAME`, `PATH`, `PARENT_ID`, `SORT`, `ICON`, `TYPE`, `CREATE_USER`, `CREATE_TIME`, `DELETED`) VALUES ('璁㈠崟绠$悊', '/business/order', 0, 11, 'el-icon-s-order', 1, 1, CURRENT_TIMESTAMP, 0);
diff --git a/server/db/business.order.fix_create_time.sql b/server/db/business.order.fix_create_time.sql
new file mode 100644
index 0000000..a5bbab4
--- /dev/null
+++ b/server/db/business.order.fix_create_time.sql
@@ -0,0 +1,6 @@
+-- 淇鍘嗗彶璁㈠崟锛氬垱寤烘椂闂村簲绛変簬椤甸潰浼氳瘽鍒涘缓鏃堕棿锛岃�岄潪鎻愪氦鏃堕棿
+UPDATE `preselect_order`
+SET `create_time` = `session_create_time`
+WHERE `session_create_time` IS NOT NULL
+  AND `isdeleted` = 0
+  AND (`create_time` IS NULL OR `create_time` = `submit_time`);
diff --git a/server/db/business.order.permissions.sql b/server/db/business.order.permissions.sql
new file mode 100644
index 0000000..87f13e4
--- /dev/null
+++ b/server/db/business.order.permissions.sql
@@ -0,0 +1,5 @@
+INSERT INTO `SYSTEM_PERMISSION`(`CODE`, `NAME`, `REMARK`, `FIXED`, `CREATE_USER`, `CREATE_TIME`, `UPDATE_USER`, `UPDATE_TIME`, `DELETED`) VALUES ('business:order:query', '鏌ヨ棰勯�夎鍗�', '', 0, 1, CURRENT_TIMESTAMP, NULL, NULL, 0);
+INSERT INTO `SYSTEM_PERMISSION`(`CODE`, `NAME`, `REMARK`, `FIXED`, `CREATE_USER`, `CREATE_TIME`, `UPDATE_USER`, `UPDATE_TIME`, `DELETED`) VALUES ('business:order:delete', '鍒犻櫎棰勯�夎鍗�', '', 0, 1, CURRENT_TIMESTAMP, NULL, NULL, 0);
+INSERT INTO `SYSTEM_PERMISSION`(`CODE`, `NAME`, `REMARK`, `FIXED`, `CREATE_USER`, `CREATE_TIME`, `UPDATE_USER`, `UPDATE_TIME`, `DELETED`) VALUES ('business:order:exportExcel', '瀵煎嚭棰勯�夎鍗曟槑缁�', '', 0, 1, CURRENT_TIMESTAMP, NULL, NULL, 0);
+INSERT INTO `SYSTEM_PERMISSION`(`CODE`, `NAME`, `REMARK`, `FIXED`, `CREATE_USER`, `CREATE_TIME`, `UPDATE_USER`, `UPDATE_TIME`, `DELETED`) VALUES ('business:webparam:anchorQuery', '鏌ヨ涓绘挱绔〉闈㈤厤缃�', '', 0, 1, CURRENT_TIMESTAMP, NULL, NULL, 0);
+INSERT INTO `SYSTEM_PERMISSION`(`CODE`, `NAME`, `REMARK`, `FIXED`, `CREATE_USER`, `CREATE_TIME`, `UPDATE_USER`, `UPDATE_TIME`, `DELETED`) VALUES ('business:webparam:anchorUpdate', '鏇存柊涓绘挱绔〉闈㈤厤缃�', '', 0, 1, CURRENT_TIMESTAMP, NULL, NULL, 0);
diff --git a/server/db/upgrade_anchor_v3.sql b/server/db/upgrade_anchor_v3.sql
new file mode 100644
index 0000000..94dda13
--- /dev/null
+++ b/server/db/upgrade_anchor_v3.sql
@@ -0,0 +1,65 @@
+-- 涓绘挱绔笌浼佷笟绔崌绾� v3 鏁版嵁搴撹縼绉�
+
+-- 1. 鍝佺被浜岀骇绫诲埆
+ALTER TABLE `category` ADD COLUMN `parent_id` INT NULL DEFAULT NULL COMMENT '鐖剁骇鍝佺被ID锛孨ULL涓轰竴绾у搧绫�' AFTER `company_id`;
+CREATE INDEX `idx_category_parent_id` ON `category` (`parent_id`);
+
+-- 2. 鍟嗗搧浜岀骇绫诲埆
+ALTER TABLE `goods` ADD COLUMN `sub_category_id` INT NULL DEFAULT NULL COMMENT '浜岀骇鍝佺被ID锛屽彲涓虹┖' AFTER `category_id`;
+CREATE INDEX `idx_goods_sub_category_id` ON `goods` (`sub_category_id`);
+
+-- 3. 涓绘挱绔〉闈㈤厤缃�
+ALTER TABLE `web_param` ADD COLUMN `anchor_param` TEXT NULL COMMENT '涓绘挱绔痠ndex_3椤甸潰閰嶇疆JSON' AFTER `new_param`;
+
+-- 4. 棰勯�夎鍗曚富琛�
+CREATE TABLE IF NOT EXISTS `preselect_order` (
+  `id` INT NOT NULL AUTO_INCREMENT COMMENT '涓婚敭',
+  `order_no` VARCHAR(20) NOT NULL COMMENT '璁㈠崟缂栧彿 yyyyMMddHHmm+3浣嶅簭鍒�',
+  `company_id` INT NOT NULL COMMENT '浼佷笟缂栫爜',
+  `anchor_user_id` INT NOT NULL COMMENT '涓绘挱鐢ㄦ埛ID',
+  `anchor_username` VARCHAR(100) NULL COMMENT '涓绘挱璐﹀彿',
+  `user_budget` DECIMAL(12,2) NULL DEFAULT 0 COMMENT '瀹㈡埛棰勭畻',
+  `category_count` INT NULL DEFAULT 0 COMMENT '鍝佺被鏁伴噺',
+  `goods_count` INT NULL DEFAULT 0 COMMENT '鍟嗗搧鏁伴噺',
+  `total_zd_price` DECIMAL(12,2) NULL DEFAULT 0 COMMENT '鏃楄埌浠峰悎璁�',
+  `total_price` DECIMAL(12,2) NULL DEFAULT 0 COMMENT '鎸囧浠峰悎璁�',
+  `session_create_time` DATETIME NULL COMMENT '椤甸潰浼氳瘽鍒涘缓鏃堕棿',
+  `create_time` DATETIME NULL COMMENT '鍒涘缓鏃堕棿',
+  `submit_time` DATETIME NULL COMMENT '鎻愪氦鏃堕棿',
+  `duration_seconds` INT NULL DEFAULT 0 COMMENT '鍒涘缓鏃堕暱(绉�)',
+  `creator` INT NULL COMMENT '鍒涘缓浜�',
+  `editor` INT NULL COMMENT '鏇存柊浜�',
+  `edit_date` DATETIME NULL COMMENT '鏇存柊鏃堕棿',
+  `isdeleted` INT NOT NULL DEFAULT 0 COMMENT '鏄惁鍒犻櫎0鍚�1鏄�',
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `uk_order_no` (`order_no`),
+  KEY `idx_company_id` (`company_id`),
+  KEY `idx_submit_time` (`submit_time`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='棰勯�夎鍗曚富琛�';
+
+-- 5. 棰勯�夎鍗曟槑缁嗚〃
+CREATE TABLE IF NOT EXISTS `preselect_order_item` (
+  `id` INT NOT NULL AUTO_INCREMENT COMMENT '涓婚敭',
+  `order_id` INT NOT NULL COMMENT '璁㈠崟ID',
+  `sort_num` INT NOT NULL DEFAULT 0 COMMENT '搴忓彿',
+  `category_id` INT NULL COMMENT '涓�绾у搧绫籌D',
+  `category_name` VARCHAR(100) NULL COMMENT '鍝佺被鍚嶇О',
+  `sub_category_id` INT NULL COMMENT '浜岀骇鍝佺被ID',
+  `sub_category_name` VARCHAR(100) NULL COMMENT '浜岀骇鍝佺被鍚嶇О',
+  `goods_id` INT NULL COMMENT '鍟嗗搧ID',
+  `goods_name` VARCHAR(200) NULL COMMENT '浜у搧鍨嬪彿',
+  `zd_price` DECIMAL(12,2) NULL COMMENT '鏃楄埌浠�',
+  `price` DECIMAL(12,2) NULL COMMENT '鎸囧浠�',
+  `isdeleted` INT NOT NULL DEFAULT 0 COMMENT '鏄惁鍒犻櫎0鍚�1鏄�',
+  PRIMARY KEY (`id`),
+  KEY `idx_order_id` (`order_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='棰勯�夎鍗曟槑缁嗚〃';
+
+-- 6. 璁㈠崟鍙峰簭鍒楄〃锛堟寜鍒嗛挓妗惰嚜澧烇級
+CREATE TABLE IF NOT EXISTS `preselect_order_seq` (
+  `id` INT NOT NULL AUTO_INCREMENT,
+  `seq_key` VARCHAR(20) NOT NULL COMMENT 'yyyyMMddHHmm',
+  `seq_val` INT NOT NULL DEFAULT 0 COMMENT '褰撳墠搴忓垪鍊�',
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `uk_seq_key` (`seq_key`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='璁㈠崟鍙峰簭鍒楄〃';
diff --git a/server/service/src/main/java/com/doumee/dao/business/PreselectOrderItemMapper.java b/server/service/src/main/java/com/doumee/dao/business/PreselectOrderItemMapper.java
new file mode 100644
index 0000000..c61e49c
--- /dev/null
+++ b/server/service/src/main/java/com/doumee/dao/business/PreselectOrderItemMapper.java
@@ -0,0 +1,9 @@
+package com.doumee.dao.business;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.doumee.dao.business.model.PreselectOrderItem;
+import org.apache.ibatis.annotations.Mapper;
+
+@Mapper
+public interface PreselectOrderItemMapper extends BaseMapper<PreselectOrderItem> {
+}
diff --git a/server/service/src/main/java/com/doumee/dao/business/PreselectOrderMapper.java b/server/service/src/main/java/com/doumee/dao/business/PreselectOrderMapper.java
new file mode 100644
index 0000000..d755f0a
--- /dev/null
+++ b/server/service/src/main/java/com/doumee/dao/business/PreselectOrderMapper.java
@@ -0,0 +1,9 @@
+package com.doumee.dao.business;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.doumee.dao.business.model.PreselectOrder;
+import org.apache.ibatis.annotations.Mapper;
+
+@Mapper
+public interface PreselectOrderMapper extends BaseMapper<PreselectOrder> {
+}
diff --git a/server/service/src/main/java/com/doumee/dao/business/PreselectOrderSeqMapper.java b/server/service/src/main/java/com/doumee/dao/business/PreselectOrderSeqMapper.java
new file mode 100644
index 0000000..75d40f6
--- /dev/null
+++ b/server/service/src/main/java/com/doumee/dao/business/PreselectOrderSeqMapper.java
@@ -0,0 +1,9 @@
+package com.doumee.dao.business;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.doumee.dao.business.model.PreselectOrderSeq;
+import org.apache.ibatis.annotations.Mapper;
+
+@Mapper
+public interface PreselectOrderSeqMapper extends BaseMapper<PreselectOrderSeq> {
+}
diff --git a/server/service/src/main/java/com/doumee/dao/business/model/Category.java b/server/service/src/main/java/com/doumee/dao/business/model/Category.java
index 3768bad..2f0398c 100644
--- a/server/service/src/main/java/com/doumee/dao/business/model/Category.java
+++ b/server/service/src/main/java/com/doumee/dao/business/model/Category.java
@@ -91,6 +91,10 @@
     @ApiModelProperty(value = "浼佷笟缂栫爜")
     @ExcelColumn(name="浼佷笟缂栫爜")
     private Integer companyId;
+
+    @ApiModelProperty(value = "鐖剁骇鍝佺被ID锛孨ULL涓轰竴绾у搧绫�")
+    @ExcelColumn(name="鐖剁骇鍝佺被ID")
+    private Integer parentId;
     @ApiModelProperty(value = "骞冲彴鍒嗙被缂栫爜")
     @ExcelColumn(name="骞冲彴鍒嗙被缂栫爜")
     private Integer platCateId;
@@ -122,4 +126,8 @@
     @ApiModelProperty(value = "灞炴��2 鍒楄〃鍊�")
     @TableField(exist = false)
     private List<CateParamSelect> cateParamSecondList;
+
+    @ApiModelProperty(value = "瀛愮被鍒垪琛�")
+    @TableField(exist = false)
+    private List<Category> children;
 }
diff --git a/server/service/src/main/java/com/doumee/dao/business/model/Goods.java b/server/service/src/main/java/com/doumee/dao/business/model/Goods.java
index 65b3b70..0be1cbd 100644
--- a/server/service/src/main/java/com/doumee/dao/business/model/Goods.java
+++ b/server/service/src/main/java/com/doumee/dao/business/model/Goods.java
@@ -77,6 +77,10 @@
     @ExcelColumn(name="鎵�灞炲搧绫荤紪鐮�")
     private Integer categoryId;
 
+    @ApiModelProperty(value = "浜岀骇鍝佺被缂栫爜", example = "1")
+    @ExcelColumn(name="浜岀骇鍝佺被缂栫爜")
+    private Integer subCategoryId;
+
     @ApiModelProperty(value = "鎵�灞炲搧鐗岀紪鐮�", example = "1")
     @ExcelColumn(name="鎵�灞炲搧鐗岀紪鐮�")
     private Integer brandId;
@@ -136,6 +140,10 @@
     @TableField(exist = false)
     private String categoryName;
 
+    @ApiModelProperty(value = "浜岀骇绫诲埆鍚嶇О")
+    @TableField(exist = false)
+    private String subCategoryName;
+
     @ApiModelProperty(value = "鍝佺墝鍚嶇О")
     @TableField(exist = false)
     private String brandName;
diff --git a/server/service/src/main/java/com/doumee/dao/business/model/PreselectOrder.java b/server/service/src/main/java/com/doumee/dao/business/model/PreselectOrder.java
new file mode 100644
index 0000000..1b6675e
--- /dev/null
+++ b/server/service/src/main/java/com/doumee/dao/business/model/PreselectOrder.java
@@ -0,0 +1,65 @@
+package com.doumee.dao.business.model;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.util.Date;
+import java.util.List;
+
+@Data
+@ApiModel("棰勯�夎鍗�")
+@TableName("`preselect_order`")
+public class PreselectOrder {
+
+    @TableId(type = IdType.AUTO)
+    private Integer id;
+
+    private String orderNo;
+    private Integer companyId;
+    private Integer anchorUserId;
+    private String anchorUsername;
+    private BigDecimal userBudget;
+    private Integer categoryCount;
+    private Integer goodsCount;
+    private BigDecimal totalZdPrice;
+    private BigDecimal totalPrice;
+
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date sessionCreateTime;
+
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date createTime;
+
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date submitTime;
+
+    private Integer durationSeconds;
+    private Integer creator;
+    private Integer editor;
+
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date editDate;
+
+    private Integer isdeleted;
+
+    @TableField(exist = false)
+    private List<PreselectOrderItem> items;
+
+    @TableField(exist = false)
+    private String durationText;
+
+    @TableField(exist = false)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date submitTimeStart;
+
+    @TableField(exist = false)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date submitTimeEnd;
+}
diff --git a/server/service/src/main/java/com/doumee/dao/business/model/PreselectOrderItem.java b/server/service/src/main/java/com/doumee/dao/business/model/PreselectOrderItem.java
new file mode 100644
index 0000000..37251f7
--- /dev/null
+++ b/server/service/src/main/java/com/doumee/dao/business/model/PreselectOrderItem.java
@@ -0,0 +1,30 @@
+package com.doumee.dao.business.model;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import io.swagger.annotations.ApiModel;
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+@Data
+@ApiModel("棰勯�夎鍗曟槑缁�")
+@TableName("`preselect_order_item`")
+public class PreselectOrderItem {
+
+    @TableId(type = IdType.AUTO)
+    private Integer id;
+
+    private Integer orderId;
+    private Integer sortNum;
+    private Integer categoryId;
+    private String categoryName;
+    private Integer subCategoryId;
+    private String subCategoryName;
+    private Integer goodsId;
+    private String goodsName;
+    private BigDecimal zdPrice;
+    private BigDecimal price;
+    private Integer isdeleted;
+}
diff --git a/server/service/src/main/java/com/doumee/dao/business/model/PreselectOrderSeq.java b/server/service/src/main/java/com/doumee/dao/business/model/PreselectOrderSeq.java
new file mode 100644
index 0000000..9322b95
--- /dev/null
+++ b/server/service/src/main/java/com/doumee/dao/business/model/PreselectOrderSeq.java
@@ -0,0 +1,17 @@
+package com.doumee.dao.business.model;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+@Data
+@TableName("`preselect_order_seq`")
+public class PreselectOrderSeq {
+
+    @TableId(type = IdType.AUTO)
+    private Integer id;
+
+    private String seqKey;
+    private Integer seqVal;
+}
diff --git a/server/service/src/main/java/com/doumee/dao/business/model/WebParam.java b/server/service/src/main/java/com/doumee/dao/business/model/WebParam.java
index 0452999..9fe8787 100644
--- a/server/service/src/main/java/com/doumee/dao/business/model/WebParam.java
+++ b/server/service/src/main/java/com/doumee/dao/business/model/WebParam.java
@@ -79,6 +79,14 @@
     @ExcelColumn(name="1.0.2鐗堟湰鍗囩骇閰嶇疆瀛樺偍淇℃伅")
     private String newParam;
 
+    @ApiModelProperty(value = "涓绘挱绔痠ndex_3椤甸潰閰嶇疆JSON")
+    @ExcelColumn(name="涓绘挱绔〉闈㈤厤缃�")
+    private String anchorParam;
+
+    @ApiModelProperty(value = "涓绘挱绔敓鏁堥〉闈㈢増鏈� v2|v3锛岄粯璁� v2")
+    @ExcelColumn(name="涓绘挱绔〉闈㈢増鏈�")
+    private String anchorPageVersion;
+
     @ApiModelProperty(value = "鍓嶇紑鍦板潃")
     @TableField(exist = false)
     private String resourcePath;
diff --git a/server/service/src/main/java/com/doumee/dao/business/model/dto/GoodsRequest.java b/server/service/src/main/java/com/doumee/dao/business/model/dto/GoodsRequest.java
index 9fb9d92..531fc62 100644
--- a/server/service/src/main/java/com/doumee/dao/business/model/dto/GoodsRequest.java
+++ b/server/service/src/main/java/com/doumee/dao/business/model/dto/GoodsRequest.java
@@ -27,6 +27,9 @@
     @ApiModelProperty(value = "绫诲埆涓婚敭")
     private String categoryId;
 
+    @ApiModelProperty(value = "浜岀骇绫诲埆涓婚敭")
+    private String subCategoryId;
+
     @ApiModelProperty(value = "灞炴��1鍚嶇О")
     private String attrFirst;
 
diff --git a/server/service/src/main/java/com/doumee/dao/business/model/dto/H5InitDataDTO.java b/server/service/src/main/java/com/doumee/dao/business/model/dto/H5InitDataDTO.java
new file mode 100644
index 0000000..82a592d
--- /dev/null
+++ b/server/service/src/main/java/com/doumee/dao/business/model/dto/H5InitDataDTO.java
@@ -0,0 +1,30 @@
+package com.doumee.dao.business.model.dto;
+
+import com.doumee.dao.business.model.Brand;
+import com.doumee.dao.business.model.Category;
+import com.doumee.dao.business.model.Goods;
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * H5 涓绘挱绔〉闈㈠垵濮嬪寲鍏ㄩ噺鏁版嵁
+ */
+@Data
+public class H5InitDataDTO {
+
+    private String anchorParam;
+
+    private String resourcePath;
+
+    /** 涓绘挱绔敓鏁堢増鏈� v2|v3锛岄粯璁� v2 */
+    private String anchorPageVersion;
+
+    private List<Category> categories;
+
+    private List<Goods> goodsList;
+
+    private List<Brand> brandList;
+
+    private String orderNo;
+}
diff --git a/server/service/src/main/java/com/doumee/dao/business/model/dto/PreselectOrderItemDTO.java b/server/service/src/main/java/com/doumee/dao/business/model/dto/PreselectOrderItemDTO.java
new file mode 100644
index 0000000..1b003c0
--- /dev/null
+++ b/server/service/src/main/java/com/doumee/dao/business/model/dto/PreselectOrderItemDTO.java
@@ -0,0 +1,25 @@
+package com.doumee.dao.business.model.dto;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+@Data
+public class PreselectOrderItemDTO {
+
+    private Integer categoryId;
+    private String categoryName;
+    private Integer subCategoryId;
+    private String subCategoryName;
+    private Integer goodsId;
+
+    @ApiModelProperty("浜у搧鍨嬪彿")
+    private String goodsName;
+
+    @ApiModelProperty("鏃楄埌浠�")
+    private BigDecimal zdPrice;
+
+    @ApiModelProperty("鎸囧浠�")
+    private BigDecimal price;
+}
diff --git a/server/service/src/main/java/com/doumee/dao/business/model/dto/SubmitPreselectOrderDTO.java b/server/service/src/main/java/com/doumee/dao/business/model/dto/SubmitPreselectOrderDTO.java
new file mode 100644
index 0000000..1972b86
--- /dev/null
+++ b/server/service/src/main/java/com/doumee/dao/business/model/dto/SubmitPreselectOrderDTO.java
@@ -0,0 +1,26 @@
+package com.doumee.dao.business.model.dto;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.util.Date;
+import java.util.List;
+
+@Data
+public class SubmitPreselectOrderDTO {
+
+    @ApiModelProperty("璁㈠崟缂栧彿")
+    private String orderNo;
+
+    @ApiModelProperty("瀹㈡埛棰勭畻")
+    private BigDecimal userBudget;
+
+    @ApiModelProperty("椤甸潰浼氳瘽鍒涘缓鏃堕棿")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date sessionCreateTime;
+
+    @ApiModelProperty("璁㈠崟鏄庣粏")
+    private List<PreselectOrderItemDTO> items;
+}
diff --git a/server/service/src/main/java/com/doumee/dao/system/dto/UpdateWebParamDTO.java b/server/service/src/main/java/com/doumee/dao/system/dto/UpdateWebParamDTO.java
index aaf5079..24374e8 100644
--- a/server/service/src/main/java/com/doumee/dao/system/dto/UpdateWebParamDTO.java
+++ b/server/service/src/main/java/com/doumee/dao/system/dto/UpdateWebParamDTO.java
@@ -32,5 +32,11 @@
     @ApiModelProperty(value = "閰嶇疆鍙傛暟 1.0.2鐗堟湰鍙渶瑕佷紶璇ュ弬鏁�")
     private String newParam;
 
+    @ApiModelProperty(value = "涓绘挱绔痠ndex_3椤甸潰閰嶇疆JSON")
+    private String anchorParam;
+
+    @ApiModelProperty(value = "涓绘挱绔敓鏁堥〉闈㈢増鏈� v2|v3")
+    private String anchorPageVersion;
+
 
 }
diff --git a/server/service/src/main/java/com/doumee/service/business/CategoryService.java b/server/service/src/main/java/com/doumee/service/business/CategoryService.java
index 8e83906..82630f4 100644
--- a/server/service/src/main/java/com/doumee/service/business/CategoryService.java
+++ b/server/service/src/main/java/com/doumee/service/business/CategoryService.java
@@ -110,4 +110,14 @@
      * @return long
      */
     long count(Category category);
+
+    List<Category> findTree(Category category);
+
+    List<Category> findChildren(Integer parentId);
+
+    Integer createSubCategory(Category category);
+
+    void updateSubCategory(Category category);
+
+    void deleteSubCategory(Integer id);
 }
diff --git a/server/service/src/main/java/com/doumee/service/business/H5InitService.java b/server/service/src/main/java/com/doumee/service/business/H5InitService.java
new file mode 100644
index 0000000..47dd65f
--- /dev/null
+++ b/server/service/src/main/java/com/doumee/service/business/H5InitService.java
@@ -0,0 +1,11 @@
+package com.doumee.service.business;
+
+import com.doumee.dao.business.model.dto.H5InitDataDTO;
+
+/**
+ * H5 涓绘挱绔垵濮嬪寲鏁版嵁
+ */
+public interface H5InitService {
+
+    H5InitDataDTO loadInitData();
+}
diff --git a/server/service/src/main/java/com/doumee/service/business/PreselectOrderService.java b/server/service/src/main/java/com/doumee/service/business/PreselectOrderService.java
new file mode 100644
index 0000000..912f4c1
--- /dev/null
+++ b/server/service/src/main/java/com/doumee/service/business/PreselectOrderService.java
@@ -0,0 +1,23 @@
+package com.doumee.service.business;
+
+import com.doumee.core.model.PageData;
+import com.doumee.core.model.PageWrap;
+import com.doumee.dao.business.model.PreselectOrder;
+import com.doumee.dao.business.model.dto.SubmitPreselectOrderDTO;
+
+import javax.servlet.http.HttpServletResponse;
+
+public interface PreselectOrderService {
+
+    String generateOrderNo();
+
+    void submit(SubmitPreselectOrderDTO dto);
+
+    PageData<PreselectOrder> findPage(PageWrap<PreselectOrder> pageWrap);
+
+    PreselectOrder findDetail(Integer id);
+
+    void deleteById(Integer id);
+
+    void exportDetail(Integer id, HttpServletResponse response);
+}
diff --git a/server/service/src/main/java/com/doumee/service/business/WebParamService.java b/server/service/src/main/java/com/doumee/service/business/WebParamService.java
index 01605f5..afdcb9a 100644
--- a/server/service/src/main/java/com/doumee/service/business/WebParamService.java
+++ b/server/service/src/main/java/com/doumee/service/business/WebParamService.java
@@ -20,4 +20,10 @@
     void renew(UpdateWebParamDTO updateWebParamDTO);
 
     void renewUpdate(UpdateWebParamDTO updateWebParamDTO);
+
+    WebParam findOneAnchor();
+
+    void renewAnchorUpdate(UpdateWebParamDTO updateWebParamDTO);
+
+    void updateAnchorPageVersion(String anchorPageVersion);
 }
diff --git a/server/service/src/main/java/com/doumee/service/business/impl/CategoryServiceImpl.java b/server/service/src/main/java/com/doumee/service/business/impl/CategoryServiceImpl.java
index 1497b58..878c184 100644
--- a/server/service/src/main/java/com/doumee/service/business/impl/CategoryServiceImpl.java
+++ b/server/service/src/main/java/com/doumee/service/business/impl/CategoryServiceImpl.java
@@ -52,6 +52,9 @@
     @Autowired
     private CateParamSelectMapper cateParamSelectMapper;
 
+    @Autowired
+    private com.doumee.dao.business.GoodsMapper goodsMapper;
+
     @Override
     public Integer create(Category category) {
         LoginUserInfo user = (LoginUserInfo) SecurityUtils.getSubject().getPrincipal();
@@ -136,6 +139,7 @@
         category.setCreator(user.getId());
         category.setIsdeleted(Constants.ZERO);
         category.setCompanyId(user.getCompanyId());
+        category.setParentId(null);
         //澶勭悊鎷奸煶闂
         category.setPinyin(PinYinUtil.getFullSpell(category.getName()));
         category.setShortPinyin(PinYinUtil.getFirstSpell(category.getName()));
@@ -426,6 +430,7 @@
                 .eq("STATUS",Constants.ZERO)
                 .eq("ISDELETED",Constants.ZERO)
                 .eq("COMPANY_ID",user.getCompanyId())
+                .isNull("PARENT_ID")
                 .orderByAsc(" SORTNUM ");
         List<Category> list = categoryMapper.selectList(wrapper);
         String prefixUrl = systemDictDataBiz.queryByCode(Constants.OSS, Constants.RESOURCE_PATH).getCode()
@@ -435,6 +440,16 @@
             c.setBudgetList(cateBudgetMapper.selectList(new QueryWrapper<CateBudget>().eq("CATEGORY_ID",c.getId())
                     .orderByAsc(" SORTNUM ")));
             this.getParamSelect(c);
+            List<Category> children = categoryMapper.selectList(new QueryWrapper<Category>()
+                    .eq("PARENT_ID", c.getId())
+                    .eq("STATUS", Constants.ZERO)
+                    .eq("ISDELETED", Constants.ZERO)
+                    .orderByAsc("SORTNUM")
+                    .last(" limit 5 "));
+            for (Category child : children) {
+                child.setPrefixUrl(prefixUrl);
+            }
+            c.setChildren(children);
         }
         return list;
     }
@@ -555,4 +570,114 @@
 
 
 
+    @Override
+    public List<Category> findTree(Category category) {
+        LoginUserInfo user = (LoginUserInfo) SecurityUtils.getSubject().getPrincipal();
+        QueryWrapper<Category> wrapper = new QueryWrapper<>();
+        wrapper.eq("STATUS", Constants.ZERO)
+                .eq("ISDELETED", Constants.ZERO)
+                .eq("COMPANY_ID", user.getCompanyId())
+                .isNull("PARENT_ID")
+                .orderByAsc("SORTNUM");
+        if (category != null && org.apache.commons.lang3.StringUtils.isNotBlank(category.getName())) {
+            wrapper.like("NAME", category.getName());
+        }
+        if (category != null && category.getType() != null) {
+            wrapper.eq("TYPE", category.getType());
+        }
+        List<Category> parents = categoryMapper.selectList(wrapper);
+        String prefixUrl = systemDictDataBiz.queryByCode(Constants.OSS, Constants.RESOURCE_PATH).getCode()
+                + systemDictDataBiz.queryByCode(Constants.OSS, Constants.CATEGORY_IMG).getCode();
+        for (Category parent : parents) {
+            parent.setPrefixUrl(prefixUrl);
+            parent.setBudgetList(cateBudgetMapper.selectList(new QueryWrapper<CateBudget>().eq("CATEGORY_ID", parent.getId()).orderByAsc(" SORTNUM ")));
+            List<Category> children = categoryMapper.selectList(new QueryWrapper<Category>()
+                    .eq("PARENT_ID", parent.getId())
+                    .eq("ISDELETED", Constants.ZERO)
+                    .orderByAsc("SORTNUM"));
+            for (Category child : children) {
+                child.setPrefixUrl(prefixUrl);
+            }
+            parent.setChildren(children);
+        }
+        return parents;
+    }
+
+    @Override
+    public List<Category> findChildren(Integer parentId) {
+        LoginUserInfo user = (LoginUserInfo) SecurityUtils.getSubject().getPrincipal();
+        Category parent = categoryMapper.selectById(parentId);
+        if (parent == null || !user.getCompanyId().equals(parent.getCompanyId())) {
+            throw new BusinessException(ResponseStatus.DATA_EMPTY.getCode(), "鐖剁骇鍝佺被涓嶅瓨鍦�");
+        }
+        String prefixUrl = systemDictDataBiz.queryByCode(Constants.OSS, Constants.RESOURCE_PATH).getCode()
+                + systemDictDataBiz.queryByCode(Constants.OSS, Constants.CATEGORY_IMG).getCode();
+        List<Category> children = categoryMapper.selectList(new QueryWrapper<Category>()
+                .eq("PARENT_ID", parentId)
+                .eq("ISDELETED", Constants.ZERO)
+                .eq("STATUS", Constants.ZERO)
+                .orderByAsc("SORTNUM"));
+        children.forEach(c -> c.setPrefixUrl(prefixUrl));
+        return children;
+    }
+
+    @Override
+    public Integer createSubCategory(Category category) {
+        LoginUserInfo user = (LoginUserInfo) SecurityUtils.getSubject().getPrincipal();
+        if (category.getParentId() == null) {
+            throw new BusinessException(ResponseStatus.NOT_ALLOWED.getCode(), "鐖剁骇鍝佺被涓嶈兘涓虹┖");
+        }
+        Category parent = categoryMapper.selectById(category.getParentId());
+        if (parent == null || parent.getParentId() != null || !user.getCompanyId().equals(parent.getCompanyId())) {
+            throw new BusinessException(ResponseStatus.NOT_ALLOWED.getCode(), "鍙兘鍦ㄦ湁鏁堢殑涓�绾у搧绫讳笅鍒涘缓瀛愮被鍒�");
+        }
+        if (categoryMapper.selectCount(new QueryWrapper<Category>().eq("ISDELETED", Constants.ZERO)
+                .eq("COMPANY_ID", user.getCompanyId()).eq("name", category.getName())) > 0) {
+            throw new BusinessException(ResponseStatus.NOT_ALLOWED.getCode(), "銆�" + category.getName() + "銆戝凡瀛樺湪");
+        }
+        category.setStatus(Constants.ZERO);
+        category.setCreateDate(new Date());
+        category.setCreator(user.getId());
+        category.setIsdeleted(Constants.ZERO);
+        category.setCompanyId(user.getCompanyId());
+        category.setType(parent.getType());
+        category.setPinyin(PinYinUtil.getFullSpell(category.getName()));
+        category.setShortPinyin(PinYinUtil.getFirstSpell(category.getName()));
+        categoryMapper.insert(category);
+        return category.getId();
+    }
+
+    @Override
+    public void updateSubCategory(Category category) {
+        LoginUserInfo user = (LoginUserInfo) SecurityUtils.getSubject().getPrincipal();
+        Category db = categoryMapper.selectById(category.getId());
+        if (db == null || db.getParentId() == null) {
+            throw new BusinessException(ResponseStatus.DATA_EMPTY.getCode(), "瀛愮被鍒笉瀛樺湪");
+        }
+        if (categoryMapper.selectCount(new QueryWrapper<Category>().eq("ISDELETED", Constants.ZERO)
+                .eq("COMPANY_ID", user.getCompanyId()).ne("id", category.getId()).eq("name", category.getName())) > 0) {
+            throw new BusinessException(ResponseStatus.NOT_ALLOWED.getCode(), "銆�" + category.getName() + "銆戝凡瀛樺湪");
+        }
+        category.setEditDate(new Date());
+        category.setEditor(user.getId());
+        category.setPinyin(PinYinUtil.getFullSpell(category.getName()));
+        category.setShortPinyin(PinYinUtil.getFirstSpell(category.getName()));
+        categoryMapper.updateById(category);
+    }
+
+    @Override
+    public void deleteSubCategory(Integer id) {
+        Category category = categoryMapper.selectById(id);
+        if (category == null || category.getParentId() == null) {
+            throw new BusinessException(ResponseStatus.DATA_EMPTY.getCode(), "瀛愮被鍒笉瀛樺湪");
+        }
+        long goodsCount = goodsMapper.selectCount(new QueryWrapper<Goods>()
+                .eq("ISDELETED", Constants.ZERO)
+                .and(w -> w.eq("SUB_CATEGORY_ID", id).or().eq("CATEGORY_ID", id)));
+        if (goodsCount > 0) {
+            throw new BusinessException(ResponseStatus.NOT_ALLOWED.getCode(), "璇ュ瓙绫诲埆涓嬪瓨鍦ㄥ叧鑱斿晢鍝侊紝鏃犳硶鍒犻櫎");
+        }
+        category.setIsdeleted(Constants.ONE);
+        categoryMapper.updateById(category);
+    }
 }
diff --git a/server/service/src/main/java/com/doumee/service/business/impl/GoodsServiceImpl.java b/server/service/src/main/java/com/doumee/service/business/impl/GoodsServiceImpl.java
index b714aac..c1e1d93 100644
--- a/server/service/src/main/java/com/doumee/service/business/impl/GoodsServiceImpl.java
+++ b/server/service/src/main/java/com/doumee/service/business/impl/GoodsServiceImpl.java
@@ -354,9 +354,9 @@
         goods.setCreateDate(new Date());
         goods.setCreator(user.getId());
         goods.setIsdeleted(Constants.ZERO);
-        //澶勭悊鎷奸煶闂
         goods.setPinyin(PinYinUtil.getFullSpell(goods.getName()));
         goods.setShortPinyin(PinYinUtil.getFirstSpell(goods.getName()));
+        this.validateSubCategory(goods);
         goodsMapper.insert(goods);
 
         List<Multifile> multifileList = goods.getMultifileList();
@@ -484,9 +484,9 @@
         goods.setEditDate(new Date());
         goods.setEditor(user.getId());
         goods.setIsdeleted(Constants.ZERO);
-        //澶勭悊鎷奸煶闂
         goods.setPinyin(PinYinUtil.getFullSpell(goods.getName()));
         goods.setShortPinyin(PinYinUtil.getFirstSpell(goods.getName()));
+        this.validateSubCategory(goods);
         goodsMapper.updateById(goods);
 
         multifileMapper.delete(new QueryWrapper<Multifile>().eq("OBJ_ID",goods.getId())
@@ -1255,6 +1255,7 @@
         queryWrapper.eq(Goods::getIsdeleted,Constants.ZERO);
 
         queryWrapper.eq(!Objects.isNull(pageWrap.getModel()) && !Objects.isNull(pageWrap.getModel().getCategoryId()), Goods::getCategoryId, pageWrap.getModel().getCategoryId())
+                .eq(!Objects.isNull(pageWrap.getModel()) && !Objects.isNull(pageWrap.getModel().getSubCategoryId()), Goods::getSubCategoryId, pageWrap.getModel().getSubCategoryId())
                 .eq(!Objects.isNull(pageWrap.getModel()) && !Objects.isNull(pageWrap.getModel().getBrandId()), Goods::getBrandId, pageWrap.getModel().getBrandId())
                 .and(!Objects.isNull(pageWrap.getModel()) && StringUtils.isNotBlank(pageWrap.getModel().getKeyword()),
                         i->i.like(Goods::getPinyin,pageWrap.getModel().getKeyword())
@@ -1275,6 +1276,16 @@
             this.dealGoodsMsg(goodsIPage.getRecords());
         }
         return PageData.from(goodsIPage);
+    }
+
+    private void validateSubCategory(Goods goods) {
+        if (goods.getSubCategoryId() == null) {
+            return;
+        }
+        Category sub = categoryMapper.selectById(goods.getSubCategoryId());
+        if (sub == null || sub.getParentId() == null || !sub.getParentId().equals(goods.getCategoryId())) {
+            throw new BusinessException(ResponseStatus.NOT_ALLOWED.getCode(), "浜岀骇绫诲埆蹇呴』灞炰簬鎵�閫変竴绾у搧绫�");
+        }
     }
 
 
@@ -1321,6 +1332,7 @@
         queryWrapper.eq(Goods::getIsdeleted,Constants.ZERO);
         queryWrapper.eq(Goods::getCompanyId,loginUserInfo.getCompanyId());
         queryWrapper.eq(!Objects.isNull(goodsRequest) && !Objects.isNull(goodsRequest.getCategoryId()), Goods::getCategoryId, goodsRequest.getCategoryId())
+                .eq(!Objects.isNull(goodsRequest) && !Objects.isNull(goodsRequest.getSubCategoryId()), Goods::getSubCategoryId, goodsRequest.getSubCategoryId())
                 .eq(!Objects.isNull(goodsRequest) && !Objects.isNull(goodsRequest.getBrandId()), Goods::getBrandId, goodsRequest.getBrandId())
                 .and(!Objects.isNull(goodsRequest) && StringUtils.isNotBlank(goodsRequest.getKeyword()),
                         i->i.like(Goods::getPinyin,goodsRequest.getKeyword())
diff --git a/server/service/src/main/java/com/doumee/service/business/impl/H5InitServiceImpl.java b/server/service/src/main/java/com/doumee/service/business/impl/H5InitServiceImpl.java
new file mode 100644
index 0000000..d973f90
--- /dev/null
+++ b/server/service/src/main/java/com/doumee/service/business/impl/H5InitServiceImpl.java
@@ -0,0 +1,51 @@
+package com.doumee.service.business.impl;
+
+import com.doumee.dao.business.model.Brand;
+import com.doumee.dao.business.model.Category;
+import com.doumee.dao.business.model.WebParam;
+import com.doumee.dao.business.model.dto.GoodsRequest;
+import com.doumee.dao.business.model.dto.H5InitDataDTO;
+import com.doumee.service.business.BrandService;
+import com.doumee.service.business.CategoryService;
+import com.doumee.service.business.GoodsService;
+import com.doumee.service.business.H5InitService;
+import com.doumee.service.business.PreselectOrderService;
+import com.doumee.service.business.WebParamService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+/**
+ * H5 涓绘挱绔垵濮嬪寲鍏ㄩ噺鏁版嵁
+ */
+@Service
+public class H5InitServiceImpl implements H5InitService {
+
+    @Autowired
+    private WebParamService webParamService;
+
+    @Autowired
+    private CategoryService categoryService;
+
+    @Autowired
+    private GoodsService goodsService;
+
+    @Autowired
+    private BrandService brandService;
+
+    @Autowired
+    private PreselectOrderService preselectOrderService;
+
+    @Override
+    public H5InitDataDTO loadInitData() {
+        H5InitDataDTO dto = new H5InitDataDTO();
+        WebParam webParam = webParamService.findOneAnchor();
+        dto.setAnchorParam(webParam.getAnchorParam());
+        dto.setAnchorPageVersion(webParam.getAnchorPageVersion() != null ? webParam.getAnchorPageVersion() : "v2");
+        dto.setResourcePath(webParam.getResourcePath());
+        dto.setCategories(categoryService.findListSaaS(new Category()));
+        dto.setGoodsList(goodsService.findListForH5(new GoodsRequest()));
+        dto.setBrandList(brandService.findList(new Brand()));
+        dto.setOrderNo(preselectOrderService.generateOrderNo());
+        return dto;
+    }
+}
diff --git a/server/service/src/main/java/com/doumee/service/business/impl/PreselectOrderServiceImpl.java b/server/service/src/main/java/com/doumee/service/business/impl/PreselectOrderServiceImpl.java
new file mode 100644
index 0000000..6210732
--- /dev/null
+++ b/server/service/src/main/java/com/doumee/service/business/impl/PreselectOrderServiceImpl.java
@@ -0,0 +1,335 @@
+package com.doumee.service.business.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.doumee.core.constants.ResponseStatus;
+import com.doumee.core.exception.BusinessException;
+import com.doumee.core.model.LoginUserInfo;
+import com.doumee.core.model.PageData;
+import com.doumee.core.model.PageWrap;
+import com.doumee.core.utils.Constants;
+import com.doumee.core.utils.Utils;
+import com.doumee.dao.business.PreselectOrderItemMapper;
+import com.doumee.dao.business.PreselectOrderMapper;
+import com.doumee.dao.business.PreselectOrderSeqMapper;
+import com.doumee.dao.business.model.PreselectOrder;
+import com.doumee.dao.business.model.PreselectOrderItem;
+import com.doumee.dao.business.model.PreselectOrderSeq;
+import com.doumee.dao.business.model.dto.PreselectOrderItemDTO;
+import com.doumee.dao.business.model.dto.SubmitPreselectOrderDTO;
+import com.doumee.service.business.PreselectOrderService;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.poi.ss.usermodel.*;
+import org.apache.poi.ss.util.CellRangeAddress;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+import org.apache.shiro.SecurityUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.CollectionUtils;
+
+import javax.servlet.http.HttpServletResponse;
+import java.math.BigDecimal;
+import java.net.URLEncoder;
+import java.text.SimpleDateFormat;
+import java.util.*;
+
+@Service
+public class PreselectOrderServiceImpl implements PreselectOrderService {
+
+    @Autowired
+    private PreselectOrderMapper preselectOrderMapper;
+
+    @Autowired
+    private PreselectOrderItemMapper preselectOrderItemMapper;
+
+    @Autowired
+    private PreselectOrderSeqMapper preselectOrderSeqMapper;
+
+    @Override
+    @Transactional
+    public synchronized String generateOrderNo() {
+        String prefix = new SimpleDateFormat("yyyyMMddHHmm").format(new Date());
+        PreselectOrderSeq seq = preselectOrderSeqMapper.selectOne(new QueryWrapper<PreselectOrderSeq>().eq("SEQ_KEY", prefix));
+        int nextVal = 1;
+        if (seq == null) {
+            seq = new PreselectOrderSeq();
+            seq.setSeqKey(prefix);
+            seq.setSeqVal(1);
+            preselectOrderSeqMapper.insert(seq);
+        } else {
+            nextVal = seq.getSeqVal() + 1;
+            seq.setSeqVal(nextVal);
+            preselectOrderSeqMapper.updateById(seq);
+        }
+        return prefix + String.format("%03d", nextVal);
+    }
+
+    @Override
+    @Transactional
+    public void submit(SubmitPreselectOrderDTO dto) {
+        LoginUserInfo user = (LoginUserInfo) SecurityUtils.getSubject().getPrincipal();
+        if (!user.getType().equals(Constants.UserType.ZHUBO.getKey())) {
+            throw new BusinessException(ResponseStatus.NOT_ALLOWED.getCode(), "闈炰富鎾敤鎴锋棤娉曟彁浜よ鍗�");
+        }
+        if (StringUtils.isBlank(dto.getOrderNo())) {
+            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "璁㈠崟缂栧彿涓嶈兘涓虹┖");
+        }
+        if (CollectionUtils.isEmpty(dto.getItems())) {
+            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "璇疯嚦灏戦�夋嫨涓�涓晢鍝�");
+        }
+        long exists = preselectOrderMapper.selectCount(new QueryWrapper<PreselectOrder>()
+                .eq("ORDER_NO", dto.getOrderNo()).eq("ISDELETED", Constants.ZERO));
+        if (exists > 0) {
+            throw new BusinessException(ResponseStatus.NOT_ALLOWED.getCode(), "璁㈠崟缂栧彿宸插瓨鍦紝璇峰埛鏂伴〉闈�");
+        }
+        Date now = new Date();
+        Date sessionCreate = dto.getSessionCreateTime() != null ? dto.getSessionCreateTime() : now;
+        long durationSeconds = Math.max(0, (now.getTime() - sessionCreate.getTime()) / 1000);
+
+        Set<Integer> categorySet = new HashSet<>();
+        BigDecimal totalZd = BigDecimal.ZERO;
+        BigDecimal totalPrice = BigDecimal.ZERO;
+        int goodsCount = 0;
+        List<PreselectOrderItemDTO> validItems = new ArrayList<>();
+        for (PreselectOrderItemDTO item : dto.getItems()) {
+            if (item.getGoodsId() == null || StringUtils.isBlank(item.getGoodsName())) {
+                continue;
+            }
+            validItems.add(item);
+            goodsCount++;
+            if (item.getCategoryId() != null) {
+                categorySet.add(item.getCategoryId());
+            }
+            if (item.getZdPrice() != null) {
+                totalZd = totalZd.add(item.getZdPrice());
+            }
+            if (item.getPrice() != null) {
+                totalPrice = totalPrice.add(item.getPrice());
+            }
+        }
+        if (validItems.isEmpty()) {
+            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "璇疯嚦灏戦�夋嫨涓�涓湁鏁堝晢鍝�");
+        }
+
+        PreselectOrder order = new PreselectOrder();
+        order.setOrderNo(dto.getOrderNo());
+        order.setCompanyId(user.getCompanyId());
+        order.setAnchorUserId(user.getId());
+        order.setAnchorUsername(user.getUsername());
+        order.setUserBudget(dto.getUserBudget() != null ? dto.getUserBudget() : BigDecimal.ZERO);
+        order.setCategoryCount(categorySet.size());
+        order.setGoodsCount(goodsCount);
+        order.setTotalZdPrice(totalZd);
+        order.setTotalPrice(totalPrice);
+        order.setSessionCreateTime(sessionCreate);
+        order.setCreateTime(sessionCreate);
+        order.setSubmitTime(now);
+        order.setDurationSeconds((int) durationSeconds);
+        order.setCreator(user.getId());
+        order.setIsdeleted(Constants.ZERO);
+        preselectOrderMapper.insert(order);
+
+        int sort = 1;
+        for (PreselectOrderItemDTO itemDto : validItems) {
+            PreselectOrderItem item = new PreselectOrderItem();
+            item.setOrderId(order.getId());
+            item.setSortNum(sort++);
+            item.setCategoryId(itemDto.getCategoryId());
+            item.setCategoryName(itemDto.getCategoryName());
+            item.setSubCategoryId(itemDto.getSubCategoryId());
+            item.setSubCategoryName(itemDto.getSubCategoryName());
+            item.setGoodsId(itemDto.getGoodsId());
+            item.setGoodsName(itemDto.getGoodsName());
+            item.setZdPrice(itemDto.getZdPrice());
+            item.setPrice(itemDto.getPrice());
+            item.setIsdeleted(Constants.ZERO);
+            preselectOrderItemMapper.insert(item);
+        }
+    }
+
+    @Override
+    public PageData<PreselectOrder> findPage(PageWrap<PreselectOrder> pageWrap) {
+        LoginUserInfo user = (LoginUserInfo) SecurityUtils.getSubject().getPrincipal();
+        IPage<PreselectOrder> page = new Page<>(pageWrap.getPage(), pageWrap.getCapacity());
+        QueryWrapper<PreselectOrder> queryWrapper = new QueryWrapper<>();
+        Utils.MP.blankToNull(pageWrap.getModel());
+        queryWrapper.eq("ISDELETED", Constants.ZERO);
+        queryWrapper.eq("COMPANY_ID", user.getCompanyId());
+        if (pageWrap.getModel() != null) {
+            if (StringUtils.isNotBlank(pageWrap.getModel().getOrderNo())) {
+                queryWrapper.like("ORDER_NO", pageWrap.getModel().getOrderNo());
+            }
+            if (StringUtils.isNotBlank(pageWrap.getModel().getAnchorUsername())) {
+                queryWrapper.like("ANCHOR_USERNAME", pageWrap.getModel().getAnchorUsername());
+            }
+            if (pageWrap.getModel().getSubmitTimeStart() != null) {
+                queryWrapper.ge("SUBMIT_TIME", pageWrap.getModel().getSubmitTimeStart());
+            }
+            if (pageWrap.getModel().getSubmitTimeEnd() != null) {
+                queryWrapper.le("SUBMIT_TIME", pageWrap.getModel().getSubmitTimeEnd());
+            }
+        }
+        queryWrapper.orderByDesc("SUBMIT_TIME");
+        IPage<PreselectOrder> result = preselectOrderMapper.selectPage(page, queryWrapper);
+        result.getRecords().forEach(o -> o.setDurationText(formatDuration(o.getDurationSeconds())));
+        return PageData.from(result);
+    }
+
+    @Override
+    public PreselectOrder findDetail(Integer id) {
+        LoginUserInfo user = (LoginUserInfo) SecurityUtils.getSubject().getPrincipal();
+        PreselectOrder order = preselectOrderMapper.selectById(id);
+        if (order == null || !Objects.equals(order.getIsdeleted(), Constants.ZERO) || !user.getCompanyId().equals(order.getCompanyId())) {
+            throw new BusinessException(ResponseStatus.DATA_EMPTY.getCode(), "璁㈠崟涓嶅瓨鍦�");
+        }
+        order.setDurationText(formatDuration(order.getDurationSeconds()));
+        List<PreselectOrderItem> items = preselectOrderItemMapper.selectList(new QueryWrapper<PreselectOrderItem>()
+                .eq("ORDER_ID", id).eq("ISDELETED", Constants.ZERO).orderByAsc("SORT_NUM"));
+        order.setItems(items);
+        return order;
+    }
+
+    @Override
+    public void deleteById(Integer id) {
+        PreselectOrder order = findDetail(id);
+        order.setIsdeleted(Constants.ONE);
+        preselectOrderMapper.updateById(order);
+    }
+
+    @Override
+    public void exportDetail(Integer id, HttpServletResponse response) {
+        PreselectOrder order = findDetail(id);
+        try (Workbook workbook = new XSSFWorkbook()) {
+            Sheet sheet = workbook.createSheet("璁㈠崟鏄庣粏");
+            sheet.setColumnWidth(0, 3000);
+            sheet.setColumnWidth(1, 6000);
+            sheet.setColumnWidth(2, 8000);
+            sheet.setColumnWidth(3, 6000);
+            sheet.setColumnWidth(4, 4000);
+
+            CellStyle titleStyle = workbook.createCellStyle();
+            Font titleFont = workbook.createFont();
+            titleFont.setBold(true);
+            titleFont.setFontHeightInPoints((short) 14);
+            titleStyle.setFont(titleFont);
+            titleStyle.setAlignment(HorizontalAlignment.CENTER);
+
+            CellStyle headerStyle = workbook.createCellStyle();
+            Font headerFont = workbook.createFont();
+            headerFont.setBold(true);
+            headerFont.setColor(IndexedColors.WHITE.getIndex());
+            headerStyle.setFont(headerFont);
+            headerStyle.setFillForegroundColor(IndexedColors.DARK_TEAL.getIndex());
+            headerStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
+            headerStyle.setAlignment(HorizontalAlignment.CENTER);
+            headerStyle.setBorderBottom(BorderStyle.THIN);
+            headerStyle.setBorderTop(BorderStyle.THIN);
+            headerStyle.setBorderLeft(BorderStyle.THIN);
+            headerStyle.setBorderRight(BorderStyle.THIN);
+
+            CellStyle dataStyle = workbook.createCellStyle();
+            dataStyle.setBorderBottom(BorderStyle.THIN);
+            dataStyle.setBorderTop(BorderStyle.THIN);
+            dataStyle.setBorderLeft(BorderStyle.THIN);
+            dataStyle.setBorderRight(BorderStyle.THIN);
+            dataStyle.setVerticalAlignment(VerticalAlignment.CENTER);
+
+            CellStyle labelStyle = workbook.createCellStyle();
+            Font labelFont = workbook.createFont();
+            labelFont.setBold(true);
+            labelStyle.setFont(labelFont);
+
+            int rowIdx = 0;
+            Row titleRow = sheet.createRow(rowIdx++);
+            Cell titleCell = titleRow.createCell(0);
+            titleCell.setCellValue("棰勯�夎鍗曟槑缁嗘竻鍗�");
+            titleCell.setCellStyle(titleStyle);
+            sheet.addMergedRegion(new CellRangeAddress(0, 0, 0, 4));
+
+            rowIdx++;
+            String[][] summary = {
+                    {"璁㈠崟缂栧彿", order.getOrderNo(), "瀹㈡埛棰勭畻", formatMoney(order.getUserBudget())},
+                    {"鍝佺被鏁伴噺", String.valueOf(order.getCategoryCount()), "鍟嗗搧鏁伴噺", String.valueOf(order.getGoodsCount())},
+                    {"鏃楄埌浠峰悎璁�", formatMoney(order.getTotalZdPrice()), "鎸囧浠峰悎璁�", formatMoney(order.getTotalPrice())},
+                    {"涓绘挱璐﹀彿", order.getAnchorUsername() != null ? order.getAnchorUsername() : "", "鍒涘缓鏃堕暱", order.getDurationText() != null ? order.getDurationText() : ""},
+                    {"鍒涘缓鏃堕棿", formatDate(order.getSessionCreateTime() != null ? order.getSessionCreateTime() : order.getCreateTime()), "鎻愪氦鏃堕棿", formatDate(order.getSubmitTime())}
+            };
+            for (String[] line : summary) {
+                Row row = sheet.createRow(rowIdx++);
+                for (int i = 0; i < line.length; i++) {
+                    Cell cell = row.createCell(i);
+                    cell.setCellValue(line[i]);
+                    cell.setCellStyle(i % 2 == 0 ? labelStyle : dataStyle);
+                }
+            }
+
+            rowIdx++;
+            Row headerRow = sheet.createRow(rowIdx++);
+            String[] headers = {"搴忓彿", "鍝佺被鍚嶇О", "浜у搧鍨嬪彿", "鏃楄埌浠�", "鎸囧浠�"};
+            for (int i = 0; i < headers.length; i++) {
+                Cell cell = headerRow.createCell(i);
+                cell.setCellValue(headers[i]);
+                cell.setCellStyle(headerStyle);
+            }
+            List<PreselectOrderItem> items = order.getItems() != null ? order.getItems() : Collections.emptyList();
+            for (PreselectOrderItem item : items) {
+                Row row = sheet.createRow(rowIdx++);
+                row.createCell(0).setCellValue(item.getSortNum() != null ? item.getSortNum() : 0);
+                row.createCell(1).setCellValue(item.getCategoryName() != null ? item.getCategoryName() : "");
+                row.createCell(2).setCellValue(item.getGoodsName() != null ? item.getGoodsName() : "");
+                row.createCell(3).setCellValue(formatMoney(item.getZdPrice()));
+                row.createCell(4).setCellValue(formatMoney(item.getPrice()));
+                for (int i = 0; i < 5; i++) {
+                    row.getCell(i).setCellStyle(dataStyle);
+                }
+            }
+
+            String fileName = "璁㈠崟_" + order.getOrderNo() + ".xlsx";
+            String encodeFileName = URLEncoder.encode(fileName, "UTF-8");
+            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
+            response.setHeader("Content-Disposition", "attachment;filename=" + encodeFileName);
+            response.setHeader("eva-opera-type", "download");
+            response.setHeader("eva-download-filename", encodeFileName);
+            workbook.write(response.getOutputStream());
+            response.flushBuffer();
+        } catch (Exception e) {
+            throw new BusinessException(ResponseStatus.EXPORT_EXCEL_ERROR.getCode(), "瀵煎嚭澶辫触");
+        }
+    }
+
+    private String formatDate(Date date) {
+        if (date == null) {
+            return "";
+        }
+        return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(date);
+    }
+
+    private String formatMoney(BigDecimal val) {
+        if (val == null) {
+            return "锟�0";
+        }
+        return "锟�" + val.stripTrailingZeros().toPlainString();
+    }
+
+    private String formatDuration(Integer seconds) {
+        if (seconds == null || seconds <= 0) {
+            return "0绉�";
+        }
+        int h = seconds / 3600;
+        int m = (seconds % 3600) / 60;
+        int s = seconds % 60;
+        StringBuilder sb = new StringBuilder();
+        if (h > 0) {
+            sb.append(h).append("灏忔椂");
+        }
+        if (m > 0) {
+            sb.append(m).append("鍒�");
+        }
+        if (s > 0 || sb.length() == 0) {
+            sb.append(s).append("绉�");
+        }
+        return sb.toString();
+    }
+}
diff --git a/server/service/src/main/java/com/doumee/service/business/impl/WebParamServiceImpl.java b/server/service/src/main/java/com/doumee/service/business/impl/WebParamServiceImpl.java
index 0fc1495..44e0e3e 100644
--- a/server/service/src/main/java/com/doumee/service/business/impl/WebParamServiceImpl.java
+++ b/server/service/src/main/java/com/doumee/service/business/impl/WebParamServiceImpl.java
@@ -51,6 +51,7 @@
         if(!Objects.isNull(webParam)){
             webParam.setResourcePath(systemDictDataBiz.queryByCode(Constants.OSS,Constants.RESOURCE_PATH).getCode() +
                     systemDictDataBiz.queryByCode(Constants.OSS,Constants.WEB_PARAM).getCode());
+            webParam.setAnchorPageVersion(normalizeAnchorPageVersion(webParam.getAnchorPageVersion()));
         }else{
             webParam = new WebParam();
             webParam.setResourcePath(systemDictDataBiz.queryByCode(Constants.OSS,Constants.RESOURCE_PATH).getCode() +
@@ -60,6 +61,7 @@
             webParam.setPkImg(systemDictDataBiz.queryByCode(Constants.WEB_PARAM,Constants.PK_IMG).getCode());
             webParam.setMainColor(systemDictDataBiz.queryByCode(Constants.WEB_PARAM,Constants.MAIN_COLOR).getCode());
             webParam.setRangeSize(systemDictDataBiz.queryByCode(Constants.WEB_PARAM,Constants.RANGE_SIZE).getCode());
+            webParam.setAnchorPageVersion(ANCHOR_PAGE_V2);
         }
         return webParam;
     }
@@ -71,6 +73,9 @@
         wrapper.lambda().eq(WebParam::getCompanyId,user.getCompanyId());
         wrapper.last(" limit 1  ");
         WebParam webParam = webParamMapper.selectOne(wrapper);
+        if (webParam != null) {
+            webParam.setAnchorPageVersion(normalizeAnchorPageVersion(webParam.getAnchorPageVersion()));
+        }
         return webParam;
     }
 
@@ -92,6 +97,7 @@
             webParamMapper.insert(webParam);
         }else{
             BeanUtils.copyProperties(updateWebParamDTO,webParam);
+            applyAnchorPageVersion(webParam, updateWebParamDTO.getAnchorPageVersion());
             webParam.setUpdateUser(user.getId());
             webParam.setUpdateTime(new Date());
             webParamMapper.updateById(webParam);
@@ -117,10 +123,110 @@
             webParamMapper.insert(webParam);
         }else{
             BeanUtils.copyProperties(updateWebParamDTO,webParam);
+            applyAnchorPageVersion(webParam, updateWebParamDTO.getAnchorPageVersion());
             webParam.setUpdateUser(user.getId());
             webParam.setUpdateTime(new Date());
             webParamMapper.updateById(webParam);
         }
     }
 
+    @Override
+    public WebParam findOneAnchor() {
+        LoginUserInfo user = (LoginUserInfo) SecurityUtils.getSubject().getPrincipal();
+        QueryWrapper<WebParam> wrapper = new QueryWrapper<>();
+        wrapper.lambda().eq(WebParam::getCompanyId, user.getCompanyId());
+        wrapper.last(" limit 1 ");
+        WebParam webParam = webParamMapper.selectOne(wrapper);
+        String resourcePath = systemDictDataBiz.queryByCode(Constants.OSS, Constants.RESOURCE_PATH).getCode()
+                + systemDictDataBiz.queryByCode(Constants.OSS, Constants.WEB_PARAM).getCode();
+        if (webParam == null) {
+            webParam = new WebParam();
+            webParam.setAnchorParam(getDefaultAnchorParam());
+            webParam.setAnchorPageVersion(ANCHOR_PAGE_V2);
+        } else if (StringUtils.isBlank(webParam.getAnchorParam())) {
+            webParam.setAnchorParam(getDefaultAnchorParam());
+        }
+        webParam.setAnchorPageVersion(normalizeAnchorPageVersion(webParam.getAnchorPageVersion()));
+        webParam.setResourcePath(resourcePath);
+        return webParam;
+    }
+
+    @Override
+    public void renewAnchorUpdate(UpdateWebParamDTO updateWebParamDTO) {
+        if (StringUtils.isBlank(updateWebParamDTO.getAnchorParam())) {
+            throw new BusinessException(ResponseStatus.BAD_REQUEST);
+        }
+        LoginUserInfo user = (LoginUserInfo) SecurityUtils.getSubject().getPrincipal();
+        QueryWrapper<WebParam> wrapper = new QueryWrapper<>();
+        wrapper.lambda().eq(WebParam::getCompanyId, user.getCompanyId());
+        wrapper.last(" limit 1 ");
+        WebParam webParam = webParamMapper.selectOne(wrapper);
+        if (Objects.isNull(webParam)) {
+            webParam = new WebParam();
+            webParam.setAnchorParam(updateWebParamDTO.getAnchorParam());
+            applyAnchorPageVersion(webParam, updateWebParamDTO.getAnchorPageVersion());
+            webParam.setCreateTime(new Date());
+            webParam.setDeleted(Constants.ZERO);
+            webParam.setCompanyId(user.getCompanyId());
+            webParam.setCreateUser(user.getId());
+            webParamMapper.insert(webParam);
+        } else {
+            webParam.setAnchorParam(updateWebParamDTO.getAnchorParam());
+            applyAnchorPageVersion(webParam, updateWebParamDTO.getAnchorPageVersion());
+            webParam.setUpdateUser(user.getId());
+            webParam.setUpdateTime(new Date());
+            webParamMapper.updateById(webParam);
+        }
+    }
+
+    @Override
+    public void updateAnchorPageVersion(String anchorPageVersion) {
+        LoginUserInfo user = (LoginUserInfo) SecurityUtils.getSubject().getPrincipal();
+        String version = normalizeAnchorPageVersion(anchorPageVersion);
+        QueryWrapper<WebParam> wrapper = new QueryWrapper<>();
+        wrapper.lambda().eq(WebParam::getCompanyId, user.getCompanyId());
+        wrapper.last(" limit 1 ");
+        WebParam webParam = webParamMapper.selectOne(wrapper);
+        if (Objects.isNull(webParam)) {
+            webParam = new WebParam();
+            webParam.setAnchorPageVersion(version);
+            webParam.setCreateTime(new Date());
+            webParam.setDeleted(Constants.ZERO);
+            webParam.setCompanyId(user.getCompanyId());
+            webParam.setCreateUser(user.getId());
+            webParamMapper.insert(webParam);
+        } else {
+            webParam.setAnchorPageVersion(version);
+            webParam.setUpdateUser(user.getId());
+            webParam.setUpdateTime(new Date());
+            webParamMapper.updateById(webParam);
+        }
+    }
+
+    private static final String ANCHOR_PAGE_V2 = "v2";
+    private static final String ANCHOR_PAGE_V3 = "v3";
+
+    private String normalizeAnchorPageVersion(String version) {
+        return ANCHOR_PAGE_V3.equalsIgnoreCase(StringUtils.trimToEmpty(version)) ? ANCHOR_PAGE_V3 : ANCHOR_PAGE_V2;
+    }
+
+    private void applyAnchorPageVersion(WebParam webParam, String anchorPageVersion) {
+        if (StringUtils.isNotBlank(anchorPageVersion)) {
+            webParam.setAnchorPageVersion(normalizeAnchorPageVersion(anchorPageVersion));
+        }
+    }
+
+    private String getDefaultAnchorParam() {
+        return "{\"rangeSize\":\"750\",\"main\":{\"bgType\":1,\"bgColor\":\"#E8DCC8\",\"alpha\":\"100\",\"bgImg\":{\"isShow\":1,\"type\":0}},"
+                + "\"topImg\":{\"type\":0,\"img\":\"\",\"imgurl\":\"\"},"
+                + "\"adImg\":{\"type\":0,\"img\":\"\",\"imgurl\":\"\"},"
+                + "\"pkImg\":{\"type\":0,\"img\":\"\",\"imgurl\":\"\"},"
+                + "\"header\":{\"backgroundType\":1,\"bgColor\":\"#4A3728\",\"bgAlpha\":\"100\",\"type\":1,\"color\":\"#FFFFFF\",\"alpha\":\"100\"},"
+                + "\"table\":{\"headerBg\":\"#4A3728\",\"headerColor\":\"#FFFFFF\",\"rowBg\":\"#F5EFE6\",\"hoverScale\":1.05},"
+                + "\"featured\":[{\"goodsId\":\"\",\"prefixUrl\":\"\",\"imgurl\":\"\",\"title\":\"\",\"tags\":[\"\",\"\"]},{\"goodsId\":\"\",\"prefixUrl\":\"\",\"imgurl\":\"\",\"title\":\"\",\"tags\":[\"\",\"\"]}],"
+                + "\"search\":{\"bgType\":1,\"bgColor\":\"#FFFFFF\",\"bgAlpha\":\"100\"},"
+                + "\"productList\":{\"bgType\":1,\"bgColor\":\"#F5EFE6\",\"bgAlpha\":\"100\"},"
+                + "\"pk\":{\"vsColor\":\"#FF8C42\"}}";
+    }
+
 }
diff --git a/server/zhubo/src/main/java/com/doumee/api/business/H5Controller.java b/server/zhubo/src/main/java/com/doumee/api/business/H5Controller.java
new file mode 100644
index 0000000..1c8d022
--- /dev/null
+++ b/server/zhubo/src/main/java/com/doumee/api/business/H5Controller.java
@@ -0,0 +1,30 @@
+package com.doumee.api.business;
+
+import com.doumee.api.BaseController;
+import com.doumee.core.model.ApiResponse;
+import com.doumee.dao.business.model.dto.H5InitDataDTO;
+import com.doumee.service.business.H5InitService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * H5 涓绘挱绔帴鍙�
+ */
+@Api(tags = "H5涓绘挱绔�")
+@RestController
+@RequestMapping("/business/h5")
+public class H5Controller extends BaseController {
+
+    @Autowired
+    private H5InitService h5InitService;
+
+    @ApiOperation("鍒濆鍖栧叏閲忔暟鎹紙椤甸潰閰嶇疆銆佸搧绫汇�佸晢鍝併�佸搧鐗屻�佽鍗曠紪鍙凤級")
+    @GetMapping("/initData")
+    public ApiResponse<H5InitDataDTO> initData() {
+        return ApiResponse.success(h5InitService.loadInitData());
+    }
+}
diff --git a/server/zhubo/src/main/java/com/doumee/api/business/PreselectOrderController.java b/server/zhubo/src/main/java/com/doumee/api/business/PreselectOrderController.java
new file mode 100644
index 0000000..0bdd24a
--- /dev/null
+++ b/server/zhubo/src/main/java/com/doumee/api/business/PreselectOrderController.java
@@ -0,0 +1,34 @@
+package com.doumee.api.business;
+
+import com.doumee.api.BaseController;
+import com.doumee.core.annotation.pr.PreventRepeat;
+import com.doumee.core.model.ApiResponse;
+import com.doumee.dao.business.model.dto.SubmitPreselectOrderDTO;
+import com.doumee.service.business.PreselectOrderService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+@Api(tags = "棰勯�夎鍗�-涓绘挱绔�")
+@RestController
+@RequestMapping("/business/order")
+public class PreselectOrderController extends BaseController {
+
+    @Autowired
+    private PreselectOrderService preselectOrderService;
+
+    @ApiOperation("鐢熸垚璁㈠崟缂栧彿")
+    @GetMapping("/generateOrderNo")
+    public ApiResponse<String> generateOrderNo() {
+        return ApiResponse.success(preselectOrderService.generateOrderNo());
+    }
+
+    @PreventRepeat
+    @ApiOperation("鎻愪氦璁㈠崟")
+    @PostMapping("/submit")
+    public ApiResponse submit(@RequestBody SubmitPreselectOrderDTO dto) {
+        preselectOrderService.submit(dto);
+        return ApiResponse.success("鎻愪氦鎴愬姛");
+    }
+}
diff --git a/server/zhubo/src/main/java/com/doumee/api/business/WebParamController.java b/server/zhubo/src/main/java/com/doumee/api/business/WebParamController.java
index d26fdbb..ed185f7 100644
--- a/server/zhubo/src/main/java/com/doumee/api/business/WebParamController.java
+++ b/server/zhubo/src/main/java/com/doumee/api/business/WebParamController.java
@@ -49,4 +49,10 @@
         return ApiResponse.success("鎿嶄綔鎴愬姛");
     }
 
+    @ApiOperation("鑾峰彇涓绘挱绔〉闈㈤厤缃�")
+    @GetMapping("/getByLoginAnchor")
+    public ApiResponse<WebParam> getByLoginAnchor() {
+        return ApiResponse.success(webParamService.findOneAnchor());
+    }
+
 }

--
Gitblit v1.9.3