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"> </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