Compare commits
2 Commits
4aa20b7257
...
d1c0419d76
| Author | SHA1 | Date |
|---|---|---|
|
|
d1c0419d76 | |
|
|
acd448b6dd |
56
App.vue
56
App.vue
|
|
@ -1,13 +1,59 @@
|
||||||
<script>
|
<script>
|
||||||
|
import UrlQuery from '@/scratch/url-query.js';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
onLaunch: function() {
|
globalData: {
|
||||||
console.log('App Launch')
|
/**
|
||||||
|
* urlParams - 当前页面 URL 参数集合
|
||||||
|
*
|
||||||
|
* 来源:
|
||||||
|
* 1. H5 环境下从 window.location(search + hash)解析
|
||||||
|
* 2. 小程序/App 环境下从页面栈 currentPage.options 获取
|
||||||
|
* 3. 分享/扫码进入时从 App.onLaunch/onShow 的 options.query 合并
|
||||||
|
*
|
||||||
|
* 目前业务中实际使用的参数:
|
||||||
|
* - token {string} 用户登录凭证
|
||||||
|
* - code {string} 淘宝后返回授权码
|
||||||
|
* - state {string} 状态值,格式通常为 "uidxxx",用于提取用户 ID(淘宝授权返回)
|
||||||
|
* - exuid {string} 推广人/邀请人用户 ID(三方进入用户标识)
|
||||||
|
* - page_uri {string} 授权成功后需要跳转的目标页面路径(必须授权过的用户该参数才有效)
|
||||||
|
*
|
||||||
|
* 注:UrlQuery 支持获取任意 URL 参数,以上仅为业务代码中显式消费的字段
|
||||||
|
*/
|
||||||
|
urlParams: {}
|
||||||
},
|
},
|
||||||
onShow: function() {
|
onLaunch: function(options) {
|
||||||
console.log('App Show')
|
console.log('App Launch');
|
||||||
|
|
||||||
|
// 获取 URL 参数并存入 globalData
|
||||||
|
let params = UrlQuery.getAll();
|
||||||
|
|
||||||
|
// #ifndef H5
|
||||||
|
// 非 H5 平台,合并 App.onLaunch 的 options.query(分享/扫码进入时的参数)
|
||||||
|
if (options && options.query && typeof options.query === 'object') {
|
||||||
|
params = { ...params, ...options.query };
|
||||||
|
}
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
this.globalData.urlParams = params;
|
||||||
|
console.log('URL 参数:', params);
|
||||||
|
},
|
||||||
|
onShow: function(options) {
|
||||||
|
console.log('App Show');
|
||||||
|
|
||||||
|
// 每次显示时更新参数(H5 下 URL 可能变化)
|
||||||
|
let params = UrlQuery.getAll();
|
||||||
|
|
||||||
|
// #ifndef H5
|
||||||
|
if (options && options.query && typeof options.query === 'object') {
|
||||||
|
params = { ...params, ...options.query };
|
||||||
|
}
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
this.globalData.urlParams = params;
|
||||||
},
|
},
|
||||||
onHide: function() {
|
onHide: function() {
|
||||||
console.log('App Hide')
|
console.log('App Hide');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,8 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import http from '@/request/request.js';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
activeTab: {
|
activeTab: {
|
||||||
|
|
@ -25,17 +27,14 @@
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
getBottomBar() {
|
getBottomBar() {
|
||||||
uni.request({
|
http.get('https://api.cmspro.haodanku.com/bottomBar/lists?cid=YsWZ21tx').then(res => {
|
||||||
url: 'https://api.cmspro.haodanku.com/bottomBar/lists?cid=YsWZ21tx',
|
if (res.data && res.data.bottom_bar) {
|
||||||
success: (res) => {
|
|
||||||
if (res.data && res.data.code === 200 && res.data.data.bottom_bar) {
|
|
||||||
// 过滤并排序:剔除“发现”,仅保留首页、榜单、分类
|
// 过滤并排序:剔除“发现”,仅保留首页、榜单、分类
|
||||||
const allowedTitles = ['首页', '榜单', '分类'];
|
const allowedTitles = ['首页', '榜单', '分类'];
|
||||||
this.bottomBarList = res.data.data.bottom_bar
|
this.bottomBarList = res.data.bottom_bar
|
||||||
.filter(item => allowedTitles.includes(item.title))
|
.filter(item => allowedTitles.includes(item.title))
|
||||||
.sort((a, b) => b.sort - a.sort);
|
.sort((a, b) => b.sort - a.sort);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
handleTabClick(tab, index) {
|
handleTabClick(tab, index) {
|
||||||
|
|
|
||||||
6
main.js
6
main.js
|
|
@ -1,9 +1,10 @@
|
||||||
import App from './App'
|
import App from './App'
|
||||||
|
import { estimateCoupon } from './utils/index.js'
|
||||||
// #ifndef VUE3
|
// #ifndef VUE3
|
||||||
import Vue from 'vue'
|
import Vue from 'vue'
|
||||||
import './uni.promisify.adaptor'
|
import './uni.promisify.adaptor'
|
||||||
Vue.config.productionTip = false
|
Vue.config.productionTip = false
|
||||||
|
Vue.prototype.$estimateCoupon = estimateCoupon;
|
||||||
App.mpType = 'app'
|
App.mpType = 'app'
|
||||||
const app = new Vue({
|
const app = new Vue({
|
||||||
...App
|
...App
|
||||||
|
|
@ -13,8 +14,11 @@ app.$mount()
|
||||||
|
|
||||||
// #ifdef VUE3
|
// #ifdef VUE3
|
||||||
import { createSSRApp } from 'vue'
|
import { createSSRApp } from 'vue'
|
||||||
|
import store from './store'
|
||||||
export function createApp() {
|
export function createApp() {
|
||||||
const app = createSSRApp(App)
|
const app = createSSRApp(App)
|
||||||
|
app.use(store)
|
||||||
|
app.config.globalProperties.$estimateCoupon = estimateCoupon;
|
||||||
return {
|
return {
|
||||||
app
|
app
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name" : "baimacms",
|
"name" : "baimacms",
|
||||||
"appid" : "__UNI__404B6E1",
|
"appid" : "__UNI__924A228",
|
||||||
"description" : "",
|
"description" : "",
|
||||||
"versionName" : "1.0.0",
|
"versionName" : "1.0.0",
|
||||||
"versionCode" : "100",
|
"versionCode" : "100",
|
||||||
|
|
@ -68,5 +68,11 @@
|
||||||
"uniStatistics" : {
|
"uniStatistics" : {
|
||||||
"enable" : false
|
"enable" : false
|
||||||
},
|
},
|
||||||
"vueVersion" : "3"
|
"vueVersion" : "3",
|
||||||
|
"h5" : {
|
||||||
|
"router" : {
|
||||||
|
"mode" : "history",
|
||||||
|
"base" : "/affiliate-activity/"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,331 @@
|
||||||
|
{
|
||||||
|
"name": "baimacms",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"lockfileVersion": 3,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"": {
|
||||||
|
"name": "baimacms",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"@dcloudio/uni-ui": "^1.5.12",
|
||||||
|
"vuex": "^4.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@babel/helper-string-parser": {
|
||||||
|
"version": "7.27.1",
|
||||||
|
"resolved": "https://registry.npmmirror.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
|
||||||
|
"integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@babel/helper-validator-identifier": {
|
||||||
|
"version": "7.28.5",
|
||||||
|
"resolved": "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz",
|
||||||
|
"integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@babel/parser": {
|
||||||
|
"version": "7.29.3",
|
||||||
|
"resolved": "https://registry.npmmirror.com/@babel/parser/-/parser-7.29.3.tgz",
|
||||||
|
"integrity": "sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/types": "^7.29.0"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"parser": "bin/babel-parser.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@babel/types": {
|
||||||
|
"version": "7.29.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/@babel/types/-/types-7.29.0.tgz",
|
||||||
|
"integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/helper-string-parser": "^7.27.1",
|
||||||
|
"@babel/helper-validator-identifier": "^7.28.5"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@dcloudio/uni-ui": {
|
||||||
|
"version": "1.5.12",
|
||||||
|
"resolved": "https://registry.npmmirror.com/@dcloudio/uni-ui/-/uni-ui-1.5.12.tgz",
|
||||||
|
"integrity": "sha512-mGDl2OZSz7D8xcUAzJegWDHOqB4MEFBSW9Esb/oJiu2/3Gk9+P/Z4bA4JZ9jv9VWBYbMrYwaTfK1Z728kABdYg==",
|
||||||
|
"license": "Apache-2.0"
|
||||||
|
},
|
||||||
|
"node_modules/@jridgewell/sourcemap-codec": {
|
||||||
|
"version": "1.5.5",
|
||||||
|
"resolved": "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
|
||||||
|
"integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true
|
||||||
|
},
|
||||||
|
"node_modules/@vue/compiler-core": {
|
||||||
|
"version": "3.5.34",
|
||||||
|
"resolved": "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.5.34.tgz",
|
||||||
|
"integrity": "sha512-s9cLyK5mLcvZ4Agva5QgRsQyLKvts9WbU9DB6NqiZkkGEdwmcEiylj5Jbwkp680drF/NNCV8OlAJSe+yMLxaJw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/parser": "^7.29.3",
|
||||||
|
"@vue/shared": "3.5.34",
|
||||||
|
"entities": "^7.0.1",
|
||||||
|
"estree-walker": "^2.0.2",
|
||||||
|
"source-map-js": "^1.2.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@vue/compiler-dom": {
|
||||||
|
"version": "3.5.34",
|
||||||
|
"resolved": "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.5.34.tgz",
|
||||||
|
"integrity": "sha512-EbF/T++k0e2MMZlJsBhzK8Sgwt0HcIPOhzn1CTB/lv6sQcyk+OWf8YeiLxZp3ro7MbbLcAfAJ6sEvjFWuNgUCw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@vue/compiler-core": "3.5.34",
|
||||||
|
"@vue/shared": "3.5.34"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@vue/compiler-sfc": {
|
||||||
|
"version": "3.5.34",
|
||||||
|
"resolved": "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.5.34.tgz",
|
||||||
|
"integrity": "sha512-D/ihr6uZeIt6r+pVZf46RWT1fAsLFMbUP7k8G1VkiiWexriED9GrX3echHd4Abbt17zjlfiFJ8z7a3BxZOPNjg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/parser": "^7.29.3",
|
||||||
|
"@vue/compiler-core": "3.5.34",
|
||||||
|
"@vue/compiler-dom": "3.5.34",
|
||||||
|
"@vue/compiler-ssr": "3.5.34",
|
||||||
|
"@vue/shared": "3.5.34",
|
||||||
|
"estree-walker": "^2.0.2",
|
||||||
|
"magic-string": "^0.30.21",
|
||||||
|
"postcss": "^8.5.14",
|
||||||
|
"source-map-js": "^1.2.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@vue/compiler-ssr": {
|
||||||
|
"version": "3.5.34",
|
||||||
|
"resolved": "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.5.34.tgz",
|
||||||
|
"integrity": "sha512-cDtTHKibkThKGHH1SP+WdccquNRYQDFH6rRjQCqT9G2ltFAfoR5pUftpab/z+aM5mW9HLLVQW7hfKKQe/1GBeQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@vue/compiler-dom": "3.5.34",
|
||||||
|
"@vue/shared": "3.5.34"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@vue/devtools-api": {
|
||||||
|
"version": "6.6.4",
|
||||||
|
"resolved": "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-6.6.4.tgz",
|
||||||
|
"integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/@vue/reactivity": {
|
||||||
|
"version": "3.5.34",
|
||||||
|
"resolved": "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.5.34.tgz",
|
||||||
|
"integrity": "sha512-y9XDjCEuBp+98k+UL5dbYkh57AHU4o6cxZedOPXw3bmrZZYLQsVHguGurq7hVrPCSrQtrnz1f9dssyFr+dMXfQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@vue/shared": "3.5.34"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@vue/runtime-core": {
|
||||||
|
"version": "3.5.34",
|
||||||
|
"resolved": "https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.5.34.tgz",
|
||||||
|
"integrity": "sha512-mKeBYvu8tcMSLhypAHBmriUFfWXKTCF/23Z4jiCoYK3UtWepkliViNLuR90V9XOyD62mUxs9p1jsrpK3CCGIzw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@vue/reactivity": "3.5.34",
|
||||||
|
"@vue/shared": "3.5.34"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@vue/runtime-dom": {
|
||||||
|
"version": "3.5.34",
|
||||||
|
"resolved": "https://registry.npmmirror.com/@vue/runtime-dom/-/runtime-dom-3.5.34.tgz",
|
||||||
|
"integrity": "sha512-e8kZzERmCwUnBRVsgSQlAfrfU2rGoy0FFKPBXSlfEjc/O3KfA7QP0t1/2ZylrbchjmIKB4dPTd07A6WPr0eOrg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@vue/reactivity": "3.5.34",
|
||||||
|
"@vue/runtime-core": "3.5.34",
|
||||||
|
"@vue/shared": "3.5.34",
|
||||||
|
"csstype": "^3.2.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@vue/server-renderer": {
|
||||||
|
"version": "3.5.34",
|
||||||
|
"resolved": "https://registry.npmmirror.com/@vue/server-renderer/-/server-renderer-3.5.34.tgz",
|
||||||
|
"integrity": "sha512-nHxmJoTrKsmrkbILRhkC9gY1G3moZbJTqCzDd7DOOzG5KH9oeJ0Unqrff5f9v0pW//jES05ZkJcNtfE8JjOIew==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@vue/compiler-ssr": "3.5.34",
|
||||||
|
"@vue/shared": "3.5.34"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"vue": "3.5.34"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@vue/shared": {
|
||||||
|
"version": "3.5.34",
|
||||||
|
"resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.5.34.tgz",
|
||||||
|
"integrity": "sha512-24uqU4OIiX29ryC3MeWid/Xf2fa2EFRUVLb77nRhk+UrTVrh/XiGtFAFmJBAtBRbjwNdsPRP+jj/OL27Eg1NDA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true
|
||||||
|
},
|
||||||
|
"node_modules/csstype": {
|
||||||
|
"version": "3.2.3",
|
||||||
|
"resolved": "https://registry.npmmirror.com/csstype/-/csstype-3.2.3.tgz",
|
||||||
|
"integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true
|
||||||
|
},
|
||||||
|
"node_modules/entities": {
|
||||||
|
"version": "7.0.1",
|
||||||
|
"resolved": "https://registry.npmmirror.com/entities/-/entities-7.0.1.tgz",
|
||||||
|
"integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==",
|
||||||
|
"license": "BSD-2-Clause",
|
||||||
|
"peer": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/fb55/entities?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/estree-walker": {
|
||||||
|
"version": "2.0.2",
|
||||||
|
"resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz",
|
||||||
|
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true
|
||||||
|
},
|
||||||
|
"node_modules/magic-string": {
|
||||||
|
"version": "0.30.21",
|
||||||
|
"resolved": "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.21.tgz",
|
||||||
|
"integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@jridgewell/sourcemap-codec": "^1.5.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/nanoid": {
|
||||||
|
"version": "3.3.12",
|
||||||
|
"resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.12.tgz",
|
||||||
|
"integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/ai"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
|
"bin": {
|
||||||
|
"nanoid": "bin/nanoid.cjs"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/picocolors": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
|
||||||
|
"license": "ISC",
|
||||||
|
"peer": true
|
||||||
|
},
|
||||||
|
"node_modules/postcss": {
|
||||||
|
"version": "8.5.14",
|
||||||
|
"resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.5.14.tgz",
|
||||||
|
"integrity": "sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/postcss/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "tidelift",
|
||||||
|
"url": "https://tidelift.com/funding/github/npm/postcss"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/ai"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
|
"dependencies": {
|
||||||
|
"nanoid": "^3.3.11",
|
||||||
|
"picocolors": "^1.1.1",
|
||||||
|
"source-map-js": "^1.2.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^10 || ^12 || >=14"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/source-map-js": {
|
||||||
|
"version": "1.2.1",
|
||||||
|
"resolved": "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.1.tgz",
|
||||||
|
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
|
||||||
|
"license": "BSD-3-Clause",
|
||||||
|
"peer": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/vue": {
|
||||||
|
"version": "3.5.34",
|
||||||
|
"resolved": "https://registry.npmmirror.com/vue/-/vue-3.5.34.tgz",
|
||||||
|
"integrity": "sha512-WdLBG9gm02OgJIG9axd5Hpx0TFLdzVgfG2evFFu8Rur5O/IoGc5cMjnjh3tPL6GnRGsYvUhBSKVPYVcxRKpMCA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@vue/compiler-dom": "3.5.34",
|
||||||
|
"@vue/compiler-sfc": "3.5.34",
|
||||||
|
"@vue/runtime-dom": "3.5.34",
|
||||||
|
"@vue/server-renderer": "3.5.34",
|
||||||
|
"@vue/shared": "3.5.34"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"typescript": "*"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"typescript": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/vuex": {
|
||||||
|
"version": "4.1.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/vuex/-/vuex-4.1.0.tgz",
|
||||||
|
"integrity": "sha512-hmV6UerDrPcgbSy9ORAtNXDr9M4wlNP4pEFKye4ujJF8oqgFFuxDCdOLS3eNoRTtq5O3hoBDh9Doj1bQMYHRbQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@vue/devtools-api": "^6.0.0-beta.11"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"vue": "^3.2.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
{
|
||||||
|
"name": "baimacms",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "main.js",
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://gitea.agrimedia.cn/15133400227/baimacms.git"
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"type": "commonjs",
|
||||||
|
"dependencies": {
|
||||||
|
"@dcloudio/uni-ui": "^1.5.12",
|
||||||
|
"vuex": "^4.1.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
51
pages.json
51
pages.json
|
|
@ -1,63 +1,94 @@
|
||||||
{
|
{
|
||||||
|
"easycom": {
|
||||||
|
"autoscan": true,
|
||||||
|
"custom": {
|
||||||
|
"^uni-(.*)": "@dcloudio/uni-ui/lib/uni-$1/uni-$1.vue"
|
||||||
|
}
|
||||||
|
},
|
||||||
"pages": [
|
"pages": [
|
||||||
{
|
{
|
||||||
"path": "pages/index/index",
|
"path": "pages/index/index",
|
||||||
"style": {
|
"style": {
|
||||||
"navigationStyle": "custom"
|
"navigationStyle": "custom",
|
||||||
|
"navigationBarTitleText": "首页",
|
||||||
|
"enablePullDownRefresh": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "pages/detail/detail",
|
"path": "pages/detail/detail",
|
||||||
"style": {
|
"style": {
|
||||||
"navigationStyle": "custom"
|
"navigationStyle": "custom",
|
||||||
|
"navigationBarTitleText": "商品详情"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "pages/category/category",
|
"path": "pages/category/category",
|
||||||
"style": {
|
"style": {
|
||||||
"navigationStyle": "custom"
|
"navigationStyle": "custom",
|
||||||
|
"navigationBarTitleText": "商品分类"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "pages/category/category_detail",
|
"path": "pages/category/category_detail",
|
||||||
"style": {
|
"style": {
|
||||||
"navigationStyle": "custom"
|
"navigationStyle": "custom",
|
||||||
|
"navigationBarTitleText": "商品分类详情"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "pages/rank/rank",
|
"path": "pages/rank/rank",
|
||||||
"style": {
|
"style": {
|
||||||
"navigationStyle": "custom"
|
"navigationStyle": "custom",
|
||||||
|
"navigationBarTitleText": "商品排名"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "pages/classify/classify",
|
"path": "pages/classify/classify",
|
||||||
"style": {
|
"style": {
|
||||||
"navigationStyle": "custom"
|
"navigationStyle": "custom",
|
||||||
|
"navigationBarTitleText": "商品分类列表"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "pages/search/search",
|
"path": "pages/search/search",
|
||||||
"style": {
|
"style": {
|
||||||
"navigationStyle": "custom"
|
"navigationStyle": "custom",
|
||||||
|
"navigationBarTitleText": "搜索"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "pages/special-sale/choicen",
|
"path": "pages/special-sale/choicen",
|
||||||
"style": {
|
"style": {
|
||||||
"navigationStyle": "custom"
|
"navigationStyle": "custom",
|
||||||
|
"navigationBarTitleText": "特殊销售"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "pages/special-sale/details",
|
"path": "pages/special-sale/details",
|
||||||
"style": {
|
"style": {
|
||||||
"navigationStyle": "custom"
|
"navigationStyle": "custom",
|
||||||
|
"navigationBarTitleText": "特殊销售详情"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "pages/save-money/save-money",
|
"path": "pages/save-money/save-money",
|
||||||
"style": {
|
"style": {
|
||||||
"navigationStyle": "custom"
|
"navigationStyle": "custom",
|
||||||
|
"navigationBarTitleText": "省钱券"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/auth/auth",
|
||||||
|
"style": {
|
||||||
|
"navigationStyle": "custom",
|
||||||
|
"navigationBarTitleText": "授权"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/activity/activity",
|
||||||
|
"style": {
|
||||||
|
"navigationStyle": "custom",
|
||||||
|
"enablePullDownRefresh": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,634 @@
|
||||||
|
<template>
|
||||||
|
<view class="activity-page">
|
||||||
|
<!-- Banner -->
|
||||||
|
<view class="banner-wrapper">
|
||||||
|
<image class="banner-img" :src="banner" mode="widthFix" />
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 分类 Tabs -->
|
||||||
|
<view class="category-sticky" v-if="category.list.length > 1">
|
||||||
|
<scroll-view scroll-x class="category-scroll" :show-scrollbar="false">
|
||||||
|
<view class="category-tabs">
|
||||||
|
<view v-for="item in category.list" :key="item.value" class="tab-item"
|
||||||
|
:class="{ active: category.value === item.value }" @click="onNavChange(item.value)">
|
||||||
|
{{ item.label }}
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</scroll-view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 商品列表 -->
|
||||||
|
<view class="goods-list" :class="{ 'empty-list': !listData.list.length }">
|
||||||
|
<view class="goods-card" v-for="(item, index) in listData.list" :key="item.itemid"
|
||||||
|
@click="handleGoodsTransfer(item)">
|
||||||
|
<view class="cover img-box">
|
||||||
|
<image class="goods-img" :src="item.itempic" mode="aspectFill" lazy-load v-if="item.itempic" />
|
||||||
|
<image class="goods-img" src="http://img.bc.fqapps.com/fudai13cae4ae6ef16739ed3b100a2ec39e97.gif"
|
||||||
|
mode="aspectFill" v-else />
|
||||||
|
<view class="sale-tag">
|
||||||
|
<text class="fire-icon">🔥</text>
|
||||||
|
<text>日销量 {{ getMoneyStr(item.itemsale2) }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="detail">
|
||||||
|
<view class="title">
|
||||||
|
<image v-if="item.shoptype === 'B'" class="shop-icon tm"
|
||||||
|
src="http://img-haodanku-com.cdn.fudaiapp.com/FvU9Lg74-WgBzWQphv98-9KgYF6d" />
|
||||||
|
<image v-else-if="item.shoptype === 'C'" class="shop-icon tb"
|
||||||
|
src="http://img-haodanku-com.cdn.fudaiapp.com/FnCy1eY4W5khYdTw_iIDhKuwGLmu" />
|
||||||
|
<text class="title-text">{{ item.itemshorttitle || item.itemtitle }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="center">
|
||||||
|
<text>{{ item.itemdesc }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="price">
|
||||||
|
<view class="end-price">
|
||||||
|
券后价<text class="price-strong">¥{{ Number(item.itemendprice) }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="origin">
|
||||||
|
<text class="del-price">¥{{ Number(item.itemprice) }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="tags" v-if="item.couponmoney != '0'">
|
||||||
|
<view class="ticket">
|
||||||
|
<text class="name">券</text><text class="value">¥{{ item.couponmoney }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="btn-block">
|
||||||
|
<view class="btn-order" @click.stop="handleGoodsTransfer(item)">
|
||||||
|
<text>立即购买</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 加载提示 -->
|
||||||
|
<view class="list-loading" v-if="loading && !listData.list.length">数据正在赶来的路上...</view>
|
||||||
|
<view class="list-finished" v-if="listData.finished && listData.list.length">-优惠到底啦-</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 分享按钮 -->
|
||||||
|
<view class="share-btn" :class="{ hide: isScrollToDown }" @click="handleShare">
|
||||||
|
<view class="share-icon">
|
||||||
|
<svg viewBox="0 0 18 19.11" class="svg-share">
|
||||||
|
<path class="cls-1"
|
||||||
|
d="M17.5,8A2.5,2.5,0,1,0,15,5.5,2.5,2.5,0,0,0,17.5,8Z" transform="translate(-3 -2)" />
|
||||||
|
<path class="cls-1"
|
||||||
|
d="M6.5,14.5A2.5,2.5,0,1,0,4,12,2.5,2.5,0,0,0,6.5,14.5Z" transform="translate(-3 -2)" />
|
||||||
|
<path class="cls-2" d="M15,6.79,8.67,10.62" transform="translate(-3 -2)" />
|
||||||
|
<path class="cls-2" d="M8.67,13.28,13.27,16" transform="translate(-3 -2)" />
|
||||||
|
<path class="cls-1"
|
||||||
|
d="M15.77,15.11a2.5,2.5,0,1,1-2.5,2.5A2.5,2.5,0,0,1,15.77,15.11Z" transform="translate(-3 -2)" />
|
||||||
|
</svg>
|
||||||
|
</view>
|
||||||
|
<text>分享好友</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 回到顶部 -->
|
||||||
|
<view class="back-top-btn" :class="{ hide: scrollTop < 600 }" @click="backTop"></view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import http from '@/request/request.js';
|
||||||
|
const API_BASE = 'https://v2.api.haodanku.com';
|
||||||
|
const CUSTOM_PARAMS = {
|
||||||
|
apikey: '5417B681C5EA'
|
||||||
|
};
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
banner: '',
|
||||||
|
loading: false,
|
||||||
|
scrollTop: 0,
|
||||||
|
lastScrollTop: 0,
|
||||||
|
isScrollToDown: false,
|
||||||
|
category: {
|
||||||
|
value: '0',
|
||||||
|
list: [],
|
||||||
|
goodsLists: []
|
||||||
|
},
|
||||||
|
listData: {
|
||||||
|
list: [],
|
||||||
|
loading: false,
|
||||||
|
finished: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onLoad(options) {
|
||||||
|
const stamp = Date.now() - Date.now() % (60 * 1000 * 30);
|
||||||
|
this.banner = 'https://img.bc.haodanku.com/cms/1635994050?t=' + stamp;
|
||||||
|
this.getListData();
|
||||||
|
},
|
||||||
|
onPageScroll(e) {
|
||||||
|
const st = e.scrollTop;
|
||||||
|
this.isScrollToDown = st > this.lastScrollTop;
|
||||||
|
this.lastScrollTop = st;
|
||||||
|
this.scrollTop = st;
|
||||||
|
},
|
||||||
|
onPullDownRefresh() {
|
||||||
|
this.reGetListData().finally(() => {
|
||||||
|
uni.stopPullDownRefresh();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getListDataParams() {
|
||||||
|
return { ...CUSTOM_PARAMS };
|
||||||
|
},
|
||||||
|
getTransferParams() {
|
||||||
|
const rid = this.$store.getters.relationId;
|
||||||
|
return {
|
||||||
|
pid: 'mm_284380119_1881450385_111415850448',
|
||||||
|
tb_name: 'michuan2018',
|
||||||
|
relation_id: rid || '-1',
|
||||||
|
...CUSTOM_PARAMS
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
async getListData() {
|
||||||
|
const listData = this.listData;
|
||||||
|
if (this.loading || listData.loading || listData.finished) return;
|
||||||
|
listData.loading = true;
|
||||||
|
this.loading = true;
|
||||||
|
|
||||||
|
const params = {
|
||||||
|
id: 125,
|
||||||
|
...this.getListDataParams()
|
||||||
|
};
|
||||||
|
|
||||||
|
http.get(`${API_BASE}/get_index_activity_items`, params)
|
||||||
|
.then(res => {
|
||||||
|
const body = res.body || {};
|
||||||
|
const block = body.data ? (body.data.block || []) : (body.block || []);
|
||||||
|
this.category.list = block.map((item, index) => ({
|
||||||
|
label: item.name,
|
||||||
|
value: String(index)
|
||||||
|
}));
|
||||||
|
this.category.goodsLists = block.map(item => item.item || []);
|
||||||
|
const list = this.category.goodsLists[0] || [];
|
||||||
|
this.listData.list = list;
|
||||||
|
this.listData.finished = true;
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
this.listData.list = [];
|
||||||
|
this.listData.finished = true;
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
setTimeout(() => {
|
||||||
|
this.listData.loading = false;
|
||||||
|
this.loading = false;
|
||||||
|
}, 200);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
reGetListData() {
|
||||||
|
this.listData.list = [];
|
||||||
|
this.listData.finished = false;
|
||||||
|
return this.getListData();
|
||||||
|
},
|
||||||
|
|
||||||
|
onNavChange(value) {
|
||||||
|
this.category.value = value;
|
||||||
|
this.listData.list = this.category.goodsLists[value] || [];
|
||||||
|
},
|
||||||
|
|
||||||
|
backTop() {
|
||||||
|
uni.pageScrollTo({
|
||||||
|
scrollTop: 0,
|
||||||
|
duration: 300
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
handleShare() {
|
||||||
|
let shareUrl;
|
||||||
|
// #ifdef H5
|
||||||
|
shareUrl = window.location.href;
|
||||||
|
// #endif
|
||||||
|
// #ifndef H5
|
||||||
|
shareUrl = 'https://your-domain.com/pages/activity/activity';
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
uni.setClipboardData({
|
||||||
|
data: shareUrl,
|
||||||
|
success: () => {
|
||||||
|
uni.showToast({
|
||||||
|
title: '复制成功,分享给身边好友吧~',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
async handleGoodsTransfer(item) {
|
||||||
|
const param = {
|
||||||
|
itemid: item.itemid,
|
||||||
|
title: item.itemshorttitle || item.itemtitle,
|
||||||
|
get_taoword: 1,
|
||||||
|
...this.getTransferParams()
|
||||||
|
};
|
||||||
|
|
||||||
|
this.loading = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
let isWechatEnv;
|
||||||
|
// #ifdef H5
|
||||||
|
isWechatEnv = /micromessenger/i.test(navigator.userAgent);
|
||||||
|
// #endif
|
||||||
|
// #ifndef H5
|
||||||
|
isWechatEnv = false;
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
if (!isWechatEnv) {
|
||||||
|
// 非微信环境:弹窗确认后跳转
|
||||||
|
http.post(`${API_BASE}/ratesurl`, param, {
|
||||||
|
header: { 'Content-Type': 'application/x-www-form-urlencoded' }
|
||||||
|
}).then(res => {
|
||||||
|
const body = res.body || {};
|
||||||
|
const data = body.data || body;
|
||||||
|
const jumpUrl = data.coupon_click_url || data.item_url;
|
||||||
|
if (jumpUrl) {
|
||||||
|
uni.showModal({
|
||||||
|
title: '提示',
|
||||||
|
content: '是否跳转到下单页面?',
|
||||||
|
success: (modalRes) => {
|
||||||
|
if (modalRes.confirm) {
|
||||||
|
// #ifdef H5
|
||||||
|
window.location.href = jumpUrl;
|
||||||
|
// #endif
|
||||||
|
// #ifdef APP-PLUS
|
||||||
|
plus.runtime.openURL(jumpUrl);
|
||||||
|
// #endif
|
||||||
|
// #ifndef H5 || APP-PLUS
|
||||||
|
uni.setClipboardData({
|
||||||
|
data: jumpUrl,
|
||||||
|
success: () => {
|
||||||
|
uni.showToast({
|
||||||
|
title: '链接已复制,请在浏览器中打开',
|
||||||
|
icon: 'none',
|
||||||
|
duration: 3000
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// #endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}).catch(() => {
|
||||||
|
uni.showToast({ title: '转链失败', icon: 'none' });
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// 微信环境:获取淘口令并复制
|
||||||
|
http.post(`${API_BASE}/ratesurl`, param, {
|
||||||
|
header: { 'Content-Type': 'application/x-www-form-urlencoded' }
|
||||||
|
}).then(res => {
|
||||||
|
const body = res.body || {};
|
||||||
|
const data = body.data || body;
|
||||||
|
const taoword = data.taoword;
|
||||||
|
if (taoword) {
|
||||||
|
const taocode = '0' + taoword + '/';
|
||||||
|
uni.setClipboardData({
|
||||||
|
data: taocode,
|
||||||
|
success: () => {
|
||||||
|
uni.showToast({
|
||||||
|
title: '复制口令成功,请打开淘宝领取',
|
||||||
|
icon: 'none',
|
||||||
|
duration: 2000
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
uni.showToast({ title: '转链失败', icon: 'none' });
|
||||||
|
}
|
||||||
|
}).catch(() => {
|
||||||
|
uni.showToast({ title: '转链失败', icon: 'none' });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
uni.showToast({
|
||||||
|
title: '转链失败',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
setTimeout(() => {
|
||||||
|
this.loading = false;
|
||||||
|
}, 200);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
getMoneyStr(num) {
|
||||||
|
const n = Number(num);
|
||||||
|
const w = 10000;
|
||||||
|
if (n < w) return String(n);
|
||||||
|
return (n / w).toFixed(2) + '万';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.activity-page {
|
||||||
|
background: #f5f5f5;
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.banner-wrapper {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.banner-img {
|
||||||
|
width: 100%;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-sticky {
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
z-index: 100;
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-scroll {
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-tabs {
|
||||||
|
display: flex;
|
||||||
|
padding: 0 20rpx;
|
||||||
|
border-bottom: 1rpx solid #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-item {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 24rpx 30rpx;
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #666;
|
||||||
|
position: relative;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-item.active {
|
||||||
|
color: #ff416c;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-item.active::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
width: 40rpx;
|
||||||
|
height: 4rpx;
|
||||||
|
background: #ff416c;
|
||||||
|
border-radius: 2rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.goods-list {
|
||||||
|
padding: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.goods-list.empty-list {
|
||||||
|
min-height: 400rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.goods-card {
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 16rpx;
|
||||||
|
margin-bottom: 20rpx;
|
||||||
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
padding: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cover {
|
||||||
|
position: relative;
|
||||||
|
width: 240rpx;
|
||||||
|
height: 240rpx;
|
||||||
|
flex-shrink: 0;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
overflow: hidden;
|
||||||
|
background: #f9f9f9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.goods-img {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sale-tag {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
background: linear-gradient(to right, rgba(255, 65, 108, 0.9), rgba(255, 75, 43, 0.9));
|
||||||
|
color: #fff;
|
||||||
|
font-size: 20rpx;
|
||||||
|
padding: 6rpx 12rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fire-icon {
|
||||||
|
margin-right: 4rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail {
|
||||||
|
flex: 1;
|
||||||
|
margin-left: 20rpx;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #333;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shop-icon {
|
||||||
|
width: 36rpx;
|
||||||
|
height: 36rpx;
|
||||||
|
margin-right: 8rpx;
|
||||||
|
flex-shrink: 0;
|
||||||
|
margin-top: 2rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title-text {
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-line-clamp: 2;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
}
|
||||||
|
|
||||||
|
.center {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #999;
|
||||||
|
margin-top: 8rpx;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-line-clamp: 1;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
}
|
||||||
|
|
||||||
|
.price {
|
||||||
|
display: flex;
|
||||||
|
align-items: baseline;
|
||||||
|
margin-top: 8rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.end-price {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #ff416c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.price-strong {
|
||||||
|
font-size: 36rpx;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-left: 4rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.origin {
|
||||||
|
margin-left: 12rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.del-price {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #999;
|
||||||
|
text-decoration: line-through;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tags {
|
||||||
|
margin-top: 8rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ticket {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
border: 1rpx solid #ff416c;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
overflow: hidden;
|
||||||
|
font-size: 22rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ticket .name {
|
||||||
|
background: linear-gradient(to right, #ff715a, #ff416c);
|
||||||
|
color: #fff;
|
||||||
|
padding: 2rpx 10rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ticket .value {
|
||||||
|
color: #ff416c;
|
||||||
|
padding: 2rpx 10rpx;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-block {
|
||||||
|
margin-top: 12rpx;
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-order {
|
||||||
|
background: linear-gradient(to right, #ff715a, #ff416c);
|
||||||
|
color: #fff;
|
||||||
|
font-size: 26rpx;
|
||||||
|
padding: 12rpx 32rpx;
|
||||||
|
border-radius: 30rpx;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-loading,
|
||||||
|
.list-finished {
|
||||||
|
text-align: center;
|
||||||
|
padding: 30rpx;
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.share-btn {
|
||||||
|
position: fixed;
|
||||||
|
right: 30rpx;
|
||||||
|
bottom: 160rpx;
|
||||||
|
z-index: 200;
|
||||||
|
background: rgba(255, 65, 108, 0.9);
|
||||||
|
color: #fff;
|
||||||
|
border-radius: 40rpx;
|
||||||
|
padding: 16rpx 24rpx;
|
||||||
|
font-size: 24rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
box-shadow: 0 4rpx 16rpx rgba(255, 65, 108, 0.3);
|
||||||
|
transition: transform 0.3s, opacity 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.share-btn.hide {
|
||||||
|
transform: translateX(120%);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.share-icon {
|
||||||
|
width: 32rpx;
|
||||||
|
height: 32rpx;
|
||||||
|
margin-right: 8rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.svg-share {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.svg-share .cls-1 {
|
||||||
|
fill: none;
|
||||||
|
stroke: #fff;
|
||||||
|
stroke-linecap: round;
|
||||||
|
stroke-linejoin: round;
|
||||||
|
stroke-width: 1.5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.svg-share .cls-2 {
|
||||||
|
fill: none;
|
||||||
|
stroke: #fff;
|
||||||
|
stroke-linecap: round;
|
||||||
|
stroke-linejoin: round;
|
||||||
|
stroke-width: 1.5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.back-top-btn {
|
||||||
|
position: fixed;
|
||||||
|
right: 30rpx;
|
||||||
|
bottom: 80rpx;
|
||||||
|
z-index: 200;
|
||||||
|
width: 80rpx;
|
||||||
|
height: 80rpx;
|
||||||
|
background: rgba(0, 0, 0, 0.5);
|
||||||
|
border-radius: 50%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
transition: transform 0.3s, opacity 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.back-top-btn::before {
|
||||||
|
content: '';
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
border-left: 16rpx solid transparent;
|
||||||
|
border-right: 16rpx solid transparent;
|
||||||
|
border-bottom: 20rpx solid #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.back-top-btn.hide {
|
||||||
|
transform: translateY(200%);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,379 @@
|
||||||
|
<template>
|
||||||
|
<view class="auth-page">
|
||||||
|
<view class="auth-box">
|
||||||
|
<!-- 加载中 -->
|
||||||
|
<template v-if="status === 'loading'">
|
||||||
|
<view class="auth-icon">
|
||||||
|
<uni-icons type="spinner-cycle" size="72" color="#EC0208" class="spin-animation" />
|
||||||
|
</view>
|
||||||
|
<view class="auth-title">{{ title }}</view>
|
||||||
|
<view class="auth-desc">{{ desc }}</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 成功 -->
|
||||||
|
<template v-if="status === 'success'">
|
||||||
|
<view class="auth-icon">
|
||||||
|
<uni-icons type="checkmarkempty" size="72" color="#07c160" />
|
||||||
|
</view>
|
||||||
|
<view class="auth-title">{{ title }}</view>
|
||||||
|
<view class="auth-desc">{{ desc }}</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 失败 -->
|
||||||
|
<template v-if="status === 'fail'">
|
||||||
|
<view class="auth-icon">
|
||||||
|
<uni-icons type="closeempty" size="72" color="#ee0a24" />
|
||||||
|
</view>
|
||||||
|
<view class="auth-title">{{ title }}</view>
|
||||||
|
<view class="auth-desc">{{ desc }}</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 复制链接 -->
|
||||||
|
<template v-if="status === 'CopyUrl'">
|
||||||
|
<view class="auth-icon">
|
||||||
|
<uni-icons type="link" size="72" color="#1989fa" />
|
||||||
|
</view>
|
||||||
|
<view class="auth-title">{{ title }}</view>
|
||||||
|
<view class="auth-desc">{{ desc }}</view>
|
||||||
|
<button class="auth-btn auth-btn--primary" v-if="CopyLink" @click="copyText">
|
||||||
|
<uni-icons type="compose" size="16" color="#ffffff" />
|
||||||
|
<text class="btn-text">复制授权地址</text>
|
||||||
|
</button>
|
||||||
|
</template>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { getEnvironment } from '@/utils/env.js';
|
||||||
|
import http from '@/request/request.js';
|
||||||
|
import pagesConfig from '@/pages.json';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
client_id: '30004725',
|
||||||
|
status: 'loading',
|
||||||
|
title: '正在完成授权...',
|
||||||
|
desc: '正在处理授权逻辑...',
|
||||||
|
CopyLink: false,
|
||||||
|
Token: '',
|
||||||
|
code: '',
|
||||||
|
queryState: '',
|
||||||
|
_t: null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
currentUid() {
|
||||||
|
return this.$store.getters.currentUid
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onLoad() {
|
||||||
|
const app = getApp();
|
||||||
|
const params = app.globalData.urlParams || {};
|
||||||
|
this.Token = params.token || '';
|
||||||
|
this.code = params.code || '';
|
||||||
|
this.queryState = params.state || '';
|
||||||
|
this.exuid = params.exuid || '';
|
||||||
|
if(this.queryState) this.$store.dispatch('setCurrentUid', (params.state || '').replace(/^uid/, '') || '');
|
||||||
|
|
||||||
|
|
||||||
|
this.handleAuth();
|
||||||
|
},
|
||||||
|
onUnload() {
|
||||||
|
if (this._t) {
|
||||||
|
clearTimeout(this._t);
|
||||||
|
this._t = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getBaseUrl() {
|
||||||
|
// return 'https://point.agrimedia.cn';
|
||||||
|
return window.location.origin;
|
||||||
|
// // #ifdef H5
|
||||||
|
// return window.location.origin;
|
||||||
|
// // #endif
|
||||||
|
// // #ifndef H5
|
||||||
|
// // 非 H5 平台(小程序/App)需配置为已备案的 H5 域名
|
||||||
|
return 'https://tpoint.agrimedia.cn';
|
||||||
|
// // #endif
|
||||||
|
},
|
||||||
|
|
||||||
|
handleAuth() {
|
||||||
|
const baseUrl = this.getBaseUrl();
|
||||||
|
if (this.Token) {
|
||||||
|
// 场景1:授权检测,通过 token 获取用户信息
|
||||||
|
http.get(`${baseUrl}/api/user`, null, {
|
||||||
|
header: { 'authori-zation': `Bearer ${this.Token}` }
|
||||||
|
}).then(this.handleUserInfo).catch(this.handleUserError);
|
||||||
|
} else if (this.code && this.queryState) {
|
||||||
|
// 场景2:淘宝授权回调(带 code + state)
|
||||||
|
http.get(`${baseUrl}/api/taobao/execute`, {
|
||||||
|
code: this.code,
|
||||||
|
state: this.queryState
|
||||||
|
}).then(res => {
|
||||||
|
const data = res.data || {};
|
||||||
|
this.setAuthData(data.relation_id, data.tbk_pid);
|
||||||
|
this.showSuccess('授权已完成,跳转中...');
|
||||||
|
}).catch(err => {
|
||||||
|
console.error('授权失败:', err);
|
||||||
|
const result = err.raw?.data;
|
||||||
|
const msg = result?.msg || result?.message || err.message || '授权处理失败,请重新授权';
|
||||||
|
this.showFail(msg);
|
||||||
|
});
|
||||||
|
} else if (this.exuid) {
|
||||||
|
// 场景3:三方uid授权
|
||||||
|
this.$store.dispatch('setIsThirdParty', true);
|
||||||
|
|
||||||
|
http.get(`${baseUrl}/api/taobao/third/relation_id`, {
|
||||||
|
uid: this.exuid
|
||||||
|
}).then(this.handleUserInfo).catch(this.handleUserError);
|
||||||
|
}else{
|
||||||
|
this.showFail('授权链接异常,请重新授权!');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
handleUserInfo(res) {
|
||||||
|
const data = res.data || {};
|
||||||
|
const relationId = Number(data.relation_id);
|
||||||
|
const uid = data.uid;
|
||||||
|
const pid = data.tbk_pid;
|
||||||
|
if (relationId) {
|
||||||
|
// 其实无论是否授权过,都请求都会返回有值的 pid,但是只有授权过才能正常使用
|
||||||
|
this.setAuthData(relationId, pid);
|
||||||
|
|
||||||
|
// 此处为处理跳转指定页面逻辑
|
||||||
|
const app = getApp();
|
||||||
|
const params = app.globalData.urlParams || {};
|
||||||
|
if (params?.page_uri && this.isValidPagePath(params.page_uri)) {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: params.page_uri
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.goHome();
|
||||||
|
}
|
||||||
|
} else if (uid) {
|
||||||
|
// 未授权
|
||||||
|
this.$store.dispatch('setCurrentUid', uid);
|
||||||
|
this.Environment();
|
||||||
|
} else {
|
||||||
|
this.showFail('获取用户信息失败!');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
handleUserError(err) {
|
||||||
|
console.error('获取用户信息失败:', err);
|
||||||
|
const msg = err.raw?.data?.msg || err.raw?.data?.message || err.message || '获取用户信息失败,请检查网络';
|
||||||
|
this.showFail(msg);
|
||||||
|
},
|
||||||
|
|
||||||
|
setAuthData(relationId, pid) {
|
||||||
|
this.$store.dispatch('setRelationId', relationId);
|
||||||
|
this.$store.dispatch('setPid', pid || 'mm_284380119_1881450385_111415850448');
|
||||||
|
},
|
||||||
|
|
||||||
|
isValidPagePath(pageUri) {
|
||||||
|
if (!pageUri) return false;
|
||||||
|
let path = pageUri.startsWith('/') ? pageUri.slice(1) : pageUri;
|
||||||
|
const queryIndex = path.indexOf('?');
|
||||||
|
if (queryIndex !== -1) {
|
||||||
|
path = path.slice(0, queryIndex);
|
||||||
|
}
|
||||||
|
return pagesConfig.pages.some(page => page.path === path);
|
||||||
|
},
|
||||||
|
|
||||||
|
goHome() {
|
||||||
|
if (this._t) {
|
||||||
|
clearTimeout(this._t);
|
||||||
|
this._t = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
uni.reLaunch({
|
||||||
|
url: '/pages/index/index'
|
||||||
|
});
|
||||||
|
// const url = '/affiliate-activity/pages/index/index';
|
||||||
|
|
||||||
|
// // #ifdef H5
|
||||||
|
// window.location.href = url;
|
||||||
|
// return;
|
||||||
|
// // #endif
|
||||||
|
|
||||||
|
// uni.reLaunch({
|
||||||
|
// url: url
|
||||||
|
// });
|
||||||
|
},
|
||||||
|
|
||||||
|
showFail(message) {
|
||||||
|
this.status = 'fail';
|
||||||
|
this.title = '授权失败';
|
||||||
|
this.desc = message || '请重新授权或返回首页';
|
||||||
|
},
|
||||||
|
|
||||||
|
showSuccess(message) {
|
||||||
|
this.status = 'success';
|
||||||
|
this.title = '授权成功';
|
||||||
|
this.desc = message;
|
||||||
|
// this._t = setTimeout(() => this.goHome(), 500);
|
||||||
|
this.goHome()
|
||||||
|
},
|
||||||
|
|
||||||
|
CopyStatus(message) {
|
||||||
|
this.status = 'CopyUrl';
|
||||||
|
this.title = '';
|
||||||
|
this.desc = message || '请点击下方按钮复制授权地址';
|
||||||
|
},
|
||||||
|
|
||||||
|
Environment() {
|
||||||
|
const env = getEnvironment();
|
||||||
|
const uid = `uid${this.currentUid}`;
|
||||||
|
const redirectUri = this.getRedirectUri();
|
||||||
|
const authUrl = `https://oauth.taobao.com/authorize?response_type=code&client_id=${this.client_id}&redirect_uri=${encodeURIComponent(redirectUri)}&state=${uid}&view=wap`;
|
||||||
|
|
||||||
|
if (env.isBrowser || env.isAppWebview) {
|
||||||
|
// 浏览器/APP WebView:直接跳转授权
|
||||||
|
// #ifdef H5
|
||||||
|
window.location.href = authUrl;
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
// #ifdef APP-PLUS
|
||||||
|
plus.runtime.openURL(authUrl);
|
||||||
|
// #endif
|
||||||
|
} else if (env.isWechatWebview || env.isWechatH5 || env.isWechatMiniProgram) {
|
||||||
|
// 微信/小程序环境:显示复制按钮
|
||||||
|
this.CopyStatus('请复制授权地址到浏览器打开');
|
||||||
|
this.CopyLink = true;
|
||||||
|
} else {
|
||||||
|
// 其他平台默认显示复制按钮
|
||||||
|
this.CopyStatus('请复制授权地址到浏览器打开');
|
||||||
|
this.CopyLink = true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
copyText() {
|
||||||
|
const uid = `uid${this.currentUid}`;
|
||||||
|
const redirectUri = this.getRedirectUri();
|
||||||
|
const text = `https://oauth.taobao.com/authorize?response_type=code&client_id=${this.client_id}&redirect_uri=${encodeURIComponent(redirectUri)}&state=${uid}&view=wap`;
|
||||||
|
|
||||||
|
uni.setClipboardData({
|
||||||
|
data: text,
|
||||||
|
success: () => {
|
||||||
|
uni.showToast({
|
||||||
|
title: '授权链接已复制',
|
||||||
|
icon: 'success'
|
||||||
|
});
|
||||||
|
},
|
||||||
|
fail: () => {
|
||||||
|
uni.showToast({
|
||||||
|
title: '复制失败,请手动复制',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
getRedirectUri() {
|
||||||
|
const baseUrl = this.getBaseUrl();
|
||||||
|
return baseUrl + '/affiliate-activity/pages/auth/auth';
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.auth-page {
|
||||||
|
min-height: 100vh;
|
||||||
|
background: linear-gradient(180deg, #f0f2f5 0%, #f7f8fa 100%);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 60rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-box {
|
||||||
|
background: #ffffff;
|
||||||
|
border-radius: 24rpx;
|
||||||
|
padding: 100rpx 56rpx 80rpx;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 640rpx;
|
||||||
|
text-align: center;
|
||||||
|
box-shadow: 0 8rpx 40rpx rgba(0, 0, 0, 0.06);
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-icon {
|
||||||
|
margin: 0 auto 40rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 100rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spin-animation {
|
||||||
|
animation: spin 1.2s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes spin {
|
||||||
|
0% {
|
||||||
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-title {
|
||||||
|
font-size: 44rpx;
|
||||||
|
color: #1a1a1a;
|
||||||
|
margin: 24rpx 0 20rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
letter-spacing: 2rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-desc {
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #666666;
|
||||||
|
margin-bottom: 56rpx;
|
||||||
|
line-height: 1.8;
|
||||||
|
padding: 0 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-btn {
|
||||||
|
margin-top: 32rpx;
|
||||||
|
height: 96rpx;
|
||||||
|
line-height: 96rpx;
|
||||||
|
background: #ffffff;
|
||||||
|
color: #333333;
|
||||||
|
border: 2rpx solid #e0e0e0;
|
||||||
|
border-radius: 48rpx;
|
||||||
|
font-size: 30rpx;
|
||||||
|
padding: 0 48rpx;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 12rpx;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-btn:active {
|
||||||
|
transform: scale(0.98);
|
||||||
|
opacity: 0.9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-btn::after {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-btn--primary {
|
||||||
|
background: linear-gradient(135deg, #ff715a, #ff416c);
|
||||||
|
color: #ffffff;
|
||||||
|
border: none;
|
||||||
|
box-shadow: 0 8rpx 24rpx rgba(255, 65, 108, 0.25);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-text {
|
||||||
|
color: inherit;
|
||||||
|
font-size: 30rpx;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
<view class="category-container">
|
<view class="category-container">
|
||||||
<!-- 头部搜索与导航 (1:1 还原首页样式) -->
|
<!-- 头部搜索与导航 (1:1 还原首页样式) -->
|
||||||
<view class="header">
|
<view class="header">
|
||||||
<view class="status-bar" :style="{ height: statusBarHeight + 'px' }"></view>
|
<!-- <view class="status-bar" :style="{ height: statusBarHeight + 'px' }"></view> -->
|
||||||
<view class="search-section">
|
<view class="search-section">
|
||||||
<view class="search-bar-wrap" @click="goSearch">
|
<view class="search-bar-wrap" @click="goSearch">
|
||||||
<text class="search-icon">🔍</text>
|
<text class="search-icon">🔍</text>
|
||||||
|
|
@ -21,7 +21,7 @@
|
||||||
|
|
||||||
<scroll-view scroll-y class="main-content" @scrolltolower="loadMore">
|
<scroll-view scroll-y class="main-content" @scrolltolower="loadMore">
|
||||||
<!-- 占位符适配固定头部 -->
|
<!-- 占位符适配固定头部 -->
|
||||||
<view class="header-placeholder" :style="{ height: (statusBarHeight + 90) + 'px' }"></view>
|
<view class="header-placeholder" :style="{ height: '90px' }"></view>
|
||||||
|
|
||||||
<!-- 子分类金刚区 (支持翻页) -->
|
<!-- 子分类金刚区 (支持翻页) -->
|
||||||
<view class="sub-grid-wrap" v-if="subCategories.length > 0">
|
<view class="sub-grid-wrap" v-if="subCategories.length > 0">
|
||||||
|
|
@ -82,6 +82,12 @@
|
||||||
<text class="coupon-txt">{{ goods.couponValue }}元</text>
|
<text class="coupon-txt">{{ goods.couponValue }}元</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
<view class="price-coupon" v-if="!$store.state.isThirdParty">
|
||||||
|
<view class="coupon-left">
|
||||||
|
<text class="coupon-tip">预估消费券</text>
|
||||||
|
<text class="coupon-val">{{ $estimateCoupon(goods.tkmoney) }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="goods-bottom-info">
|
<view class="goods-bottom-info">
|
||||||
|
|
@ -103,6 +109,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import http from '@/request/request.js';
|
||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
|
@ -154,12 +161,10 @@
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
getCategoryTabs() {
|
getCategoryTabs() {
|
||||||
uni.request({
|
http.get('https://api.cmspro.haodanku.com/index/superCategory?is_get_second=1&cid=YsWZ21tx').then(res => {
|
||||||
url: 'https://api.cmspro.haodanku.com/index/superCategory?is_get_second=1&cid=YsWZ21tx',
|
if (res.data) {
|
||||||
success: (res) => {
|
|
||||||
if (res.data && res.data.code === 200) {
|
|
||||||
// 映射为与首页一致的 navList 结构
|
// 映射为与首页一致的 navList 结构
|
||||||
const categories = res.data.data.map(item => ({
|
const categories = res.data.map(item => ({
|
||||||
name: item.name,
|
name: item.name,
|
||||||
cat_id: item.cat_id,
|
cat_id: item.cat_id,
|
||||||
second: item.second || []
|
second: item.second || []
|
||||||
|
|
@ -171,7 +176,6 @@
|
||||||
this.subCategories = currentCat.second;
|
this.subCategories = currentCat.second;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
getProducts(refresh = false) {
|
getProducts(refresh = false) {
|
||||||
|
|
@ -191,16 +195,15 @@
|
||||||
sortParam = this.priceOrder === 'asc' ? 8 : 9;
|
sortParam = this.priceOrder === 'asc' ? 8 : 9;
|
||||||
}
|
}
|
||||||
|
|
||||||
uni.request({
|
http.get(`https://api.cmspro.haodanku.com/find/allItemList?category_id=${this.currentCatId}&page=${this.page}&sort=${sortParam}&page_size=20&cid=YsWZ21tx`).then(res => {
|
||||||
url: `https://api.cmspro.haodanku.com/find/allItemList?category_id=${this.currentCatId}&page=${this.page}&sort=${sortParam}&page_size=20&cid=YsWZ21tx`,
|
if (res.data && res.data.item_info) {
|
||||||
success: (res) => {
|
const list = res.data.item_info.map(item => ({
|
||||||
if (res.data && res.data.code === 200 && res.data.data && res.data.data.item_info) {
|
|
||||||
const list = res.data.data.item_info.map(item => ({
|
|
||||||
id: item.id,
|
id: item.id,
|
||||||
image: item.itempic,
|
image: item.itempic,
|
||||||
title: item.itemshorttitle && item.itemshorttitle.length > 17 ? item.itemshorttitle.substring(0, 17) + '...' : item.itemshorttitle,
|
title: item.itemshorttitle && item.itemshorttitle.length > 17 ? item.itemshorttitle.substring(0, 17) + '...' : item.itemshorttitle,
|
||||||
finalPrice: item.itemendprice,
|
finalPrice: item.itemendprice,
|
||||||
couponValue: item.couponmoney || 0,
|
couponValue: item.couponmoney || 0,
|
||||||
|
tkmoney: item.tkmoney || 0,
|
||||||
sales: item.itemsale >= 10000 ? (item.itemsale / 10000).toFixed(1) + '万' : item.itemsale,
|
sales: item.itemsale >= 10000 ? (item.itemsale / 10000).toFixed(1) + '万' : item.itemsale,
|
||||||
shopType: item.shoptype === 'B' ? '天猫' : '淘宝',
|
shopType: item.shoptype === 'B' ? '天猫' : '淘宝',
|
||||||
shopName: item.shopname,
|
shopName: item.shopname,
|
||||||
|
|
@ -221,10 +224,8 @@
|
||||||
} else {
|
} else {
|
||||||
this.finished = true;
|
this.finished = true;
|
||||||
}
|
}
|
||||||
},
|
}).finally(() => {
|
||||||
complete: () => {
|
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
}
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
switchCategory(item) {
|
switchCategory(item) {
|
||||||
|
|
@ -625,6 +626,23 @@
|
||||||
padding: 0 8rpx;
|
padding: 0 8rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.price-coupon {
|
||||||
|
display: flex;
|
||||||
|
align-items: baseline;
|
||||||
|
margin-top: 4rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.coupon-tip {
|
||||||
|
font-size: 22rpx;
|
||||||
|
color: #ff8a00;
|
||||||
|
}
|
||||||
|
|
||||||
|
.coupon-val {
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #ff8a00;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
.goods-bottom-info {
|
.goods-bottom-info {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
<view class="detail-page-container">
|
<view class="detail-page-container">
|
||||||
<!-- 沉浸式顶部标题栏 -->
|
<!-- 沉浸式顶部标题栏 -->
|
||||||
<view class="nav-header">
|
<view class="nav-header">
|
||||||
<view class="status-bar" :style="{ height: statusBarHeight + 'px' }"></view>
|
<!-- <view class="status-bar" :style="{ height: statusBarHeight + 'px' }"></view> -->
|
||||||
<view class="header-content">
|
<view class="header-content">
|
||||||
<view class="back-area" @click="goBack">
|
<view class="back-area" @click="goBack">
|
||||||
<text class="back-icon">〈</text>
|
<text class="back-icon">〈</text>
|
||||||
|
|
@ -16,7 +16,7 @@
|
||||||
|
|
||||||
<scroll-view scroll-y class="scroll-content" @scrolltolower="loadMore">
|
<scroll-view scroll-y class="scroll-content" @scrolltolower="loadMore">
|
||||||
<!-- 占位适配固定头部 -->
|
<!-- 占位适配固定头部 -->
|
||||||
<view class="header-placeholder" :style="{ height: (statusBarHeight + 44) + 'px' }"></view>
|
<view class="header-placeholder" :style="{ height: '44px' }"></view>
|
||||||
|
|
||||||
<!-- 排序筛选栏 -->
|
<!-- 排序筛选栏 -->
|
||||||
<view class="filter-bar">
|
<view class="filter-bar">
|
||||||
|
|
@ -33,26 +33,33 @@
|
||||||
|
|
||||||
<!-- 高级筛选标签栏 -->
|
<!-- 高级筛选标签栏 -->
|
||||||
<view class="sub-filter-row">
|
<view class="sub-filter-row">
|
||||||
<view class="sub-filter-item" :class="{ 'active': filterHasCoupon }" @click="toggleFilter('coupon')">有券</view>
|
<view class="sub-filter-item" :class="{ 'active': filterHasCoupon }" @click="toggleFilter('coupon')">有券
|
||||||
<view class="sub-filter-item" :class="{ 'active': filterIsFlagship }" @click="toggleFilter('flagship')">旗舰店</view>
|
</view>
|
||||||
<view class="sub-filter-item" :class="{ 'active': filterIsTmall }" @click="toggleFilter('tmall')">天猫</view>
|
<view class="sub-filter-item" :class="{ 'active': filterIsFlagship }" @click="toggleFilter('flagship')">
|
||||||
<view class="sub-filter-item" :class="{ 'active': filterIsBrand }" @click="toggleFilter('brand')">品牌</view>
|
旗舰店</view>
|
||||||
|
<view class="sub-filter-item" :class="{ 'active': filterIsTmall }" @click="toggleFilter('tmall')">天猫
|
||||||
|
</view>
|
||||||
|
<view class="sub-filter-item" :class="{ 'active': filterIsBrand }" @click="toggleFilter('brand')">品牌
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 商品列表 (横向单列风格) -->
|
<!-- 商品列表 (横向单列风格) -->
|
||||||
<view class="goods-list">
|
<view class="goods-list">
|
||||||
<view class="goods-item" v-for="(goods, idx) in goodsList" :key="idx" @click="goToDetail(goods.id)">
|
<view class="goods-item" v-for="(goods, idx) in goodsList" :key="idx"
|
||||||
|
@click="goToDetail('id', goods.id)">
|
||||||
<view class="g-img-left">
|
<view class="g-img-left">
|
||||||
<image class="goods-img" :src="goods.image" mode="aspectFill"></image>
|
<image class="goods-img" :src="goods.image" mode="aspectFill"></image>
|
||||||
</view>
|
</view>
|
||||||
<view class="goods-info">
|
<view class="goods-info">
|
||||||
<view class="goods-title-row">
|
<view class="goods-title-row">
|
||||||
<image src="https://img-haodanku-com.cdn.fudaiapp.com/FlyOSTvjC3LjrkUoJ0NPxx1qnGz4" class="platform-icon"></image>
|
<image src="https://img-haodanku-com.cdn.fudaiapp.com/FlyOSTvjC3LjrkUoJ0NPxx1qnGz4"
|
||||||
|
class="platform-icon"></image>
|
||||||
<text class="title-text">{{ goods.title }}</text>
|
<text class="title-text">{{ goods.title }}</text>
|
||||||
</view>
|
</view>
|
||||||
<!-- 标签组 -->
|
<!-- 标签组 -->
|
||||||
<view class="labels-row" v-if="goods.labels && goods.labels.length > 0">
|
<view class="labels-row" v-if="goods.labels && goods.labels.length > 0">
|
||||||
<text class="label-tag" v-for="(label, lIdx) in goods.labels.slice(0, 2)" :key="lIdx">{{ label }}</text>
|
<text class="label-tag" v-for="(label, lIdx) in goods.labels.slice(0, 2)"
|
||||||
|
:key="lIdx">{{ label }}</text>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="goods-price-section">
|
<view class="goods-price-section">
|
||||||
|
|
@ -65,6 +72,12 @@
|
||||||
<text class="coupon-txt">{{ goods.couponValue }}元</text>
|
<text class="coupon-txt">{{ goods.couponValue }}元</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
<view class="price-coupon" v-if="!$store.state.isThirdParty">
|
||||||
|
<view class="coupon-left">
|
||||||
|
<text class="coupon-tip">预估消费券</text>
|
||||||
|
<text class="coupon-val">{{ $estimateCoupon(goods.tkmoney) }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="goods-bottom-info">
|
<view class="goods-bottom-info">
|
||||||
|
|
@ -85,17 +98,20 @@
|
||||||
|
|
||||||
<!-- 推荐商品列表 -->
|
<!-- 推荐商品列表 -->
|
||||||
<view class="goods-list recommend-list" v-if="recommendList.length > 0">
|
<view class="goods-list recommend-list" v-if="recommendList.length > 0">
|
||||||
<view class="goods-item" v-for="(goods, idx) in recommendList" :key="idx" @click="goToDetail(goods.id)">
|
<view class="goods-item" v-for="(goods, idx) in recommendList" :key="idx"
|
||||||
|
@click="goToDetail('keywordid', goods.id)">
|
||||||
<view class="g-img-left">
|
<view class="g-img-left">
|
||||||
<image class="goods-img" :src="goods.image" mode="aspectFill"></image>
|
<image class="goods-img" :src="goods.image" mode="aspectFill"></image>
|
||||||
</view>
|
</view>
|
||||||
<view class="goods-info">
|
<view class="goods-info">
|
||||||
<view class="goods-title-row">
|
<view class="goods-title-row">
|
||||||
<image src="https://img-haodanku-com.cdn.fudaiapp.com/FlyOSTvjC3LjrkUoJ0NPxx1qnGz4" class="platform-icon"></image>
|
<image src="https://img-haodanku-com.cdn.fudaiapp.com/FlyOSTvjC3LjrkUoJ0NPxx1qnGz4"
|
||||||
|
class="platform-icon"></image>
|
||||||
<text class="title-text">{{ goods.title }}</text>
|
<text class="title-text">{{ goods.title }}</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="labels-row" v-if="goods.labels && goods.labels.length > 0">
|
<view class="labels-row" v-if="goods.labels && goods.labels.length > 0">
|
||||||
<text class="label-tag" v-for="(label, lIdx) in goods.labels.slice(0, 2)" :key="lIdx">{{ label }}</text>
|
<text class="label-tag" v-for="(label, lIdx) in goods.labels.slice(0, 2)"
|
||||||
|
:key="lIdx">{{ label }}</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="goods-price-section">
|
<view class="goods-price-section">
|
||||||
<view class="price-main">
|
<view class="price-main">
|
||||||
|
|
@ -107,6 +123,12 @@
|
||||||
<text class="coupon-txt">{{ goods.couponValue }}元</text>
|
<text class="coupon-txt">{{ goods.couponValue }}元</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
<view class="price-coupon" v-if="!$store.state.isThirdParty">
|
||||||
|
<view class="coupon-left">
|
||||||
|
<text class="coupon-tip">预估消费券</text>
|
||||||
|
<text class="coupon-val">{{ $estimateCoupon(goods.tkmoney) }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="goods-bottom-info">
|
<view class="goods-bottom-info">
|
||||||
<text class="sales">已售{{ goods.sales }}件</text>
|
<text class="sales">已售{{ goods.sales }}件</text>
|
||||||
|
|
@ -129,6 +151,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import http from '@/request/request.js';
|
||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
|
@ -195,17 +218,20 @@
|
||||||
filterParams += `&shoptype=${shopTypes.join(',')}`;
|
filterParams += `&shoptype=${shopTypes.join(',')}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
uni.request({
|
http.get(
|
||||||
url: `https://api.cmspro.haodanku.com/find/allItemList?category_id=${this.mainCatId}&son_category=${encodeURIComponent(this.secondCategory)}&page=${this.page}&sort=${sortParam}&page_size=20&cid=YsWZ21tx${filterParams}`,
|
`https://api.cmspro.haodanku.com/find/allItemList?category_id=${this.mainCatId}&son_category=${encodeURIComponent(this.secondCategory)}&page=${this.page}&sort=${sortParam}&page_size=20&cid=YsWZ21tx${filterParams}`
|
||||||
success: (res) => {
|
).then(res => {
|
||||||
if (res.data && res.data.code === 200 && res.data.data && res.data.data.item_info) {
|
if (res.data && res.data.item_info) {
|
||||||
const list = res.data.data.item_info.map(item => ({
|
const list = res.data.item_info.map(item => ({
|
||||||
id: item.id,
|
id: item.id,
|
||||||
image: item.itempic,
|
image: item.itempic,
|
||||||
title: item.itemshorttitle && item.itemshorttitle.length > 18 ? item.itemshorttitle.substring(0, 18) + '...' : item.itemshorttitle,
|
title: item.itemshorttitle && item.itemshorttitle.length > 18 ? item
|
||||||
|
.itemshorttitle.substring(0, 18) + '...' : item.itemshorttitle,
|
||||||
finalPrice: item.itemendprice,
|
finalPrice: item.itemendprice,
|
||||||
couponValue: item.couponmoney || 0,
|
couponValue: item.couponmoney || 0,
|
||||||
sales: item.itemsale >= 10000 ? (item.itemsale / 10000).toFixed(1) + '万' : item.itemsale,
|
tkmoney: item.tkmoney || 0,
|
||||||
|
sales: item.itemsale >= 10000 ? (item.itemsale / 10000).toFixed(1) + '万' :
|
||||||
|
item.itemsale,
|
||||||
shopType: item.shoptype === 'B' ? '天猫' : '淘宝',
|
shopType: item.shoptype === 'B' ? '天猫' : '淘宝',
|
||||||
shopName: item.shopname,
|
shopName: item.shopname,
|
||||||
labels: item.label || []
|
labels: item.label || []
|
||||||
|
|
@ -229,10 +255,8 @@
|
||||||
this.finished = true;
|
this.finished = true;
|
||||||
if (refresh) this.getRecommendList();
|
if (refresh) this.getRecommendList();
|
||||||
}
|
}
|
||||||
},
|
}).finally(() => {
|
||||||
complete: () => {
|
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
}
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
getRecommendList() {
|
getRecommendList() {
|
||||||
|
|
@ -244,27 +268,33 @@
|
||||||
if (this.filterIsFlagship) recommendParams += '&is_tmall=1';
|
if (this.filterIsFlagship) recommendParams += '&is_tmall=1';
|
||||||
if (this.filterIsTmall) recommendParams += '&min_id=1';
|
if (this.filterIsTmall) recommendParams += '&min_id=1';
|
||||||
|
|
||||||
uni.request({
|
http.get(
|
||||||
url: `https://api.cmspro.haodanku.com/superSearch/getList?sort=0&page_size=20&category_id=${this.mainCatId}&son_category=${encodeURIComponent(this.secondCategory)}&cid=YsWZ21tx${recommendParams}`,
|
`https://api.cmspro.haodanku.com/superSearch/getList?sort=0&page_size=20&category_id=${this.mainCatId}&son_category=${encodeURIComponent(this.secondCategory)}&cid=YsWZ21tx${recommendParams}`
|
||||||
success: (res) => {
|
).then(res => {
|
||||||
if (res.data && res.data.code === 200 && res.data.data) {
|
console.log('res', res)
|
||||||
const list = res.data.data.map(item => ({
|
if (res.data) {
|
||||||
|
const list = res.data.map(item => {
|
||||||
|
console.log('推荐商品:', item.id || item.itemid)
|
||||||
|
return {
|
||||||
id: item.id || item.itemid,
|
id: item.id || item.itemid,
|
||||||
image: item.itempic,
|
image: item.itempic,
|
||||||
title: item.itemshorttitle && item.itemshorttitle.length > 18 ? item.itemshorttitle.substring(0, 18) + '...' : item.itemshorttitle,
|
title: item.itemshorttitle && item.itemshorttitle.length > 18 ? item
|
||||||
|
.itemshorttitle.substring(0, 18) + '...' : item.itemshorttitle,
|
||||||
finalPrice: item.itemendprice,
|
finalPrice: item.itemendprice,
|
||||||
couponValue: item.couponmoney || 0,
|
couponValue: item.couponmoney || 0,
|
||||||
sales: item.itemsale >= 10000 ? (item.itemsale / 10000).toFixed(1) + '万' : item.itemsale,
|
tkmoney: item.tkmoney || 0,
|
||||||
|
sales: item.itemsale >= 10000 ? (item.itemsale / 10000).toFixed(1) + '万' :
|
||||||
|
item.itemsale,
|
||||||
shopType: item.shoptype === 'B' ? '天猫' : '淘宝',
|
shopType: item.shoptype === 'B' ? '天猫' : '淘宝',
|
||||||
shopName: item.shopname,
|
shopName: item.shopname,
|
||||||
labels: item.label || (item.couponmoney > 0 ? [`${item.couponmoney}元券`] : [])
|
labels: item.label || (item.couponmoney > 0 ? [`${item.couponmoney}元券`] :
|
||||||
}));
|
[])
|
||||||
|
}
|
||||||
|
});
|
||||||
this.recommendList = list;
|
this.recommendList = list;
|
||||||
}
|
}
|
||||||
},
|
}).finally(() => {
|
||||||
complete: () => {
|
|
||||||
this.loadingRecommend = false;
|
this.loadingRecommend = false;
|
||||||
}
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
changeSort(type) {
|
changeSort(type) {
|
||||||
|
|
@ -287,9 +317,10 @@
|
||||||
loadMore() {
|
loadMore() {
|
||||||
this.getProducts();
|
this.getProducts();
|
||||||
},
|
},
|
||||||
goToDetail(id) {
|
goToDetail(key, id) {
|
||||||
|
// console.log(`/pages/detail/detail?${key}=${id}`)
|
||||||
uni.navigateTo({
|
uni.navigateTo({
|
||||||
url: `/pages/detail/detail?id=${id}`
|
url: `/pages/detail/detail?${key}=${id}`
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -544,7 +575,7 @@
|
||||||
.coupon-icon {
|
.coupon-icon {
|
||||||
font-size: 20rpx;
|
font-size: 20rpx;
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
background: rgba(255,255,255,0.2);
|
background: rgba(255, 255, 255, 0.2);
|
||||||
padding: 0 6rpx;
|
padding: 0 6rpx;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
@ -557,6 +588,23 @@
|
||||||
padding: 0 8rpx;
|
padding: 0 8rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.price-coupon {
|
||||||
|
display: flex;
|
||||||
|
align-items: baseline;
|
||||||
|
margin-top: 4rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.coupon-tip {
|
||||||
|
font-size: 22rpx;
|
||||||
|
color: #ff8a00;
|
||||||
|
}
|
||||||
|
|
||||||
|
.coupon-val {
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #ff8a00;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
.goods-bottom-info {
|
.goods-bottom-info {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<view class="classify-container">
|
<view class="classify-container">
|
||||||
<view class="classify-header">
|
<view class="classify-header">
|
||||||
<view class="status-bar" :style="{ height: statusBarHeight + 'px' }"></view>
|
<!-- <view class="status-bar" :style="{ height: statusBarHeight + 'px' }"></view> -->
|
||||||
<view class="header-content">
|
<view class="header-content">
|
||||||
<view class="search-bar" @click="goToSearch">
|
<view class="search-bar" @click="goToSearch">
|
||||||
<text class="search-icon">🔍</text>
|
<text class="search-icon">🔍</text>
|
||||||
|
|
@ -10,7 +10,7 @@
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="main-body" :style="{ paddingTop: (statusBarHeight + 44) + 'px' }">
|
<view class="main-body" :style="{ paddingTop: '44px' }">
|
||||||
<!-- 左侧一级菜单 -->
|
<!-- 左侧一级菜单 -->
|
||||||
<scroll-view scroll-y class="side-bar" :show-scrollbar="false">
|
<scroll-view scroll-y class="side-bar" :show-scrollbar="false">
|
||||||
<view class="side-item" :class="{ 'active': activeIndex === index }"
|
<view class="side-item" :class="{ 'active': activeIndex === index }"
|
||||||
|
|
@ -47,6 +47,7 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import BottomNav from '@/components/bottom-nav/bottom-nav.vue';
|
import BottomNav from '@/components/bottom-nav/bottom-nav.vue';
|
||||||
|
import http from '@/request/request.js';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
|
|
@ -75,29 +76,23 @@
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
getSuperCategory() {
|
getSuperCategory() {
|
||||||
uni.request({
|
http.get('https://api.cmspro.haodanku.com/index/superCategory?cid=YsWZ21tx', undefined, {
|
||||||
url: 'https://api.cmspro.haodanku.com/index/superCategory?cid=YsWZ21tx',
|
|
||||||
header: {
|
header: {
|
||||||
'accept': 'application/json, text/plain, */*',
|
'accept': 'application/json, text/plain, */*',
|
||||||
'accept-language': 'zh-CN,zh;q=0.9'
|
'accept-language': 'zh-CN,zh;q=0.9'
|
||||||
},
|
}
|
||||||
success: (res) => {
|
}).then(res => {
|
||||||
if (res.data && res.data.code === 200) {
|
this.categoryData = res.data;
|
||||||
this.categoryData = res.data.data;
|
|
||||||
// 获取数据后尝试匹配底部导航高亮状态
|
// 获取数据后尝试匹配底部导航高亮状态
|
||||||
this.matchNavActive();
|
this.matchNavActive();
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
matchNavActive() {
|
matchNavActive() {
|
||||||
// 获取底部导航数据,查找“分类”对应的索引
|
// 获取底部导航数据,查找“分类”对应的索引
|
||||||
uni.request({
|
http.get('https://api.cmspro.haodanku.com/bottomBar/lists?cid=YsWZ21tx').then(res => {
|
||||||
url: 'https://api.cmspro.haodanku.com/bottomBar/lists?cid=YsWZ21tx',
|
if (res.data && res.data.bottom_bar) {
|
||||||
success: (res) => {
|
|
||||||
if (res.data && res.data.code === 200 && res.data.data.bottom_bar) {
|
|
||||||
const allowedTitles = ['首页', '榜单', '分类'];
|
const allowedTitles = ['首页', '榜单', '分类'];
|
||||||
const list = res.data.data.bottom_bar
|
const list = res.data.bottom_bar
|
||||||
.filter(item => allowedTitles.includes(item.title))
|
.filter(item => allowedTitles.includes(item.title))
|
||||||
.sort((a, b) => b.sort - a.sort);
|
.sort((a, b) => b.sort - a.sort);
|
||||||
const idx = list.findIndex(item => item.title === '分类');
|
const idx = list.findIndex(item => item.title === '分类');
|
||||||
|
|
@ -105,7 +100,6 @@
|
||||||
this.navActiveIndex = idx;
|
this.navActiveIndex = idx;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
switchMainCategory(index) {
|
switchMainCategory(index) {
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,16 @@
|
||||||
<template>
|
<template>
|
||||||
<view class="detail-container">
|
<view class="detail-container">
|
||||||
<!-- 顶部渐变导航栏 -->
|
<!-- 顶部渐变导航栏 -->
|
||||||
<view class="fixed-header" :style="{ opacity: navOpacity, paddingTop: statusBarHeight + 'px' }">
|
|
||||||
|
<view class="fixed-header" :style="{ opacity: navOpacity }">
|
||||||
<view class="header-content">
|
<view class="header-content">
|
||||||
<text class="header-title">商品详情</text>
|
<text class="header-title">商品详情</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 悬浮返回按钮 -->
|
<!-- 悬浮返回按钮 -->
|
||||||
<view class="back-btn" :style="{ top: statusBarHeight + 'px', backgroundColor: 'rgba(0,0,0,' + bgOpacity + ')' }" @click="goBack">
|
|
||||||
|
<view class="back-btn" :style="{ backgroundColor: 'rgba(0,0,0,' + bgOpacity + ')' }" @click="goBack">
|
||||||
<view class="back-icon-box" :style="{ borderColor: iconColor }"></view>
|
<view class="back-icon-box" :style="{ borderColor: iconColor }"></view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
|
|
@ -28,7 +30,7 @@
|
||||||
<!-- 活动横幅 -->
|
<!-- 活动横幅 -->
|
||||||
<view class="activity-banner" v-if="product.activity && product.activity.app_img">
|
<view class="activity-banner" v-if="product.activity && product.activity.app_img">
|
||||||
<image :src="product.activity.app_img" mode="widthFix" class="banner-img"></image>
|
<image :src="product.activity.app_img" mode="widthFix" class="banner-img"></image>
|
||||||
<view class="banner-time" v-if="product.activityTime">TIME: {{ product.activityTime }}</view>
|
<!-- <view class="banner-time" v-if="product.activityTime">TIME: {{ product.activityTime }}</view> -->
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 热销榜单提示 -->
|
<!-- 热销榜单提示 -->
|
||||||
|
|
@ -66,6 +68,9 @@
|
||||||
<view class="tags-row">
|
<view class="tags-row">
|
||||||
<text class="tag-capsule" v-for="(tag, index) in product.labels" :key="index">{{ tag }}</text>
|
<text class="tag-capsule" v-for="(tag, index) in product.labels" :key="index">{{ tag }}</text>
|
||||||
</view>
|
</view>
|
||||||
|
<view class="estimate-tip" v-if="!$store.state.isThirdParty">
|
||||||
|
<text>消费券:{{ $estimateCoupon(product.commission) }}为预估值,实际以系统发放为准</text>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 优惠券模块 -->
|
<!-- 优惠券模块 -->
|
||||||
|
|
@ -78,11 +83,11 @@
|
||||||
</view>
|
</view>
|
||||||
<view class="coupon-time">使用期限: {{ product.couponTime }}</view>
|
<view class="coupon-time">使用期限: {{ product.couponTime }}</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="coupon-right">
|
<!-- <view class="coupon-right">
|
||||||
<view class="get-btn" @click="goBuy">立即领券</view>
|
<view class="get-btn" @click="goBuy">立即领券</view>
|
||||||
</view>
|
</view> -->
|
||||||
<view class="coupon-circle top"></view>
|
<!-- <view class="coupon-circle top"></view> -->
|
||||||
<view class="coupon-circle bottom"></view>
|
<!-- <view class="coupon-circle bottom"></view> -->
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
|
|
@ -158,7 +163,7 @@
|
||||||
</view>
|
</view>
|
||||||
<view class="list">
|
<view class="list">
|
||||||
<view class="list-container">
|
<view class="list-container">
|
||||||
<view class="g-shop-list" v-for="(item, idx) in similarProducts" :key="idx" @click="goToDetail(item.itemid)">
|
<view class="g-shop-list" v-for="(item, idx) in similarProducts" :key="idx" @click="goToDetail(item.id)">
|
||||||
<view class="shop-img">
|
<view class="shop-img">
|
||||||
<image :src="item.itempic" mode="aspectFill" lazy-load></image>
|
<image :src="item.itempic" mode="aspectFill" lazy-load></image>
|
||||||
</view>
|
</view>
|
||||||
|
|
@ -178,6 +183,12 @@
|
||||||
<text>{{ item.couponmoney }}元</text>
|
<text>{{ item.couponmoney }}元</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
<view class="flex-left coupon-row" v-if="!$store.state.isThirdParty">
|
||||||
|
<view class="d-coupon-left">
|
||||||
|
<text class="coupon-tip">预估消费券</text>
|
||||||
|
<text class="coupon-val">{{ $estimateCoupon(item.tkmoney) }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
<view class="flex-left flex-left-t">
|
<view class="flex-left flex-left-t">
|
||||||
<view class="sales-volume">已售{{ formatSales(item.itemsale) }}件</view>
|
<view class="sales-volume">已售{{ formatSales(item.itemsale) }}件</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
@ -225,7 +236,7 @@
|
||||||
</view>
|
</view>
|
||||||
<view class="bar-right">
|
<view class="bar-right">
|
||||||
<view class="copy-btn" @click="copyTaoWord">复制口令购买</view>
|
<view class="copy-btn" @click="copyTaoWord">复制口令购买</view>
|
||||||
<view class="buy-btn" @click="goBuy">领券购买</view>
|
<!-- <view class="buy-btn" @click="goBuy">领券购买</view> -->
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
@ -272,17 +283,24 @@
|
||||||
},
|
},
|
||||||
onLoad(options) {
|
onLoad(options) {
|
||||||
this.product.id = options.id || '55493973';
|
this.product.id = options.id || '55493973';
|
||||||
|
this.keywordid = options.keywordid || '';
|
||||||
const sysInfo = uni.getSystemInfoSync();
|
const sysInfo = uni.getSystemInfoSync();
|
||||||
this.statusBarHeight = sysInfo.statusBarHeight || 44;
|
this.statusBarHeight = sysInfo.statusBarHeight || 44;
|
||||||
this.getDetailData();
|
this.getDetailData();
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
getDetailData() {
|
getDetailData() {
|
||||||
|
const useSuperSearch = this.keywordid;
|
||||||
uni.request({
|
uni.request({
|
||||||
url: `https://api.cmspro.haodanku.com/detail/itemInfo?id=${this.product.id}&cid=YsWZ21tx`,
|
url: useSuperSearch
|
||||||
|
? `https://api.cmspro.haodanku.com/superSearch/getList?keyword=${this.keywordid}&cid=YsWZ21tx`
|
||||||
|
: `https://api.cmspro.haodanku.com/detail/itemInfo?id=${this.product.id}&cid=YsWZ21tx`,
|
||||||
success: (res) => {
|
success: (res) => {
|
||||||
if (res.data && res.data.code === 200) {
|
if (res.data && res.data.code === 200) {
|
||||||
const d = res.data.data;
|
const d = useSuperSearch
|
||||||
|
? (Array.isArray(res.data.data) && res.data.data.length > 0 ? res.data.data[0] : null)
|
||||||
|
: res.data.data;
|
||||||
|
if (!d) return;
|
||||||
console.log('详情数据获取成功:', d);
|
console.log('详情数据获取成功:', d);
|
||||||
|
|
||||||
// 格式化活动时间
|
// 格式化活动时间
|
||||||
|
|
@ -379,12 +397,19 @@
|
||||||
this.getSimilarProducts(d.itemid, d.son_category);
|
this.getSimilarProducts(d.itemid, d.son_category);
|
||||||
|
|
||||||
this.isLoaded = true;
|
this.isLoaded = true;
|
||||||
|
// 预加载淘口令和领券链接,避免 iOS 异步剪贴板限制
|
||||||
|
this.fetchTaoWord();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
fetchTaoWord(callback) {
|
fetchTaoWord(callback) {
|
||||||
if (!this.product.taobaoId) return;
|
if (!this.product.taobaoId) return;
|
||||||
|
// 已获取过则直接回调,避免重复请求,同时保证 iOS 剪贴板在同步路径中
|
||||||
|
if (this.product.taoCode || this.product.couponUrl) {
|
||||||
|
if (callback) callback();
|
||||||
|
return;
|
||||||
|
}
|
||||||
uni.showLoading({ title: '加载中...', mask: true });
|
uni.showLoading({ title: '加载中...', mask: true });
|
||||||
uni.request({
|
uni.request({
|
||||||
url: 'https://v2.api.haodanku.com/ratesurl',
|
url: 'https://v2.api.haodanku.com/ratesurl',
|
||||||
|
|
@ -398,7 +423,7 @@
|
||||||
get_taoword: 1,
|
get_taoword: 1,
|
||||||
pid: 'mm_284380119_1881450385_111415850448',
|
pid: 'mm_284380119_1881450385_111415850448',
|
||||||
tb_name: 'michuan2018',
|
tb_name: 'michuan2018',
|
||||||
relation_id: '2864040832',
|
relation_id: this.$store.getters.relationId || '',
|
||||||
apikey: '5417B681C5EA'
|
apikey: '5417B681C5EA'
|
||||||
},
|
},
|
||||||
success: (res) => {
|
success: (res) => {
|
||||||
|
|
@ -423,7 +448,10 @@
|
||||||
url: `https://api.cmspro.haodanku.com/detail/getRecommendItems?itemid=${itemId}&son_category=${sonCategory}&cid=YsWZ21tx`,
|
url: `https://api.cmspro.haodanku.com/detail/getRecommendItems?itemid=${itemId}&son_category=${sonCategory}&cid=YsWZ21tx`,
|
||||||
success: (res) => {
|
success: (res) => {
|
||||||
if (res.data && res.data.code === 200) {
|
if (res.data && res.data.code === 200) {
|
||||||
this.similarProducts = res.data.data.slice(0, 10);
|
this.similarProducts = res.data.data.slice(0, 10).map(item => ({
|
||||||
|
...item,
|
||||||
|
tkmoney: item.tkmoney || 0
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -435,6 +463,7 @@
|
||||||
return sale;
|
return sale;
|
||||||
},
|
},
|
||||||
goToDetail(id) {
|
goToDetail(id) {
|
||||||
|
// console.log(id)
|
||||||
uni.navigateTo({
|
uni.navigateTo({
|
||||||
url: `/pages/detail/detail?id=${id}`
|
url: `/pages/detail/detail?id=${id}`
|
||||||
});
|
});
|
||||||
|
|
@ -466,17 +495,23 @@
|
||||||
uni.showToast({ title: '暂无领券链接', icon: 'none' });
|
uni.showToast({ title: '暂无领券链接', icon: 'none' });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// #ifdef H5
|
|
||||||
window.location.href = url;
|
|
||||||
// #endif
|
|
||||||
// #ifndef H5
|
|
||||||
uni.setClipboardData({
|
uni.setClipboardData({
|
||||||
data: url,
|
data: url,
|
||||||
success: () => {
|
success: () => {
|
||||||
uni.showToast({ title: '链接已复制,请到浏览器打开', icon: 'none' });
|
uni.showToast({ title: '链接已复制,请到浏览器打开', icon: 'none' });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// #endif
|
// // #ifdef H5
|
||||||
|
// window.location.href = url;
|
||||||
|
// // #endif
|
||||||
|
// // #ifndef H5
|
||||||
|
// uni.setClipboardData({
|
||||||
|
// data: url,
|
||||||
|
// success: () => {
|
||||||
|
// uni.showToast({ title: '链接已复制,请到浏览器打开', icon: 'none' });
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
// // #endif
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
handleScroll(e) {
|
handleScroll(e) {
|
||||||
|
|
@ -491,7 +526,7 @@
|
||||||
this.iconColor = ratio > 0.5 ? '#333333' : '#ffffff';
|
this.iconColor = ratio > 0.5 ? '#333333' : '#ffffff';
|
||||||
},
|
},
|
||||||
goHome() {
|
goHome() {
|
||||||
uni.switchTab({
|
uni.redirectTo({
|
||||||
url: '/pages/index/index'
|
url: '/pages/index/index'
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
@ -880,6 +915,22 @@
|
||||||
padding: 0 8rpx;
|
padding: 0 8rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.coupon-row {
|
||||||
|
align-items: baseline;
|
||||||
|
margin-top: 4rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.coupon-row .coupon-tip {
|
||||||
|
font-size: 22rpx;
|
||||||
|
color: #ff8a00;
|
||||||
|
}
|
||||||
|
|
||||||
|
.coupon-row .coupon-val {
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: #ff8a00;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
.sales-volume {
|
.sales-volume {
|
||||||
font-size: 22rpx;
|
font-size: 22rpx;
|
||||||
color: #999;
|
color: #999;
|
||||||
|
|
@ -1074,6 +1125,15 @@
|
||||||
margin-bottom: 10rpx;
|
margin-bottom: 10rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.estimate-tip {
|
||||||
|
font-size: 22rpx;
|
||||||
|
color: #ff8a00;
|
||||||
|
background-color: #fff8f0;
|
||||||
|
padding: 8rpx 16rpx;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
margin-top: 16rpx;
|
||||||
|
}
|
||||||
|
|
||||||
/* 优惠券 */
|
/* 优惠券 */
|
||||||
.coupon-section {
|
.coupon-section {
|
||||||
padding: 0 30rpx;
|
padding: 0 30rpx;
|
||||||
|
|
@ -1610,7 +1670,8 @@
|
||||||
|
|
||||||
.copy-btn {
|
.copy-btn {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
background-color: #ffbcba;
|
/* background-color: #ffbcba; */
|
||||||
|
background: linear-gradient(to right, #ff715a, #ff416c);
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
<template>
|
<template>
|
||||||
<view class="container">
|
<view class="container">
|
||||||
<!-- 头部搜索与导航 -->
|
<!-- 头部搜索与导航 -->
|
||||||
|
<!-- :style="{ paddingTop: statusBarHeight + 'px' }" -->
|
||||||
<view class="header">
|
<view class="header">
|
||||||
<view class="status-bar" :style="{ height: statusBarHeight + 'px' }"></view>
|
|
||||||
<view class="search-section">
|
<view class="search-section">
|
||||||
<view class="search-bar-wrap" @click="goSearch">
|
<view class="search-bar-wrap" @click="goSearch">
|
||||||
<text class="search-icon">🔍</text>
|
<text class="search-icon">🔍</text>
|
||||||
|
|
@ -18,8 +18,8 @@
|
||||||
</view>
|
</view>
|
||||||
</scroll-view>
|
</scroll-view>
|
||||||
</view>
|
</view>
|
||||||
|
<!-- :style="{ paddingTop: headerHeight + 'px' }" -->
|
||||||
<scroll-view scroll-y class="main-content" :show-scrollbar="false" :scroll-into-view="scrollTarget" scroll-with-animation>
|
<view class="main-content" :style="{ paddingTop: '120px' }">
|
||||||
<!-- 轮播图区域 -->
|
<!-- 轮播图区域 -->
|
||||||
<view class="banner-box" id="top-section">
|
<view class="banner-box" id="top-section">
|
||||||
<swiper class="swiper" circular autoplay indicator-dots indicator-color="rgba(255,255,255,0.5)" indicator-active-color="#ffffff">
|
<swiper class="swiper" circular autoplay indicator-dots indicator-color="rgba(255,255,255,0.5)" indicator-active-color="#ffffff">
|
||||||
|
|
@ -240,13 +240,19 @@
|
||||||
</view>
|
</view>
|
||||||
<view class="g-coupon-tag">券 {{ goods.couponValue }}元</view>
|
<view class="g-coupon-tag">券 {{ goods.couponValue }}元</view>
|
||||||
</view>
|
</view>
|
||||||
|
<view class="price-coupon" v-if="!$store.state.isThirdParty">
|
||||||
|
<view class="coupon-left">
|
||||||
|
<text class="coupon-tip">预估消费券</text>
|
||||||
|
<text class="coupon-val">{{ $estimateCoupon(goods.tkmoney) }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
<view class="goods-sales">已售 {{ goods.sales }} 件</view>
|
<view class="goods-sales">已售 {{ goods.sales }} 件</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="loading-more">-- 到底啦 --</view>
|
<view class="loading-more">-- 到底啦 --</view>
|
||||||
</scroll-view>
|
</view>
|
||||||
|
|
||||||
<!-- 通用底部 Tab 栏 -->
|
<!-- 通用底部 Tab 栏 -->
|
||||||
<bottom-nav :activeTab="0"></bottom-nav>
|
<bottom-nav :activeTab="0"></bottom-nav>
|
||||||
|
|
@ -255,6 +261,7 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import BottomNav from '@/components/bottom-nav/bottom-nav.vue';
|
import BottomNav from '@/components/bottom-nav/bottom-nav.vue';
|
||||||
|
import http from '@/request/request.js';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
|
|
@ -266,7 +273,6 @@
|
||||||
activeBottomTab: 0,
|
activeBottomTab: 0,
|
||||||
currentTab: 0,
|
currentTab: 0,
|
||||||
navList: [{ name: '首页', cat_id: 0 }, { name: '推荐', cat_id: -1 }],
|
navList: [{ name: '首页', cat_id: 0 }, { name: '推荐', cat_id: -1 }],
|
||||||
scrollTarget: '',
|
|
||||||
banners: [
|
banners: [
|
||||||
'https://images.unsplash.com/photo-1483985988355-763728e1935b?w=800&q=80',
|
'https://images.unsplash.com/photo-1483985988355-763728e1935b?w=800&q=80',
|
||||||
'https://images.unsplash.com/photo-1542291026-7eec264c27ff?w=800&q=80',
|
'https://images.unsplash.com/photo-1542291026-7eec264c27ff?w=800&q=80',
|
||||||
|
|
@ -313,6 +319,17 @@
|
||||||
this.getWorthBuyLists();
|
this.getWorthBuyLists();
|
||||||
this.getBrandSaleList();
|
this.getBrandSaleList();
|
||||||
},
|
},
|
||||||
|
onPullDownRefresh() {
|
||||||
|
this.getIndexData();
|
||||||
|
this.getCategoryList();
|
||||||
|
this.getNoticeList();
|
||||||
|
this.getGoodsList();
|
||||||
|
this.getWorthBuyLists();
|
||||||
|
this.getBrandSaleList();
|
||||||
|
setTimeout(() => {
|
||||||
|
uni.stopPullDownRefresh();
|
||||||
|
}, 600);
|
||||||
|
},
|
||||||
computed: {
|
computed: {
|
||||||
gridMenuPages() {
|
gridMenuPages() {
|
||||||
const pages = [];
|
const pages = [];
|
||||||
|
|
@ -344,11 +361,8 @@
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
getIndexData() {
|
getIndexData() {
|
||||||
uni.request({
|
http.get('https://api.cmspro.haodanku.com/index/index?cid=YsWZ21tx').then(res => {
|
||||||
url: 'https://api.cmspro.haodanku.com/index/index?cid=YsWZ21tx',
|
const d = res.data;
|
||||||
success: (res) => {
|
|
||||||
if (res.data && res.data.code === 200 && res.data.data) {
|
|
||||||
const d = res.data.data;
|
|
||||||
if (Array.isArray(d.navs)) {
|
if (Array.isArray(d.navs)) {
|
||||||
this.menus = d.navs;
|
this.menus = d.navs;
|
||||||
}
|
}
|
||||||
|
|
@ -360,46 +374,40 @@
|
||||||
if (Array.isArray(d.tile_long) && d.tile_long.length > 0) {
|
if (Array.isArray(d.tile_long) && d.tile_long.length > 0) {
|
||||||
this.adList = d.tile_long.map(t => t.img).filter(Boolean);
|
this.adList = d.tile_long.map(t => t.img).filter(Boolean);
|
||||||
}
|
}
|
||||||
}
|
}).catch(err => {
|
||||||
}
|
console.error('获取首页数据失败:', err.message);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
goToDetail(id) {
|
goToDetail(id) {
|
||||||
console.log('正在跳转到详情页,商品ID:', id);
|
console.log('正在跳转到详情页,商品ID:', id);
|
||||||
uni.navigateTo({
|
uni.navigateTo({
|
||||||
url: `/pages/detail/detail?id=${id}`
|
url: `/pages/detail/detail?id=${id}`
|
||||||
|
// url: `/pages/activity/activity`
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
getCategoryList() {
|
getCategoryList() {
|
||||||
uni.request({
|
http.get('https://api.cmspro.haodanku.com/index/category?cid=YsWZ21tx').then(res => {
|
||||||
url: 'https://api.cmspro.haodanku.com/index/category?cid=YsWZ21tx',
|
if (res.data.category) {
|
||||||
success: (res) => {
|
this.navList = [{ name: '首页', cat_id: 0 }, { name: '推荐', cat_id: -1 }, ...res.data.category];
|
||||||
if (res.data && res.data.code === 200 && res.data.data.category) {
|
|
||||||
this.navList = [{ name: '首页', cat_id: 0 }, { name: '推荐', cat_id: -1 }, ...res.data.data.category];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}).catch(err => {
|
||||||
|
console.error('获取分类失败:', err.message);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
getNoticeList() {
|
getNoticeList() {
|
||||||
uni.request({
|
http.get('https://api.cmspro.haodanku.com/msg/getMsgs?cid=YsWZ21tx').then(res => {
|
||||||
url: 'https://api.cmspro.haodanku.com/msg/getMsgs?cid=YsWZ21tx',
|
this.noticeList = res.data.msgs;
|
||||||
success: (res) => {
|
if (res.data.num) {
|
||||||
if (res.data && res.data.code === 200) {
|
this.qiangNum = res.data.num;
|
||||||
this.noticeList = res.data.data.msgs;
|
|
||||||
if (res.data.data.num) {
|
|
||||||
this.qiangNum = res.data.data.num;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}).catch(err => {
|
||||||
|
console.error('获取通知失败:', err.message);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
getGoodsList() {
|
getGoodsList() {
|
||||||
uni.request({
|
http.get('https://api.cmspro.haodanku.com/recommend/getRecommend?page_size=200&page=1&type=2&cid=YsWZ21tx').then(res => {
|
||||||
url: 'https://api.cmspro.haodanku.com/recommend/getRecommend?page_size=200&page=1&type=2&cid=YsWZ21tx',
|
const list = res.data.recommends.map(item => {
|
||||||
success: (res) => {
|
|
||||||
if (res.data && res.data.code === 200) {
|
|
||||||
const list = res.data.data.recommends.map(item => {
|
|
||||||
// 格式化销量
|
// 格式化销量
|
||||||
let salesStr = item.itemsale;
|
let salesStr = item.itemsale;
|
||||||
if (item.itemsale >= 10000) {
|
if (item.itemsale >= 10000) {
|
||||||
|
|
@ -415,19 +423,17 @@
|
||||||
sales: salesStr,
|
sales: salesStr,
|
||||||
brandTag: item.brand_name,
|
brandTag: item.brand_name,
|
||||||
lowestTag: item.label && item.label.length > 0 ? item.label[0] : '',
|
lowestTag: item.label && item.label.length > 0 ? item.label[0] : '',
|
||||||
shopType: item.shoptype === 'B' ? '天猫' : '淘宝'
|
shopType: item.shoptype === 'B' ? '天猫' : '淘宝',
|
||||||
|
tkmoney: item.tkmoney
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
this.goodsList = list;
|
this.goodsList = list;
|
||||||
}
|
}).catch(err => {
|
||||||
}
|
console.error('获取商品列表失败:', err.message);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
getWorthBuyLists() {
|
getWorthBuyLists() {
|
||||||
uni.request({
|
http.get('https://api.cmspro.haodanku.com/index/deserveLists?cid=YsWZ21tx').then(res => {
|
||||||
url: 'https://api.cmspro.haodanku.com/index/deserveLists?cid=YsWZ21tx',
|
|
||||||
success: (res) => {
|
|
||||||
if (res.data && res.data.code === 200) {
|
|
||||||
const formatData = (list) => {
|
const formatData = (list) => {
|
||||||
return list.map(item => ({
|
return list.map(item => ({
|
||||||
id: item.id,
|
id: item.id,
|
||||||
|
|
@ -443,19 +449,17 @@
|
||||||
shopType: item.shoptype === 'B' ? '天猫' : '淘宝'
|
shopType: item.shoptype === 'B' ? '天猫' : '淘宝'
|
||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
this.deserveList = formatData(res.data.data.deserve_lists);
|
this.deserveList = formatData(res.data.deserve_lists);
|
||||||
this.nineList = formatData(res.data.data.nine_lists);
|
this.nineList = formatData(res.data.nine_lists);
|
||||||
this.nineteenList = formatData(res.data.data.nineteen_lists);
|
this.nineteenList = formatData(res.data.nineteen_lists);
|
||||||
}
|
}).catch(err => {
|
||||||
}
|
console.error('获取值得买列表失败:', err.message);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
getBrandSaleList() {
|
getBrandSaleList() {
|
||||||
uni.request({
|
http.get('https://api.cmspro.haodanku.com/brandItem/choiceness?cid=YsWZ21tx').then(res => {
|
||||||
url: 'https://api.cmspro.haodanku.com/brandItem/choiceness?cid=YsWZ21tx',
|
if (res.data.brand_prefecture) {
|
||||||
success: (res) => {
|
this.brandSaleShops = res.data.brand_prefecture.map(shop => ({
|
||||||
if (res.data && res.data.code === 200 && res.data.data.brand_prefecture) {
|
|
||||||
this.brandSaleShops = res.data.data.brand_prefecture.map(shop => ({
|
|
||||||
bg: shop.backimage || 'https://images.unsplash.com/photo-1556228720-195a672e8a03?w=800&q=80',
|
bg: shop.backimage || 'https://images.unsplash.com/photo-1556228720-195a672e8a03?w=800&q=80',
|
||||||
logo: shop.brand_logo || 'https://images.unsplash.com/photo-1560155016-bd4879ae8f21?w=200&q=80',
|
logo: shop.brand_logo || 'https://images.unsplash.com/photo-1560155016-bd4879ae8f21?w=200&q=80',
|
||||||
name: shop.fq_brand_name || '大牌特卖',
|
name: shop.fq_brand_name || '大牌特卖',
|
||||||
|
|
@ -472,7 +476,8 @@
|
||||||
}))
|
}))
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
}
|
}).catch(err => {
|
||||||
|
console.error('获取品牌特卖失败:', err.message);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
switchTab(index) {
|
switchTab(index) {
|
||||||
|
|
@ -489,13 +494,9 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
this.currentTab = index;
|
this.currentTab = index;
|
||||||
this.scrollTarget = ''; // 重置目标,确保下次点击仍能触发滚动
|
uni.pageScrollTo({
|
||||||
this.$nextTick(() => {
|
selector: tabName === '推荐' ? '#goods-list-section' : '#top-section',
|
||||||
if (tabName === '推荐') {
|
duration: 300
|
||||||
this.scrollTarget = 'goods-list-section';
|
|
||||||
} else if (tabName === '首页') {
|
|
||||||
this.scrollTarget = 'top-section';
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
goToChoiceness() {
|
goToChoiceness() {
|
||||||
|
|
@ -515,14 +516,15 @@
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.container {
|
.container {
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
height: 100vh;
|
|
||||||
background-color: #f5f6f8;
|
background-color: #f5f6f8;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 头部区域 */
|
/* 头部区域 */
|
||||||
.header {
|
.header {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
background: #ffffff;
|
background: #ffffff;
|
||||||
padding-bottom: 10rpx;
|
padding-bottom: 10rpx;
|
||||||
z-index: 100;
|
z-index: 100;
|
||||||
|
|
@ -620,13 +622,12 @@
|
||||||
|
|
||||||
/* 主体滚动区域 */
|
/* 主体滚动区域 */
|
||||||
.main-content {
|
.main-content {
|
||||||
flex: 1;
|
min-height: 100vh;
|
||||||
overflow-y: auto;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 轮播图 */
|
/* 轮播图 */
|
||||||
.banner-box {
|
.banner-box {
|
||||||
padding: 20rpx 30rpx 10rpx;
|
padding: 0 30rpx 10rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.swiper {
|
.swiper {
|
||||||
|
|
@ -1625,6 +1626,23 @@
|
||||||
border-radius: 4rpx;
|
border-radius: 4rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.price-coupon {
|
||||||
|
display: flex;
|
||||||
|
align-items: baseline;
|
||||||
|
margin-top: 4rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.coupon-tip {
|
||||||
|
font-size: 22rpx;
|
||||||
|
color: #ff8a00;
|
||||||
|
}
|
||||||
|
|
||||||
|
.coupon-val {
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #ff8a00;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
.goods-sales {
|
.goods-sales {
|
||||||
font-size: 24rpx;
|
font-size: 24rpx;
|
||||||
color: #999;
|
color: #999;
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
<view class="rank-container">
|
<view class="rank-container">
|
||||||
<!-- 沉浸式渐变头部 -->
|
<!-- 沉浸式渐变头部 -->
|
||||||
<view class="rank-header">
|
<view class="rank-header">
|
||||||
<view class="status-bar" :style="{ height: statusBarHeight + 'px' }"></view>
|
<!-- <view class="status-bar" :style="{ height: statusBarHeight + 'px' }"></view> -->
|
||||||
<!-- 榜单类型切换 -->
|
<!-- 榜单类型切换 -->
|
||||||
<view class="rank-main-tabs">
|
<view class="rank-main-tabs">
|
||||||
<view class="main-tab" :class="{ 'active': rankType === 1 }" @click="switchRankType(1)">
|
<view class="main-tab" :class="{ 'active': rankType === 1 }" @click="switchRankType(1)">
|
||||||
|
|
@ -30,7 +30,7 @@
|
||||||
|
|
||||||
<scroll-view scroll-y class="rank-content" @scrolltolower="loadMore">
|
<scroll-view scroll-y class="rank-content" @scrolltolower="loadMore">
|
||||||
<!-- 顶部背景延伸区 (仅作为背景块,不包裹卡片) -->
|
<!-- 顶部背景延伸区 (仅作为背景块,不包裹卡片) -->
|
||||||
<view class="rank-bg-header" :style="{ height: (statusBarHeight + 110) + 'px' }"></view>
|
<view class="rank-bg-header" :style="{ height: '110px' }"></view>
|
||||||
|
|
||||||
<!-- Top 1-3 领奖台布局 (参考 shop-sy2 样式) -->
|
<!-- Top 1-3 领奖台布局 (参考 shop-sy2 样式) -->
|
||||||
<view class="top-three-wrap" v-if="goodsList.length >= 3">
|
<view class="top-three-wrap" v-if="goodsList.length >= 3">
|
||||||
|
|
@ -147,6 +147,7 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import BottomNav from '@/components/bottom-nav/bottom-nav.vue';
|
import BottomNav from '@/components/bottom-nav/bottom-nav.vue';
|
||||||
|
import http from '@/request/request.js';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
|
|
@ -194,18 +195,16 @@
|
||||||
if (this.loading) return;
|
if (this.loading) return;
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
|
|
||||||
uni.request({
|
http.get('https://api.cmspro.haodanku.com/ranking/lists', {
|
||||||
url: 'https://api.cmspro.haodanku.com/ranking/lists',
|
|
||||||
data: {
|
|
||||||
type: this.rankType,
|
type: this.rankType,
|
||||||
category_id: this.currentCateId,
|
category_id: this.currentCateId,
|
||||||
page: this.page,
|
page: this.page,
|
||||||
page_size: 60,
|
page_size: 60,
|
||||||
cid: 'YsWZ21tx'
|
cid: 'YsWZ21tx'
|
||||||
},
|
})
|
||||||
success: (res) => {
|
.then(res => {
|
||||||
if (res.data && res.data.code === 200 && res.data.data && res.data.data.list_item && res.data.data.list_item.list) {
|
if (res.data && res.data.list_item && res.data.list_item.list) {
|
||||||
const list = res.data.data.list_item.list.map(item => {
|
const list = res.data.list_item.list.map(item => {
|
||||||
// 处理标题
|
// 处理标题
|
||||||
let title = item.itemshorttitle || item.itemtitle;
|
let title = item.itemshorttitle || item.itemtitle;
|
||||||
if (title.length > 18) {
|
if (title.length > 18) {
|
||||||
|
|
@ -221,7 +220,7 @@
|
||||||
let itemSale2Str = itemSale2 >= 10000 ? (itemSale2 / 10000).toFixed(1) + '万' : itemSale2;
|
let itemSale2Str = itemSale2 >= 10000 ? (itemSale2 / 10000).toFixed(1) + '万' : itemSale2;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: item.itemid,
|
id: item.id,
|
||||||
image: item.itempic,
|
image: item.itempic,
|
||||||
title: title,
|
title: title,
|
||||||
finalPrice: item.itemendprice,
|
finalPrice: item.itemendprice,
|
||||||
|
|
@ -235,10 +234,9 @@
|
||||||
});
|
});
|
||||||
this.goodsList = [...this.goodsList, ...list];
|
this.goodsList = [...this.goodsList, ...list];
|
||||||
}
|
}
|
||||||
},
|
})
|
||||||
complete: () => {
|
.finally(() => {
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
}
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
loadMore() {
|
loadMore() {
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
<view class="save-money-container">
|
<view class="save-money-container">
|
||||||
<!-- 沉浸式状态栏与高定自定义顶部导航条 -->
|
<!-- 沉浸式状态栏与高定自定义顶部导航条 -->
|
||||||
<view class="custom-nav-bar">
|
<view class="custom-nav-bar">
|
||||||
<view class="status-bar" :style="{ height: statusBarHeight + 'px' }"></view>
|
<!-- <view class="status-bar" :style="{ height: statusBarHeight + 'px' }"></view> -->
|
||||||
<view class="nav-header">
|
<view class="nav-header">
|
||||||
<view class="nav-back" @click="goBack">
|
<view class="nav-back" @click="goBack">
|
||||||
<text class="back-arrow">〈</text>
|
<text class="back-arrow">〈</text>
|
||||||
|
|
@ -13,7 +13,7 @@
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 占位符防止图片被顶部悬浮遮挡 -->
|
<!-- 占位符防止图片被顶部悬浮遮挡 -->
|
||||||
<view class="nav-placeholder" :style="{ height: (statusBarHeight + 44) + 'px' }"></view>
|
<view class="nav-placeholder" :style="{ height: '44px' }"></view>
|
||||||
|
|
||||||
<!-- 核心内容区:无缝连接的两张全景自适应介绍照片 -->
|
<!-- 核心内容区:无缝连接的两张全景自适应介绍照片 -->
|
||||||
<view class="photo-flow">
|
<view class="photo-flow">
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
<view class="search-container">
|
<view class="search-container">
|
||||||
<!-- 头部搜索栏 -->
|
<!-- 头部搜索栏 -->
|
||||||
<view class="search-header">
|
<view class="search-header">
|
||||||
<view class="status-bar" :style="{ height: statusBarHeight + 'px' }"></view>
|
<!-- <view class="status-bar" :style="{ height: statusBarHeight + 'px' }"></view> -->
|
||||||
<view class="header-main">
|
<view class="header-main">
|
||||||
<view class="back-btn" @click="goBack" hover-class="back-btn-hover">
|
<view class="back-btn" @click="goBack" hover-class="back-btn-hover">
|
||||||
<text class="back-arrow">‹</text>
|
<text class="back-arrow">‹</text>
|
||||||
|
|
@ -24,7 +24,7 @@
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<scroll-view scroll-y class="search-body" :style="{ paddingTop: (statusBarHeight + 96) + 'px' }"
|
<scroll-view scroll-y class="search-body" :style="{ paddingTop: '96px' }"
|
||||||
@scrolltolower="onScrollBottom">
|
@scrolltolower="onScrollBottom">
|
||||||
<!-- 搜索结果列表 -->
|
<!-- 搜索结果列表 -->
|
||||||
<block v-if="isSearching">
|
<block v-if="isSearching">
|
||||||
|
|
@ -73,6 +73,12 @@
|
||||||
<text class="coupon-txt">{{ goods.couponValue }}元</text>
|
<text class="coupon-txt">{{ goods.couponValue }}元</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
<view class="price-coupon" v-if="!$store.state.isThirdParty">
|
||||||
|
<view class="coupon-left">
|
||||||
|
<text class="coupon-tip">预估消费券</text>
|
||||||
|
<text class="coupon-val">{{ $estimateCoupon(goods.tkmoney) }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="goods-bottom-info">
|
<view class="goods-bottom-info">
|
||||||
<text class="sales">已售{{ goods.sales }}件</text>
|
<text class="sales">已售{{ goods.sales }}件</text>
|
||||||
|
|
@ -174,6 +180,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import http from '@/request/request.js';
|
||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
|
@ -250,22 +257,18 @@
|
||||||
uni.setStorageSync('search_history', JSON.stringify(list));
|
uni.setStorageSync('search_history', JSON.stringify(list));
|
||||||
},
|
},
|
||||||
getRankingList() {
|
getRankingList() {
|
||||||
uni.request({
|
http.get('https://api.cmspro.haodanku.com/search/searchRankingList?cid=YsWZ21tx')
|
||||||
url: 'https://api.cmspro.haodanku.com/search/searchRankingList?cid=YsWZ21tx',
|
.then(res => {
|
||||||
success: (res) => {
|
if (res.data && res.data.ranking_list) {
|
||||||
if (res.data && res.data.code === 200 && res.data.data.ranking_list) {
|
this.rankingList = res.data.ranking_list.slice(0, 10);
|
||||||
this.rankingList = res.data.data.ranking_list.slice(0, 10);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
getHotThemes() {
|
getHotThemes() {
|
||||||
uni.request({
|
http.get('https://api.cmspro.haodanku.com/index/hotTheme?cid=YsWZ21tx')
|
||||||
url: 'https://api.cmspro.haodanku.com/index/hotTheme?cid=YsWZ21tx',
|
.then(res => {
|
||||||
success: (res) => {
|
if (res.data) {
|
||||||
if (res.data && res.data.code === 200) {
|
this.hotThemes = res.data.slice(0, 10);
|
||||||
this.hotThemes = res.data.data.slice(0, 10);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
@ -317,11 +320,9 @@
|
||||||
if (this.filterIsTmall) shopTypes.push(2);
|
if (this.filterIsTmall) shopTypes.push(2);
|
||||||
if (shopTypes.length > 0) data.shoptype = shopTypes.join(',');
|
if (shopTypes.length > 0) data.shoptype = shopTypes.join(',');
|
||||||
|
|
||||||
uni.request({
|
http.get('https://api.cmspro.haodanku.com/find/allItemList', data)
|
||||||
url: 'https://api.cmspro.haodanku.com/find/allItemList',
|
.then(res => {
|
||||||
data,
|
const body = res.body || {};
|
||||||
success: (res) => {
|
|
||||||
const body = res.data || {};
|
|
||||||
if (body.code === 200 && body.data) {
|
if (body.code === 200 && body.data) {
|
||||||
if (body.data.num_page) this.numPage = body.data.num_page;
|
if (body.data.num_page) this.numPage = body.data.num_page;
|
||||||
const rawList = body.data.item_info || [];
|
const rawList = body.data.item_info || [];
|
||||||
|
|
@ -334,6 +335,7 @@
|
||||||
: (item.itemshorttitle || item.itemtitle),
|
: (item.itemshorttitle || item.itemtitle),
|
||||||
finalPrice: item.itemendprice,
|
finalPrice: item.itemendprice,
|
||||||
couponValue: item.couponmoney || 0,
|
couponValue: item.couponmoney || 0,
|
||||||
|
tkmoney: item.tkmoney || 0,
|
||||||
sales: item.itemsale >= 10000
|
sales: item.itemsale >= 10000
|
||||||
? (item.itemsale / 10000).toFixed(1) + '万'
|
? (item.itemsale / 10000).toFixed(1) + '万'
|
||||||
: item.itemsale,
|
: item.itemsale,
|
||||||
|
|
@ -359,13 +361,13 @@
|
||||||
uni.showToast({ title: body.msg || '搜索失败', icon: 'none' });
|
uni.showToast({ title: body.msg || '搜索失败', icon: 'none' });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
})
|
||||||
fail: () => {
|
.catch(err => {
|
||||||
uni.showToast({ title: '网络错误', icon: 'none' });
|
uni.showToast({ title: err.message || '网络错误', icon: 'none' });
|
||||||
},
|
this.loading = false;
|
||||||
complete: () => {
|
})
|
||||||
|
.finally(() => {
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
}
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
onScrollBottom() {
|
onScrollBottom() {
|
||||||
|
|
@ -976,6 +978,23 @@
|
||||||
padding: 0 8rpx;
|
padding: 0 8rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.price-coupon {
|
||||||
|
display: flex;
|
||||||
|
align-items: baseline;
|
||||||
|
margin-top: 4rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.coupon-tip {
|
||||||
|
font-size: 22rpx;
|
||||||
|
color: #ff8a00;
|
||||||
|
}
|
||||||
|
|
||||||
|
.coupon-val {
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #ff8a00;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
.goods-bottom-info {
|
.goods-bottom-info {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
<view class="choiceness-container">
|
<view class="choiceness-container">
|
||||||
<!-- 自定义沉浸式头部导航 -->
|
<!-- 自定义沉浸式头部导航 -->
|
||||||
<view class="custom-header">
|
<view class="custom-header">
|
||||||
<view class="status-bar" :style="{ height: statusBarHeight + 'px' }"></view>
|
<!-- <view class="status-bar" :style="{ height: statusBarHeight + 'px' }"></view> -->
|
||||||
<view class="nav-bar">
|
<view class="nav-bar">
|
||||||
<view class="nav-left back-area" @click="goBack">
|
<view class="nav-left back-area" @click="goBack">
|
||||||
<text class="back-icon">〈</text>
|
<text class="back-icon">〈</text>
|
||||||
|
|
@ -13,7 +13,7 @@
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 占位符确保内容不被顶部悬浮覆盖 -->
|
<!-- 占位符确保内容不被顶部悬浮覆盖 -->
|
||||||
<view class="header-placeholder" :style="{ height: (statusBarHeight + 44) + 'px' }"></view>
|
<view class="header-placeholder" :style="{ height: '44px' }"></view>
|
||||||
|
|
||||||
<!-- 顶部深紫渐变横向品牌滚动专区 (支持右滑到底无限加载) -->
|
<!-- 顶部深紫渐变横向品牌滚动专区 (支持右滑到底无限加载) -->
|
||||||
<view class="top-scroll-brands-section">
|
<view class="top-scroll-brands-section">
|
||||||
|
|
@ -162,6 +162,8 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import http from '@/request/request.js';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
|
@ -443,11 +445,10 @@
|
||||||
fetchTopBrandsData(isLoadMore = false) {
|
fetchTopBrandsData(isLoadMore = false) {
|
||||||
if (this.topLoading) return;
|
if (this.topLoading) return;
|
||||||
this.topLoading = true;
|
this.topLoading = true;
|
||||||
uni.request({
|
http.get(`https://api.cmspro.haodanku.com/brandItem/getBrands?page=${this.topPage}&page_size=20&cid=YsWZ21tx`)
|
||||||
url: `https://api.cmspro.haodanku.com/brandItem/getBrands?page=${this.topPage}&page_size=20&cid=YsWZ21tx`,
|
.then((res) => {
|
||||||
success: (res) => {
|
if (res.data && Array.isArray(res.data.list)) {
|
||||||
if (res.data && res.data.code === 200 && res.data.data && Array.isArray(res.data.data.list)) {
|
const list = res.data.list.map(item => ({
|
||||||
const list = res.data.data.list.map(item => ({
|
|
||||||
id: item.id,
|
id: item.id,
|
||||||
fq_brand_name: item.fq_brand_name,
|
fq_brand_name: item.fq_brand_name,
|
||||||
brand_logo: item.brand_logo ? item.brand_logo.replace('http://', 'https://') : ''
|
brand_logo: item.brand_logo ? item.brand_logo.replace('http://', 'https://') : ''
|
||||||
|
|
@ -461,7 +462,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const pagination = res.data.data.pagination;
|
const pagination = res.data.pagination;
|
||||||
if (pagination && this.topPage >= pagination.page_count) {
|
if (pagination && this.topPage >= pagination.page_count) {
|
||||||
this.topFinished = true;
|
this.topFinished = true;
|
||||||
} else if (list.length < 20) {
|
} else if (list.length < 20) {
|
||||||
|
|
@ -472,27 +473,25 @@
|
||||||
this.topFinished = true;
|
this.topFinished = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
})
|
||||||
fail: (err) => {
|
.catch((err) => {
|
||||||
console.log('拉取上方滚动品牌接口异常', err);
|
console.log('拉取上方滚动品牌接口异常', err);
|
||||||
if (isLoadMore && this.topPage > 1) {
|
if (isLoadMore && this.topPage > 1) {
|
||||||
this.topPage -= 1;
|
this.topPage -= 1;
|
||||||
}
|
}
|
||||||
},
|
})
|
||||||
complete: () => {
|
.finally(() => {
|
||||||
this.topLoading = false;
|
this.topLoading = false;
|
||||||
}
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
// 全量通用专场初始化及导航字典挂载
|
// 全量通用专场初始化及导航字典挂载
|
||||||
fetchChoicenessData() {
|
fetchChoicenessData() {
|
||||||
uni.request({
|
http.get('https://api.cmspro.haodanku.com/brandItem/choiceness?is_get_category=1&cid=YsWZ21tx')
|
||||||
url: 'https://api.cmspro.haodanku.com/brandItem/choiceness?is_get_category=1&cid=YsWZ21tx',
|
.then((res) => {
|
||||||
success: (res) => {
|
if (res.data) {
|
||||||
if (res.data && res.data.code === 200 && res.data.data) {
|
|
||||||
// 1. 挂载/补充导航字典
|
// 1. 挂载/补充导航字典
|
||||||
if (res.data.data.category && Array.isArray(res.data.data.category)) {
|
if (res.data.category && Array.isArray(res.data.category)) {
|
||||||
const mappedCats = res.data.data.category.map(c => ({
|
const mappedCats = res.data.category.map(c => ({
|
||||||
cat_id: Number(c.cat_id),
|
cat_id: Number(c.cat_id),
|
||||||
cat_name: c.cat_name
|
cat_name: c.cat_name
|
||||||
}));
|
}));
|
||||||
|
|
@ -500,8 +499,8 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. 挂载全景精选海报流
|
// 2. 挂载全景精选海报流
|
||||||
if (res.data.data.brand_prefecture && Array.isArray(res.data.data.brand_prefecture)) {
|
if (res.data.brand_prefecture && Array.isArray(res.data.brand_prefecture)) {
|
||||||
const list = res.data.data.brand_prefecture.map(shop => {
|
const list = res.data.brand_prefecture.map(shop => {
|
||||||
let logoStr = shop.brand_logo ? shop.brand_logo.replace('http://', 'https://') : 'https://cdn-icons-png.flaticon.com/512/882/882730.png';
|
let logoStr = shop.brand_logo ? shop.brand_logo.replace('http://', 'https://') : 'https://cdn-icons-png.flaticon.com/512/882/882730.png';
|
||||||
let bgStr = shop.backimage ? shop.backimage.replace('http://', 'https://') : 'https://images.unsplash.com/photo-1556228720-195a672e8a03?w=800&q=80';
|
let bgStr = shop.backimage ? shop.backimage.replace('http://', 'https://') : 'https://images.unsplash.com/photo-1556228720-195a672e8a03?w=800&q=80';
|
||||||
|
|
||||||
|
|
@ -531,18 +530,16 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
})
|
||||||
fail: (err) => {
|
.catch((err) => {
|
||||||
console.log('拉取线上精选品牌专场分类联动数据失败,采用固定底座显示', err);
|
console.log('拉取线上精选品牌专场分类联动数据失败,采用固定底座显示', err);
|
||||||
}
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
// 接驳 brandCategory 接口分流,内置光影轮盘自动修复缺失背景。取消突兀 Toast 完美适配内嵌底座
|
// 接驳 brandCategory 接口分流,内置光影轮盘自动修复缺失背景。取消突兀 Toast 完美适配内嵌底座
|
||||||
fetchBrandCategoryData(categoryId) {
|
fetchBrandCategoryData(categoryId) {
|
||||||
uni.request({
|
http.get(`https://api.cmspro.haodanku.com/brandItem/brandCategory?category_id=${categoryId}&cid=YsWZ21tx`)
|
||||||
url: `https://api.cmspro.haodanku.com/brandItem/brandCategory?category_id=${categoryId}&cid=YsWZ21tx`,
|
.then((res) => {
|
||||||
success: (res) => {
|
if (res.data && Array.isArray(res.data.brands)) {
|
||||||
if (res.data && res.data.code === 200 && res.data.data && Array.isArray(res.data.data.brands)) {
|
|
||||||
// 预置唯美光影/极简质感图库底座,智能赋能无海报的品牌
|
// 预置唯美光影/极简质感图库底座,智能赋能无海报的品牌
|
||||||
const defaultBanners = [
|
const defaultBanners = [
|
||||||
'https://images.unsplash.com/photo-1556228720-195a672e8a03?w=800&q=80',
|
'https://images.unsplash.com/photo-1556228720-195a672e8a03?w=800&q=80',
|
||||||
|
|
@ -552,7 +549,7 @@
|
||||||
'https://images.unsplash.com/photo-1621939514649-280e2fc8a00w?w=800&q=80'
|
'https://images.unsplash.com/photo-1621939514649-280e2fc8a00w?w=800&q=80'
|
||||||
];
|
];
|
||||||
|
|
||||||
const list = res.data.data.brands.map((b, bIndex) => {
|
const list = res.data.brands.map((b, bIndex) => {
|
||||||
let logoStr = b.brand_logo ? b.brand_logo.replace('http://', 'https://') : 'https://cdn-icons-png.flaticon.com/512/882/882730.png';
|
let logoStr = b.brand_logo ? b.brand_logo.replace('http://', 'https://') : 'https://cdn-icons-png.flaticon.com/512/882/882730.png';
|
||||||
let bgStr = defaultBanners[bIndex % defaultBanners.length];
|
let bgStr = defaultBanners[bIndex % defaultBanners.length];
|
||||||
|
|
||||||
|
|
@ -592,19 +589,17 @@
|
||||||
} else {
|
} else {
|
||||||
console.log('拉取分类数据异常或格式不对');
|
console.log('拉取分类数据异常或格式不对');
|
||||||
}
|
}
|
||||||
},
|
})
|
||||||
fail: (err) => {
|
.catch((err) => {
|
||||||
console.log('调用 brandCategory 接口失败', err);
|
console.log('调用 brandCategory 接口失败', err);
|
||||||
}
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
// 底部品牌热销单品聚合数据流
|
// 底部品牌热销单品聚合数据流
|
||||||
fetchBrandSaleData() {
|
fetchBrandSaleData() {
|
||||||
uni.request({
|
http.get('https://api.cmspro.haodanku.com/brandItem/brandSale?cid=YsWZ21tx')
|
||||||
url: 'https://api.cmspro.haodanku.com/brandItem/brandSale?cid=YsWZ21tx',
|
.then((res) => {
|
||||||
success: (res) => {
|
if (res.data && Array.isArray(res.data.items)) {
|
||||||
if (res.data && res.data.code === 200 && res.data.data && Array.isArray(res.data.data.items)) {
|
const list = res.data.items.map(item => {
|
||||||
const list = res.data.data.items.map(item => {
|
|
||||||
let logoUrl = '';
|
let logoUrl = '';
|
||||||
if (item.brand_info && item.brand_info.brand_logo) {
|
if (item.brand_info && item.brand_info.brand_logo) {
|
||||||
logoUrl = item.brand_info.brand_logo.replace('http://', 'https://');
|
logoUrl = item.brand_info.brand_logo.replace('http://', 'https://');
|
||||||
|
|
@ -640,10 +635,9 @@
|
||||||
this.brandSaleList = list;
|
this.brandSaleList = list;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
})
|
||||||
fail: (err) => {
|
.catch((err) => {
|
||||||
console.log('拉取底部品牌热销接口异常,采用极尽精美的静态底座显示', err);
|
console.log('拉取底部品牌热销接口异常,采用极尽精美的静态底座显示', err);
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
<view class="brand-detail-container">
|
<view class="brand-detail-container">
|
||||||
<!-- 自定义沉浸式头部导航 -->
|
<!-- 自定义沉浸式头部导航 -->
|
||||||
<view class="custom-header" :class="{ 'header-scrolled': scrollY > 50 }">
|
<view class="custom-header" :class="{ 'header-scrolled': scrollY > 50 }">
|
||||||
<view class="status-bar" :style="{ height: statusBarHeight + 'px' }"></view>
|
<!-- <view class="status-bar" :style="{ height: statusBarHeight + 'px' }"></view> -->
|
||||||
<view class="nav-bar">
|
<view class="nav-bar">
|
||||||
<view class="nav-left back-area" @click="goBack">
|
<view class="nav-left back-area" @click="goBack">
|
||||||
<text class="back-icon">〈</text>
|
<text class="back-icon">〈</text>
|
||||||
|
|
@ -22,7 +22,7 @@
|
||||||
<view class="biography-overlay"></view>
|
<view class="biography-overlay"></view>
|
||||||
|
|
||||||
<!-- 品牌身份核心图腾层 -->
|
<!-- 品牌身份核心图腾层 -->
|
||||||
<view class="biography-core" :style="{ paddingTop: (statusBarHeight + 44) + 'px' }">
|
<view class="biography-core" :style="{ paddingTop: '44px' }">
|
||||||
<view class="logo-wrapper">
|
<view class="logo-wrapper">
|
||||||
<image class="brand-logo-img" :src="brandInfo.brand_logo || brandInfo.inside_logo" mode="aspectFit"></image>
|
<image class="brand-logo-img" :src="brandInfo.brand_logo || brandInfo.inside_logo" mode="aspectFit"></image>
|
||||||
</view>
|
</view>
|
||||||
|
|
@ -149,6 +149,8 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import http from '@/request/request.js';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
|
@ -278,21 +280,20 @@
|
||||||
sortParam = this.priceOrder === 'asc' ? 8 : 9;
|
sortParam = this.priceOrder === 'asc' ? 8 : 9;
|
||||||
}
|
}
|
||||||
|
|
||||||
uni.request({
|
http.get(`https://api.cmspro.haodanku.com/brandItem/detail?page=${this.page}&page_size=20&sort=${sortParam}&brand_id=${this.brandId}&cid=YsWZ21tx`)
|
||||||
url: `https://api.cmspro.haodanku.com/brandItem/detail?page=${this.page}&page_size=20&sort=${sortParam}&brand_id=${this.brandId}&cid=YsWZ21tx`,
|
.then((res) => {
|
||||||
success: (res) => {
|
if (res.data) {
|
||||||
if (res.data && res.data.code === 200 && res.data.data) {
|
|
||||||
// 挂载核心品牌自画像
|
// 挂载核心品牌自画像
|
||||||
if (res.data.data.brand_info) {
|
if (res.data.brand_info) {
|
||||||
const info = res.data.data.brand_info;
|
const info = res.data.brand_info;
|
||||||
if (info.brand_logo) info.brand_logo = info.brand_logo.replace('http://', 'https://');
|
if (info.brand_logo) info.brand_logo = info.brand_logo.replace('http://', 'https://');
|
||||||
if (info.inside_logo) info.inside_logo = info.inside_logo.replace('http://', 'https://');
|
if (info.inside_logo) info.inside_logo = info.inside_logo.replace('http://', 'https://');
|
||||||
this.brandInfo = info;
|
this.brandInfo = info;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 挂载分页商品方阵
|
// 挂载分页商品方阵
|
||||||
if (res.data.data.items && Array.isArray(res.data.data.items.list)) {
|
if (res.data.items && Array.isArray(res.data.items.list)) {
|
||||||
const list = res.data.data.items.list.map(goods => {
|
const list = res.data.items.list.map(goods => {
|
||||||
let pic = goods.itempic ? goods.itempic.replace('http://', 'https://') : '';
|
let pic = goods.itempic ? goods.itempic.replace('http://', 'https://') : '';
|
||||||
return {
|
return {
|
||||||
id: goods.id,
|
id: goods.id,
|
||||||
|
|
@ -315,7 +316,7 @@
|
||||||
this.itemList = list;
|
this.itemList = list;
|
||||||
}
|
}
|
||||||
|
|
||||||
const pagination = res.data.data.items.pagination;
|
const pagination = res.data.items.pagination;
|
||||||
if (pagination && this.page >= pagination.page_count) {
|
if (pagination && this.page >= pagination.page_count) {
|
||||||
this.finished = true;
|
this.finished = true;
|
||||||
} else if (list.length < 20) {
|
} else if (list.length < 20) {
|
||||||
|
|
@ -327,15 +328,14 @@
|
||||||
} else {
|
} else {
|
||||||
uni.showToast({ title: '加载专区数据失败', icon: 'none' });
|
uni.showToast({ title: '加载专区数据失败', icon: 'none' });
|
||||||
}
|
}
|
||||||
},
|
})
|
||||||
fail: (err) => {
|
.catch((err) => {
|
||||||
console.log('拉取品牌专页接口错误', err);
|
console.log('拉取品牌专页接口错误', err);
|
||||||
if (isLoadMore && this.page > 1) this.page -= 1;
|
if (isLoadMore && this.page > 1) this.page -= 1;
|
||||||
},
|
})
|
||||||
complete: () => {
|
.finally(() => {
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
if (!isLoadMore) uni.hideLoading();
|
if (!isLoadMore) uni.hideLoading();
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,257 @@
|
||||||
|
/**
|
||||||
|
* uni.request 统一封装
|
||||||
|
*
|
||||||
|
* 设计目标:
|
||||||
|
* 1. 自动兼容项目中所有 API 的成功状态判断(code === 200 / code === 1 / status === 200)
|
||||||
|
* 2. 支持自定义 header、超时、baseURL
|
||||||
|
* 3. Promise 化,调用简单
|
||||||
|
* 4. 支持请求/响应拦截器(便于后续统一注入 token、处理错误等)
|
||||||
|
* 5. 数据提取自动降级:优先取 res.data.data,不存在则取 res.data
|
||||||
|
*/
|
||||||
|
|
||||||
|
class Request {
|
||||||
|
/**
|
||||||
|
* @param {Object} config - 全局默认配置
|
||||||
|
* @param {string} config.baseURL - 基础 URL
|
||||||
|
* @param {number} config.timeout - 超时时间(ms),默认 30000
|
||||||
|
* @param {Object} config.header - 默认请求头
|
||||||
|
*/
|
||||||
|
constructor(config = {}) {
|
||||||
|
this.config = {
|
||||||
|
baseURL: '',
|
||||||
|
timeout: 30000,
|
||||||
|
header: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
...config
|
||||||
|
};
|
||||||
|
this.interceptors = {
|
||||||
|
request: [],
|
||||||
|
response: []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加请求拦截器
|
||||||
|
* @param {Function} fn - (config) => config
|
||||||
|
*/
|
||||||
|
addRequestInterceptor(fn) {
|
||||||
|
if (typeof fn === 'function') {
|
||||||
|
this.interceptors.request.push(fn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加响应拦截器
|
||||||
|
* @param {Function} fn - (response) => response
|
||||||
|
*/
|
||||||
|
addResponseInterceptor(fn) {
|
||||||
|
if (typeof fn === 'function') {
|
||||||
|
this.interceptors.response.push(fn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断响应是否成功
|
||||||
|
*
|
||||||
|
* 兼容项目中实际存在的 4 种响应格式:
|
||||||
|
* 1. CMS API: res.data.code === 200
|
||||||
|
* 2. Auth API: res.statusCode === 200 && (res.data.status === 200 || res.data.code === 1)
|
||||||
|
* 3. Hdk v2 API: res.data.code === 1
|
||||||
|
* 4. 兜底: res.statusCode === 200(无业务状态码时)
|
||||||
|
*
|
||||||
|
* @param {Object} res - uni.request 的 success 回调参数
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
isSuccess(res) {
|
||||||
|
// HTTP 层已失败(如 404/500)
|
||||||
|
if (res.statusCode >= 400) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = res.data;
|
||||||
|
if (!data || typeof data !== 'object') {
|
||||||
|
// 无响应体时,以 HTTP 状态码为准
|
||||||
|
return res.statusCode === 200;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提取业务状态码(兼容 code / status 两种字段名)
|
||||||
|
const code = data.code !== undefined ? Number(data.code)
|
||||||
|
: data.status !== undefined ? Number(data.status)
|
||||||
|
: null;
|
||||||
|
|
||||||
|
if (code !== null && !isNaN(code)) {
|
||||||
|
return code === 200 || code === 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 无业务状态码时,以 HTTP 状态码为准
|
||||||
|
return res.statusCode === 200;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从响应中提取业务数据
|
||||||
|
*
|
||||||
|
* 降级策略:
|
||||||
|
* - 优先取 res.data.data(CMS 标准格式)
|
||||||
|
* - 不存在则取 res.data 本身(直接返回格式)
|
||||||
|
* - 空响应返回 null
|
||||||
|
*
|
||||||
|
* @param {Object} res - uni.request 的 success 回调参数
|
||||||
|
* @returns {any}
|
||||||
|
*/
|
||||||
|
extractData(res) {
|
||||||
|
const data = res.data;
|
||||||
|
if (!data || typeof data !== 'object') {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
return data.data !== undefined ? data.data : data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从响应中提取错误信息
|
||||||
|
* @param {Object} res - uni.request 的 success 回调参数
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
extractError(res) {
|
||||||
|
const data = res.data;
|
||||||
|
if (!data || typeof data !== 'object') {
|
||||||
|
return `请求失败 (HTTP ${res.statusCode})`;
|
||||||
|
}
|
||||||
|
return data.msg || data.message || data.error || `请求失败 (code: ${data.code || data.status})`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 核心请求方法
|
||||||
|
*
|
||||||
|
* @param {Object} options - 请求配置
|
||||||
|
* @param {string} options.url - 请求地址(可相对 baseURL)
|
||||||
|
* @param {string} options.method - 请求方法:GET/POST/PUT/DELETE,默认 GET
|
||||||
|
* @param {Object} options.data - 请求数据
|
||||||
|
* @param {Object} options.header - 自定义请求头(会合并覆盖默认 header,大小写不敏感)
|
||||||
|
* @param {number} options.timeout - 本次请求单独设置的超时时间
|
||||||
|
* @returns {Promise<{success: true, data: any, body: any, raw: Object}>}
|
||||||
|
*/
|
||||||
|
request(options = {}) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
// 1. 合并配置:默认 → 实例配置 → 单次请求配置
|
||||||
|
// header 合并需大小写不敏感去重(如 Content-Type 与 content-type 是同一头)
|
||||||
|
const mergedHeader = {};
|
||||||
|
for (const [k, v] of Object.entries(this.config.header)) {
|
||||||
|
mergedHeader[k.toLowerCase()] = v;
|
||||||
|
}
|
||||||
|
for (const [k, v] of Object.entries(options.header || {})) {
|
||||||
|
mergedHeader[k.toLowerCase()] = v;
|
||||||
|
}
|
||||||
|
let config = {
|
||||||
|
...this.config,
|
||||||
|
...options,
|
||||||
|
header: mergedHeader
|
||||||
|
};
|
||||||
|
|
||||||
|
// 2. 拼接 baseURL(如果 url 不是完整 HTTP 地址)
|
||||||
|
if (config.baseURL && config.url && !config.url.startsWith('http')) {
|
||||||
|
config.url = config.baseURL.replace(/\/$/, '') + '/' + config.url.replace(/^\//, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 执行请求拦截器
|
||||||
|
try {
|
||||||
|
for (const fn of this.interceptors.request) {
|
||||||
|
config = fn(config) || config;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
reject({ success: false, message: err.message || '请求拦截器异常', code: -2 });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 发起请求
|
||||||
|
uni.request({
|
||||||
|
url: config.url,
|
||||||
|
method: (config.method || 'GET').toUpperCase(),
|
||||||
|
data: config.data,
|
||||||
|
header: config.header,
|
||||||
|
timeout: config.timeout,
|
||||||
|
success: (res) => {
|
||||||
|
// 5. 执行响应拦截器
|
||||||
|
try {
|
||||||
|
for (const fn of this.interceptors.response) {
|
||||||
|
res = fn(res) || res;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
reject({ success: false, message: err.message || '响应拦截器异常', code: -3, raw: res });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6. 判断成功/失败
|
||||||
|
if (this.isSuccess(res)) {
|
||||||
|
resolve({
|
||||||
|
success: true,
|
||||||
|
data: this.extractData(res),
|
||||||
|
body: res.data,
|
||||||
|
raw: res
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
reject({
|
||||||
|
success: false,
|
||||||
|
message: this.extractError(res),
|
||||||
|
code: res.data?.code ?? res.data?.status ?? res.statusCode,
|
||||||
|
raw: res
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fail: (err) => {
|
||||||
|
reject({
|
||||||
|
success: false,
|
||||||
|
message: err.errMsg || '网络请求失败,请检查网络',
|
||||||
|
code: -1,
|
||||||
|
raw: err
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GET 请求快捷方法
|
||||||
|
* @param {string} url
|
||||||
|
* @param {Object} data
|
||||||
|
* @param {Object} options
|
||||||
|
*/
|
||||||
|
get(url, data, options = {}) {
|
||||||
|
return this.request({ url, data, method: 'GET', ...options });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* POST 请求快捷方法
|
||||||
|
* @param {string} url
|
||||||
|
* @param {Object} data
|
||||||
|
* @param {Object} options
|
||||||
|
*/
|
||||||
|
post(url, data, options = {}) {
|
||||||
|
return this.request({ url, data, method: 'POST', ...options });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PUT 请求快捷方法
|
||||||
|
*/
|
||||||
|
put(url, data, options = {}) {
|
||||||
|
return this.request({ url, data, method: 'PUT', ...options });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DELETE 请求快捷方法
|
||||||
|
*/
|
||||||
|
delete(url, data, options = {}) {
|
||||||
|
return this.request({ url, data, method: 'DELETE', ...options });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// 创建全局默认实例
|
||||||
|
// ============================================================
|
||||||
|
const http = new Request();
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// 导出
|
||||||
|
// ============================================================
|
||||||
|
export default http;
|
||||||
|
export { Request };
|
||||||
|
|
@ -0,0 +1,257 @@
|
||||||
|
/**
|
||||||
|
* uni.request 统一封装
|
||||||
|
*
|
||||||
|
* 设计目标:
|
||||||
|
* 1. 自动兼容项目中所有 API 的成功状态判断(code === 200 / code === 1 / status === 200)
|
||||||
|
* 2. 支持自定义 header、超时、baseURL
|
||||||
|
* 3. Promise 化,调用简单
|
||||||
|
* 4. 支持请求/响应拦截器(便于后续统一注入 token、处理错误等)
|
||||||
|
* 5. 数据提取自动降级:优先取 res.data.data,不存在则取 res.data
|
||||||
|
*/
|
||||||
|
|
||||||
|
class Request {
|
||||||
|
/**
|
||||||
|
* @param {Object} config - 全局默认配置
|
||||||
|
* @param {string} config.baseURL - 基础 URL
|
||||||
|
* @param {number} config.timeout - 超时时间(ms),默认 30000
|
||||||
|
* @param {Object} config.header - 默认请求头
|
||||||
|
*/
|
||||||
|
constructor(config = {}) {
|
||||||
|
this.config = {
|
||||||
|
baseURL: '',
|
||||||
|
timeout: 30000,
|
||||||
|
header: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
...config
|
||||||
|
};
|
||||||
|
this.interceptors = {
|
||||||
|
request: [],
|
||||||
|
response: []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加请求拦截器
|
||||||
|
* @param {Function} fn - (config) => config
|
||||||
|
*/
|
||||||
|
addRequestInterceptor(fn) {
|
||||||
|
if (typeof fn === 'function') {
|
||||||
|
this.interceptors.request.push(fn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加响应拦截器
|
||||||
|
* @param {Function} fn - (response) => response
|
||||||
|
*/
|
||||||
|
addResponseInterceptor(fn) {
|
||||||
|
if (typeof fn === 'function') {
|
||||||
|
this.interceptors.response.push(fn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断响应是否成功
|
||||||
|
*
|
||||||
|
* 兼容项目中实际存在的 4 种响应格式:
|
||||||
|
* 1. CMS API: res.data.code === 200
|
||||||
|
* 2. Auth API: res.statusCode === 200 && (res.data.status === 200 || res.data.code === 1)
|
||||||
|
* 3. Hdk v2 API: res.data.code === 1
|
||||||
|
* 4. 兜底: res.statusCode === 200(无业务状态码时)
|
||||||
|
*
|
||||||
|
* @param {Object} res - uni.request 的 success 回调参数
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
isSuccess(res) {
|
||||||
|
// HTTP 层已失败(如 404/500)
|
||||||
|
if (res.statusCode >= 400) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = res.data;
|
||||||
|
if (!data || typeof data !== 'object') {
|
||||||
|
// 无响应体时,以 HTTP 状态码为准
|
||||||
|
return res.statusCode === 200;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提取业务状态码(兼容 code / status 两种字段名)
|
||||||
|
const code = data.code !== undefined ? Number(data.code)
|
||||||
|
: data.status !== undefined ? Number(data.status)
|
||||||
|
: null;
|
||||||
|
|
||||||
|
if (code !== null && !isNaN(code)) {
|
||||||
|
return code === 200 || code === 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 无业务状态码时,以 HTTP 状态码为准
|
||||||
|
return res.statusCode === 200;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从响应中提取业务数据
|
||||||
|
*
|
||||||
|
* 降级策略:
|
||||||
|
* - 优先取 res.data.data(CMS 标准格式)
|
||||||
|
* - 不存在则取 res.data 本身(直接返回格式)
|
||||||
|
* - 空响应返回 null
|
||||||
|
*
|
||||||
|
* @param {Object} res - uni.request 的 success 回调参数
|
||||||
|
* @returns {any}
|
||||||
|
*/
|
||||||
|
extractData(res) {
|
||||||
|
const data = res.data;
|
||||||
|
if (!data || typeof data !== 'object') {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
return data.data !== undefined ? data.data : data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从响应中提取错误信息
|
||||||
|
* @param {Object} res - uni.request 的 success 回调参数
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
extractError(res) {
|
||||||
|
const data = res.data;
|
||||||
|
if (!data || typeof data !== 'object') {
|
||||||
|
return `请求失败 (HTTP ${res.statusCode})`;
|
||||||
|
}
|
||||||
|
return data.msg || data.message || data.error || `请求失败 (code: ${data.code || data.status})`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 核心请求方法
|
||||||
|
*
|
||||||
|
* @param {Object} options - 请求配置
|
||||||
|
* @param {string} options.url - 请求地址(可相对 baseURL)
|
||||||
|
* @param {string} options.method - 请求方法:GET/POST/PUT/DELETE,默认 GET
|
||||||
|
* @param {Object} options.data - 请求数据
|
||||||
|
* @param {Object} options.header - 自定义请求头(会合并覆盖默认 header,大小写不敏感)
|
||||||
|
* @param {number} options.timeout - 本次请求单独设置的超时时间
|
||||||
|
* @returns {Promise<{success: true, data: any, body: any, raw: Object}>}
|
||||||
|
*/
|
||||||
|
request(options = {}) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
// 1. 合并配置:默认 → 实例配置 → 单次请求配置
|
||||||
|
// header 合并需大小写不敏感去重(如 Content-Type 与 content-type 是同一头)
|
||||||
|
const mergedHeader = {};
|
||||||
|
for (const [k, v] of Object.entries(this.config.header)) {
|
||||||
|
mergedHeader[k.toLowerCase()] = v;
|
||||||
|
}
|
||||||
|
for (const [k, v] of Object.entries(options.header || {})) {
|
||||||
|
mergedHeader[k.toLowerCase()] = v;
|
||||||
|
}
|
||||||
|
let config = {
|
||||||
|
...this.config,
|
||||||
|
...options,
|
||||||
|
header: mergedHeader
|
||||||
|
};
|
||||||
|
|
||||||
|
// 2. 拼接 baseURL(如果 url 不是完整 HTTP 地址)
|
||||||
|
if (config.baseURL && config.url && !config.url.startsWith('http')) {
|
||||||
|
config.url = config.baseURL.replace(/\/$/, '') + '/' + config.url.replace(/^\//, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 执行请求拦截器
|
||||||
|
try {
|
||||||
|
for (const fn of this.interceptors.request) {
|
||||||
|
config = fn(config) || config;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
reject({ success: false, message: err.message || '请求拦截器异常', code: -2 });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 发起请求
|
||||||
|
uni.request({
|
||||||
|
url: config.url,
|
||||||
|
method: (config.method || 'GET').toUpperCase(),
|
||||||
|
data: config.data,
|
||||||
|
header: config.header,
|
||||||
|
timeout: config.timeout,
|
||||||
|
success: (res) => {
|
||||||
|
// 5. 执行响应拦截器
|
||||||
|
try {
|
||||||
|
for (const fn of this.interceptors.response) {
|
||||||
|
res = fn(res) || res;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
reject({ success: false, message: err.message || '响应拦截器异常', code: -3, raw: res });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6. 判断成功/失败
|
||||||
|
if (this.isSuccess(res)) {
|
||||||
|
resolve({
|
||||||
|
success: true,
|
||||||
|
data: this.extractData(res),
|
||||||
|
body: res.data,
|
||||||
|
raw: res
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
reject({
|
||||||
|
success: false,
|
||||||
|
message: this.extractError(res),
|
||||||
|
code: res.data?.code ?? res.data?.status ?? res.statusCode,
|
||||||
|
raw: res
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fail: (err) => {
|
||||||
|
reject({
|
||||||
|
success: false,
|
||||||
|
message: err.errMsg || '网络请求失败,请检查网络',
|
||||||
|
code: -1,
|
||||||
|
raw: err
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GET 请求快捷方法
|
||||||
|
* @param {string} url
|
||||||
|
* @param {Object} data
|
||||||
|
* @param {Object} options
|
||||||
|
*/
|
||||||
|
get(url, data, options = {}) {
|
||||||
|
return this.request({ url, data, method: 'GET', ...options });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* POST 请求快捷方法
|
||||||
|
* @param {string} url
|
||||||
|
* @param {Object} data
|
||||||
|
* @param {Object} options
|
||||||
|
*/
|
||||||
|
post(url, data, options = {}) {
|
||||||
|
return this.request({ url, data, method: 'POST', ...options });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PUT 请求快捷方法
|
||||||
|
*/
|
||||||
|
put(url, data, options = {}) {
|
||||||
|
return this.request({ url, data, method: 'PUT', ...options });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DELETE 请求快捷方法
|
||||||
|
*/
|
||||||
|
delete(url, data, options = {}) {
|
||||||
|
return this.request({ url, data, method: 'DELETE', ...options });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// 创建全局默认实例
|
||||||
|
// ============================================================
|
||||||
|
const http = new Request();
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// 导出
|
||||||
|
// ============================================================
|
||||||
|
export default http;
|
||||||
|
export { Request };
|
||||||
|
|
@ -0,0 +1,346 @@
|
||||||
|
/**
|
||||||
|
* ============================================================
|
||||||
|
* URL 参数解析工具 - url-query.js
|
||||||
|
* 支持 uni-app 全平台(H5、微信小程序、支付宝小程序、
|
||||||
|
* 百度小程序、抖音小程序、App、快应用)
|
||||||
|
* ============================================================
|
||||||
|
*
|
||||||
|
* 【使用方式】
|
||||||
|
* import UrlQuery from '@/scratch/url-query.js';
|
||||||
|
*
|
||||||
|
* // 获取单个参数(自动类型转换)
|
||||||
|
* const id = UrlQuery.get('id'); // "123" → 123 (number)
|
||||||
|
* const name = UrlQuery.get('name'); // "张三" (string)
|
||||||
|
* const flag = UrlQuery.get('flag'); // "true" → true (boolean)
|
||||||
|
* const rid = UrlQuery.get('rid', '-1'); // 不存在返回默认值 '-1'
|
||||||
|
*
|
||||||
|
* // 获取原始字符串(不自动类型转换)
|
||||||
|
* const raw = UrlQuery.get('id', '', false); // "123" (string)
|
||||||
|
*
|
||||||
|
* // 获取所有参数
|
||||||
|
* const params = UrlQuery.getAll(); // { id: 123, name: "张三" }
|
||||||
|
*
|
||||||
|
* // 判断参数是否存在
|
||||||
|
* const hasId = UrlQuery.has('id'); // true / false
|
||||||
|
*
|
||||||
|
* // 解析指定 URL 字符串
|
||||||
|
* const parsed = UrlQuery.parseUrl('https://example.com?id=1&name=a');
|
||||||
|
*
|
||||||
|
* // 解析查询字符串
|
||||||
|
* const obj = UrlQuery.parse('?a=1&b=2&b=3'); // { a: "1", b: ["2", "3"] }
|
||||||
|
*
|
||||||
|
* 【特性】
|
||||||
|
* 1. 多平台自适应(H5 通过 location 获取,小程序/App 通过页面栈获取)
|
||||||
|
* 2. 自动类型转换(数字、布尔值、null)
|
||||||
|
* 3. 安全解码(异常编码不会报错)
|
||||||
|
* 4. 支持数组参数(同名参数自动转为数组)
|
||||||
|
* 5. 支持 history 路由和 hash 路由模式
|
||||||
|
* 6. 容错处理(空值、undefined、异常 URL 等)
|
||||||
|
* ============================================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
const UrlQuery = {
|
||||||
|
/**
|
||||||
|
* 解析 URL 查询字符串为对象
|
||||||
|
* @param {string} queryString - 查询字符串,如 "?a=1&b=2" 或 "a=1&b=2"
|
||||||
|
* @returns {Object} 解析后的参数对象,同名参数自动转为数组
|
||||||
|
*/
|
||||||
|
parse(queryString) {
|
||||||
|
const result = {};
|
||||||
|
|
||||||
|
if (typeof queryString !== 'string') {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 统一去掉开头的 ? 和 #,并去除首尾空白
|
||||||
|
queryString = queryString.replace(/^[?#]/, '').trim();
|
||||||
|
if (!queryString) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
const pairs = queryString.split('&');
|
||||||
|
|
||||||
|
for (let i = 0; i < pairs.length; i++) {
|
||||||
|
const pair = pairs[i];
|
||||||
|
if (!pair) continue;
|
||||||
|
|
||||||
|
const eqIndex = pair.indexOf('=');
|
||||||
|
let key, value;
|
||||||
|
|
||||||
|
if (eqIndex === -1) {
|
||||||
|
// 无等号的情况,如 "?foo&bar"
|
||||||
|
key = pair;
|
||||||
|
value = '';
|
||||||
|
} else {
|
||||||
|
key = pair.substring(0, eqIndex);
|
||||||
|
value = pair.substring(eqIndex + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 安全解码(异常编码不抛错)
|
||||||
|
try {
|
||||||
|
key = decodeURIComponent(key);
|
||||||
|
} catch (e) {
|
||||||
|
/* 保留原始值 */
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
value = decodeURIComponent(value);
|
||||||
|
} catch (e) {
|
||||||
|
/* 保留原始值 */
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理数组参数(如 id=1&id=2 → { id: ['1', '2'] })
|
||||||
|
if (Object.prototype.hasOwnProperty.call(result, key)) {
|
||||||
|
if (!Array.isArray(result[key])) {
|
||||||
|
result[key] = [result[key]];
|
||||||
|
}
|
||||||
|
result[key].push(value);
|
||||||
|
} else {
|
||||||
|
result[key] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前页面的 URL 参数对象
|
||||||
|
* 多平台自适应:H5 从 window.location 获取,其他平台从页面栈获取
|
||||||
|
* @returns {Object} 当前页面所有参数
|
||||||
|
*/
|
||||||
|
getCurrent() {
|
||||||
|
let params = {};
|
||||||
|
|
||||||
|
// #ifdef H5
|
||||||
|
// H5 环境下优先从 window.location 解析(兼容 history 和 hash 路由)
|
||||||
|
try {
|
||||||
|
if (typeof window !== 'undefined' && window.location) {
|
||||||
|
const searchParams = this.parse(window.location.search);
|
||||||
|
const hashParts = window.location.hash.split('?');
|
||||||
|
const hashParams = hashParts.length > 1 ? this.parse(hashParts[1]) : {};
|
||||||
|
params = { ...searchParams, ...hashParams };
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('[UrlQuery] H5 解析 location 失败:', e);
|
||||||
|
}
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
// 尝试从页面栈获取(uni-app 所有平台通用)
|
||||||
|
try {
|
||||||
|
const pages = getCurrentPages();
|
||||||
|
if (pages && pages.length > 0) {
|
||||||
|
const currentPage = pages[pages.length - 1];
|
||||||
|
if (currentPage && currentPage.options && typeof currentPage.options === 'object') {
|
||||||
|
// H5 下合并 location 与 options,其他平台以 options 为准
|
||||||
|
// #ifdef H5
|
||||||
|
params = { ...params, ...currentPage.options };
|
||||||
|
// #endif
|
||||||
|
// #ifndef H5
|
||||||
|
params = { ...currentPage.options };
|
||||||
|
// #endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// getCurrentPages 在部分环境下可能不可用,静默处理
|
||||||
|
}
|
||||||
|
|
||||||
|
return params;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析指定 URL 字符串中的参数
|
||||||
|
* @param {string} url - 完整 URL,如 "https://example.com/path?id=1&name=a#hash?b=2"
|
||||||
|
* @returns {Object} 解析后的参数对象(search 参数优先,hash 参数补充)
|
||||||
|
*/
|
||||||
|
parseUrl(url) {
|
||||||
|
if (typeof url !== 'string' || !url.trim()) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
url = url.trim();
|
||||||
|
|
||||||
|
// 优先使用浏览器 URL API(H5 / 现代环境)
|
||||||
|
try {
|
||||||
|
if (typeof URL !== 'undefined') {
|
||||||
|
const urlObj = new URL(url);
|
||||||
|
return this.parse(urlObj.search);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// URL 构造函数不支持或解析失败,降级到手动提取
|
||||||
|
}
|
||||||
|
|
||||||
|
// 降级处理:手动提取 search 和 hash 中的参数
|
||||||
|
let search = '';
|
||||||
|
let hashQuery = '';
|
||||||
|
|
||||||
|
const searchIndex = url.indexOf('?');
|
||||||
|
const hashIndex = url.indexOf('#');
|
||||||
|
|
||||||
|
if (searchIndex !== -1) {
|
||||||
|
const endIndex = (hashIndex !== -1 && hashIndex > searchIndex) ? hashIndex : url.length;
|
||||||
|
search = url.substring(searchIndex, endIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hashIndex !== -1) {
|
||||||
|
const hashPart = url.substring(hashIndex + 1);
|
||||||
|
const hashQIndex = hashPart.indexOf('?');
|
||||||
|
if (hashQIndex !== -1) {
|
||||||
|
hashQuery = hashPart.substring(hashQIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const searchParams = this.parse(search);
|
||||||
|
const hashParams = this.parse(hashQuery);
|
||||||
|
|
||||||
|
return { ...searchParams, ...hashParams };
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取单个参数值
|
||||||
|
* @param {string} key - 参数名(必填)
|
||||||
|
* @param {*} defaultValue - 参数不存在时返回的默认值(默认空字符串)
|
||||||
|
* @param {boolean} autoType - 是否自动类型转换(默认 true)
|
||||||
|
* true : "123"→123, "true"→true, "false"→false, "null"→null
|
||||||
|
* false : 始终返回原始字符串
|
||||||
|
* @returns {*} 参数值
|
||||||
|
*/
|
||||||
|
get(key, defaultValue = '', autoType = true) {
|
||||||
|
if (typeof key !== 'string' || !key) {
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const params = this.getCurrent();
|
||||||
|
|
||||||
|
if (!Object.prototype.hasOwnProperty.call(params, key)) {
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let value = params[key];
|
||||||
|
|
||||||
|
// 值为 undefined / null 时返回默认值
|
||||||
|
if (value === undefined || value === null) {
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 不自动类型转换时直接返回字符串形式
|
||||||
|
if (!autoType) {
|
||||||
|
return String(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 已经是非字符串类型(如数字、布尔)直接返回
|
||||||
|
if (typeof value !== 'string') {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 空字符串处理
|
||||||
|
if (value === '') {
|
||||||
|
return defaultValue !== '' ? defaultValue : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 布尔值转换
|
||||||
|
if (value === 'true') return true;
|
||||||
|
if (value === 'false') return false;
|
||||||
|
|
||||||
|
// null / undefined 字符串转换
|
||||||
|
if (value === 'null') return null;
|
||||||
|
if (value === 'undefined') return undefined;
|
||||||
|
|
||||||
|
// 整数转换(严格匹配,避免 "0123" 被转成 123 后丢失前导零)
|
||||||
|
if (/^-?(0|[1-9]\d*)$/.test(value)) {
|
||||||
|
const intVal = parseInt(value, 10);
|
||||||
|
if (!isNaN(intVal) && String(intVal) === value) {
|
||||||
|
return intVal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 浮点数转换
|
||||||
|
if (/^-?(0|[1-9]\d*)\.\d+$/.test(value)) {
|
||||||
|
const floatVal = parseFloat(value);
|
||||||
|
if (!isNaN(floatVal) && String(floatVal) === value) {
|
||||||
|
return floatVal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取所有参数(原始字符串形式,不做类型转换)
|
||||||
|
* @returns {Object} 所有参数的字符串键值对
|
||||||
|
*/
|
||||||
|
getAll() {
|
||||||
|
return this.getCurrent();
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取所有参数并进行自动类型转换
|
||||||
|
* @returns {Object} 所有参数(数字、布尔值已转换)
|
||||||
|
*/
|
||||||
|
getAllTyped() {
|
||||||
|
const params = this.getCurrent();
|
||||||
|
const result = {};
|
||||||
|
for (const key in params) {
|
||||||
|
if (Object.prototype.hasOwnProperty.call(params, key)) {
|
||||||
|
result[key] = this.get(key, '', true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断参数是否存在于当前 URL 中
|
||||||
|
* @param {string} key - 参数名
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
has(key) {
|
||||||
|
if (typeof key !== 'string' || !key) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return Object.prototype.hasOwnProperty.call(this.getCurrent(), key);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取数组类型的参数值
|
||||||
|
* 若参数为单个值则包装为数组返回,不存在返回默认值
|
||||||
|
* @param {string} key - 参数名
|
||||||
|
* @param {Array} defaultValue - 默认值(默认空数组)
|
||||||
|
* @returns {Array} 数组形式的参数值
|
||||||
|
*/
|
||||||
|
getArray(key, defaultValue = []) {
|
||||||
|
if (typeof key !== 'string' || !key) {
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const params = this.getCurrent();
|
||||||
|
if (!Object.prototype.hasOwnProperty.call(params, key)) {
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const value = params[key];
|
||||||
|
if (Array.isArray(value)) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
if (value === undefined || value === null) {
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
return [value];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 兼容 CommonJS / ES Module / 全局变量
|
||||||
|
// #ifdef VUE3
|
||||||
|
export default UrlQuery;
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
// #ifndef VUE3
|
||||||
|
if (typeof module !== 'undefined' && module.exports) {
|
||||||
|
module.exports = UrlQuery;
|
||||||
|
} else if (typeof define === 'function' && define.amd) {
|
||||||
|
define(function () { return UrlQuery; });
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
window.UrlQuery = UrlQuery;
|
||||||
|
} catch (e) {
|
||||||
|
// 非浏览器环境静默处理
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// #endif
|
||||||
|
|
@ -0,0 +1,64 @@
|
||||||
|
import { createStore } from 'vuex'
|
||||||
|
|
||||||
|
const store = createStore({
|
||||||
|
state: {
|
||||||
|
currentUid: '',
|
||||||
|
relationId: '',
|
||||||
|
pid: '',
|
||||||
|
isThirdParty: false
|
||||||
|
},
|
||||||
|
getters: {
|
||||||
|
currentUid: state => state.currentUid,
|
||||||
|
relationId: state => state.relationId,
|
||||||
|
pid: state => state.pid,
|
||||||
|
isThirdParty: state => state.isThirdParty
|
||||||
|
},
|
||||||
|
mutations: {
|
||||||
|
SET_CURRENT_UID(state, uid) {
|
||||||
|
state.currentUid = uid
|
||||||
|
},
|
||||||
|
CLEAR_CURRENT_UID(state) {
|
||||||
|
state.currentUid = ''
|
||||||
|
},
|
||||||
|
SET_RELATION_ID(state, rid) {
|
||||||
|
state.relationId = rid
|
||||||
|
},
|
||||||
|
CLEAR_RELATION_ID(state) {
|
||||||
|
state.relationId = ''
|
||||||
|
},
|
||||||
|
SET_PID(state, pid) {
|
||||||
|
state.pid = pid
|
||||||
|
},
|
||||||
|
CLEAR_PID(state) {
|
||||||
|
state.pid = ''
|
||||||
|
},
|
||||||
|
SET_IS_THIRD_PARTY(state, flag) {
|
||||||
|
state.isThirdParty = flag
|
||||||
|
}
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
setCurrentUid({ commit }, uid) {
|
||||||
|
commit('SET_CURRENT_UID', uid)
|
||||||
|
},
|
||||||
|
clearCurrentUid({ commit }) {
|
||||||
|
commit('CLEAR_CURRENT_UID')
|
||||||
|
},
|
||||||
|
setRelationId({ commit }, rid) {
|
||||||
|
commit('SET_RELATION_ID', rid)
|
||||||
|
},
|
||||||
|
clearRelationId({ commit }) {
|
||||||
|
commit('CLEAR_RELATION_ID')
|
||||||
|
},
|
||||||
|
setPid({ commit }, pid) {
|
||||||
|
commit('SET_PID', pid)
|
||||||
|
},
|
||||||
|
clearPid({ commit }) {
|
||||||
|
commit('CLEAR_PID')
|
||||||
|
},
|
||||||
|
setIsThirdParty({ commit }, flag) {
|
||||||
|
commit('SET_IS_THIRD_PARTY', flag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
export default store
|
||||||
|
|
@ -0,0 +1,150 @@
|
||||||
|
<template>
|
||||||
|
<view class="test-page">
|
||||||
|
<view class="desc-card">
|
||||||
|
<text class="title">IntersectionObserver 仅首次触发演示</text>
|
||||||
|
<text class="subtitle">向下滚动,观察各模块首次进入视口时的淡入效果</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="section-title">场景:仅首次触发 — 视口内淡入动画</view>
|
||||||
|
<view
|
||||||
|
class="fade-box"
|
||||||
|
v-for="(item, index) in fadeList"
|
||||||
|
:key="index"
|
||||||
|
:id="`fade-${index}`"
|
||||||
|
:class="{ 'fade-in': item.visible }"
|
||||||
|
>
|
||||||
|
<text class="box-text">模块 {{ index + 1 }}</text>
|
||||||
|
<text class="box-status">{{ item.triggered ? '✅ 已触发(不再重复)' : '⏳ 等待首次进入视口...' }}</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="footer-space"></view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
fadeList: Array.from({ length: 10 }, () => ({
|
||||||
|
visible: false,
|
||||||
|
triggered: false
|
||||||
|
})),
|
||||||
|
// ⭐ 关键:用一个数组存放每个元素独立的 observer,避免互相覆盖
|
||||||
|
observers: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onReady() {
|
||||||
|
this.initFadeObserver();
|
||||||
|
},
|
||||||
|
onUnload() {
|
||||||
|
// 页面卸载时,断开所有 observer
|
||||||
|
this.observers.forEach(obs => obs.disconnect());
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
initFadeObserver() {
|
||||||
|
this.fadeList.forEach((_, index) => {
|
||||||
|
// ⭐ 核心修复:每个元素创建独立的 IntersectionObserver 实例
|
||||||
|
// 同一个实例多次调用 observe() 会被覆盖,只有最后一个生效
|
||||||
|
const observer = uni.createIntersectionObserver(this, {
|
||||||
|
thresholds: [0]
|
||||||
|
});
|
||||||
|
|
||||||
|
observer.relativeToViewport();
|
||||||
|
|
||||||
|
observer.observe(`#fade-${index}`, (result) => {
|
||||||
|
const item = this.fadeList[index];
|
||||||
|
|
||||||
|
// 进入视口 且 之前没触发过
|
||||||
|
if (result.intersectionRatio > 0 && !item.triggered) {
|
||||||
|
console.log(`模块 ${index + 1} 首次进入视口`);
|
||||||
|
|
||||||
|
this.$set(this.fadeList, index, {
|
||||||
|
visible: true,
|
||||||
|
triggered: true
|
||||||
|
});
|
||||||
|
|
||||||
|
// 触发后断开这个 observer,彻底释放
|
||||||
|
observer.disconnect();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 保存到数组,方便 onUnload 统一清理
|
||||||
|
this.observers.push(observer);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.test-page {
|
||||||
|
background-color: #f5f6f8;
|
||||||
|
padding: 30rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.desc-card {
|
||||||
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||||
|
border-radius: 24rpx;
|
||||||
|
padding: 40rpx;
|
||||||
|
margin-bottom: 40rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
color: #ffffff;
|
||||||
|
font-size: 40rpx;
|
||||||
|
font-weight: bold;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.subtitle {
|
||||||
|
color: rgba(255, 255, 255, 0.85);
|
||||||
|
font-size: 26rpx;
|
||||||
|
margin-top: 16rpx;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-title {
|
||||||
|
font-size: 32rpx;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #333;
|
||||||
|
margin: 40rpx 0 20rpx;
|
||||||
|
padding-left: 20rpx;
|
||||||
|
border-left: 8rpx solid #ff416c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fade-box {
|
||||||
|
height: 240rpx;
|
||||||
|
background: #ffffff;
|
||||||
|
border-radius: 20rpx;
|
||||||
|
margin-bottom: 20rpx;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05);
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(60rpx);
|
||||||
|
transition: all 0.6s cubic-bezier(0.25, 0.8, 0.25, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.fade-box.fade-in {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.box-text {
|
||||||
|
font-size: 36rpx;
|
||||||
|
color: #333;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.box-status {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #999;
|
||||||
|
margin-top: 12rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-space {
|
||||||
|
height: 100rpx;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
{
|
{
|
||||||
"hash": "0694fca5",
|
"hash": "aee3647b",
|
||||||
"configHash": "6057985e",
|
"configHash": "43aa957d",
|
||||||
"lockfileHash": "e3b0c442",
|
"lockfileHash": "32773baf",
|
||||||
"browserHash": "9518c2ac",
|
"browserHash": "732cb9f3",
|
||||||
"optimized": {},
|
"optimized": {},
|
||||||
"chunks": {}
|
"chunks": {}
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,271 @@
|
||||||
|
/**
|
||||||
|
* ============================================================
|
||||||
|
* 环境检测器 - uni-app 适配版
|
||||||
|
* 支持:H5、微信小程序、支付宝小程序、百度小程序、抖音小程序、App
|
||||||
|
* ============================================================
|
||||||
|
*
|
||||||
|
* 原始版本基于 navigator.userAgent(H5 专用)
|
||||||
|
* 本版本通过 uni-app 条件编译适配多端
|
||||||
|
*
|
||||||
|
* 【用法】
|
||||||
|
* import { getEnvironment, detect, isWechat, isBrowser, ... } from '@/utils/env.js';
|
||||||
|
*
|
||||||
|
* const env = getEnvironment();
|
||||||
|
* if (env.isBrowser) { ... }
|
||||||
|
* if (env.isWechatWebview) { ... }
|
||||||
|
* ============================================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* ========== H5 平台:原始 UA 检测 ========== */
|
||||||
|
// #ifdef H5
|
||||||
|
const UA = (navigator.userAgent || '').toLowerCase();
|
||||||
|
|
||||||
|
function _isWechatMiniProgram() {
|
||||||
|
if (window.__wxjs_environment === 'miniprogram') return true;
|
||||||
|
if (/miniprogram/.test(UA)) return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function _isWechatH5() {
|
||||||
|
if (!/micromessenger/.test(UA)) return false;
|
||||||
|
if (_isWechatMiniProgram()) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function _isWechat() {
|
||||||
|
return /micromessenger/.test(UA);
|
||||||
|
}
|
||||||
|
|
||||||
|
function _isAppWebview() {
|
||||||
|
const patterns = [
|
||||||
|
/aliapp\(tb/i,
|
||||||
|
/aliapp\(tm/i,
|
||||||
|
/jdapp/i,
|
||||||
|
/aweme/i,
|
||||||
|
/ksnc/i,
|
||||||
|
/weibo/i,
|
||||||
|
/dingtalk/i,
|
||||||
|
/alipayclient/i,
|
||||||
|
/baiduboxapp/i,
|
||||||
|
/qq\//i,
|
||||||
|
];
|
||||||
|
return patterns.some(function (re) { return re.test(UA); });
|
||||||
|
}
|
||||||
|
|
||||||
|
function _isAppNative() {
|
||||||
|
if (typeof window.TBJS !== 'undefined') return true;
|
||||||
|
if (typeof window.AlipayJSBridge !== 'undefined') return true;
|
||||||
|
if (typeof window.dd !== 'undefined') return true;
|
||||||
|
if (typeof window.toutiao !== 'undefined') return true;
|
||||||
|
if (typeof window.JDBridge !== 'undefined') return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function _isBrowser() {
|
||||||
|
if (_isWechat()) return false;
|
||||||
|
if (_isAppWebview()) return false;
|
||||||
|
if (_isAppNative()) return false;
|
||||||
|
return /safari|chrome|crios|firefox|fxios|edge|edg|opr/.test(UA);
|
||||||
|
}
|
||||||
|
|
||||||
|
function _isMobile() {
|
||||||
|
return /iphone|ipad|ipod|android/.test(UA);
|
||||||
|
}
|
||||||
|
|
||||||
|
function _getAppName() {
|
||||||
|
var map = [
|
||||||
|
{ name: 'taobao', regex: /aliapp\(tb/i },
|
||||||
|
{ name: 'tmall', regex: /aliapp\(tm/i },
|
||||||
|
{ name: 'jd', regex: /jdapp/i },
|
||||||
|
{ name: 'douyin', regex: /aweme/i },
|
||||||
|
{ name: 'kuaishou', regex: /ksnc/i },
|
||||||
|
{ name: 'weibo', regex: /weibo/i },
|
||||||
|
{ name: 'dingtalk', regex: /dingtalk/i },
|
||||||
|
{ name: 'alipay', regex: /alipayclient/i },
|
||||||
|
{ name: 'baidu', regex: /baiduboxapp/i },
|
||||||
|
{ name: 'qq', regex: /qq\//i },
|
||||||
|
];
|
||||||
|
for (var i = 0; i < map.length; i++) {
|
||||||
|
if (map[i].regex.test(UA)) return map[i].name;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function _detect() {
|
||||||
|
if (_isWechatMiniProgram()) return 'wechat-miniprogram';
|
||||||
|
if (_isWechatH5()) return 'wechat-h5';
|
||||||
|
if (_isAppNative()) return 'app-native';
|
||||||
|
if (_isAppWebview()) return 'app-webview';
|
||||||
|
if (_isBrowser()) return _isMobile() ? 'mobile-browser' : 'pc-browser';
|
||||||
|
return 'unknown';
|
||||||
|
}
|
||||||
|
|
||||||
|
function _getEnvironment() {
|
||||||
|
var wechat = _isWechat();
|
||||||
|
var wechatMini = _isWechatMiniProgram();
|
||||||
|
var wechatH5Env = _isWechatH5();
|
||||||
|
var appWebview = _isAppWebview();
|
||||||
|
var appNative = _isAppNative();
|
||||||
|
var browser = _isBrowser();
|
||||||
|
|
||||||
|
return {
|
||||||
|
isApp: appNative,
|
||||||
|
isWechat: wechat,
|
||||||
|
isWechatH5: wechatH5Env,
|
||||||
|
isBrowser: browser,
|
||||||
|
isWechatWebview: wechatH5Env || wechatMini,
|
||||||
|
isAppWebview: appWebview,
|
||||||
|
isWechatMiniProgram: wechatMini,
|
||||||
|
isAppNative: appNative,
|
||||||
|
appName: _getAppName(),
|
||||||
|
isMobile: _isMobile(),
|
||||||
|
env: _detect()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
/* ========== 微信小程序 ========== */
|
||||||
|
// #ifdef MP-WEIXIN
|
||||||
|
function _getEnvironment() {
|
||||||
|
return {
|
||||||
|
isApp: false,
|
||||||
|
isWechat: true,
|
||||||
|
isWechatH5: false,
|
||||||
|
isBrowser: false,
|
||||||
|
isWechatWebview: true,
|
||||||
|
isAppWebview: false,
|
||||||
|
isWechatMiniProgram: true,
|
||||||
|
isAppNative: false,
|
||||||
|
appName: null,
|
||||||
|
isMobile: true,
|
||||||
|
env: 'wechat-miniprogram'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
function _detect() { return 'wechat-miniprogram'; }
|
||||||
|
function _isWechatMiniProgram() { return true; }
|
||||||
|
function _isWechatH5() { return false; }
|
||||||
|
function _isWechat() { return true; }
|
||||||
|
function _isWechatWebview() { return true; }
|
||||||
|
function _isAppWebview() { return false; }
|
||||||
|
function _isAppNative() { return false; }
|
||||||
|
function _isBrowser() { return false; }
|
||||||
|
function _isMobile() { return true; }
|
||||||
|
function _getAppName() { return null; }
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
/* ========== App 平台 ========== */
|
||||||
|
// #ifdef APP-PLUS
|
||||||
|
function _getEnvironment() {
|
||||||
|
return {
|
||||||
|
isApp: false,
|
||||||
|
isWechat: false,
|
||||||
|
isWechatH5: false,
|
||||||
|
isBrowser: false,
|
||||||
|
isWechatWebview: false,
|
||||||
|
isAppWebview: true,
|
||||||
|
isWechatMiniProgram: false,
|
||||||
|
isAppNative: false,
|
||||||
|
appName: null,
|
||||||
|
isMobile: true,
|
||||||
|
env: 'app-webview'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
function _detect() { return 'app-webview'; }
|
||||||
|
function _isWechatMiniProgram() { return false; }
|
||||||
|
function _isWechatH5() { return false; }
|
||||||
|
function _isWechat() { return false; }
|
||||||
|
function _isWechatWebview() { return false; }
|
||||||
|
function _isAppWebview() { return true; }
|
||||||
|
function _isAppNative() { return false; }
|
||||||
|
function _isBrowser() { return false; }
|
||||||
|
function _isMobile() { return true; }
|
||||||
|
function _getAppName() { return null; }
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
/* ========== 其他小程序平台(支付宝、百度、抖音等) ========== */
|
||||||
|
// #ifndef H5 || MP-WEIXIN || APP-PLUS
|
||||||
|
function _getEnvironment() {
|
||||||
|
return {
|
||||||
|
isApp: false,
|
||||||
|
isWechat: false,
|
||||||
|
isWechatH5: false,
|
||||||
|
isBrowser: false,
|
||||||
|
isWechatWebview: false,
|
||||||
|
isAppWebview: false,
|
||||||
|
isWechatMiniProgram: false,
|
||||||
|
isAppNative: false,
|
||||||
|
appName: null,
|
||||||
|
isMobile: true,
|
||||||
|
env: 'unknown'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
function _detect() { return 'unknown'; }
|
||||||
|
function _isWechatMiniProgram() { return false; }
|
||||||
|
function _isWechatH5() { return false; }
|
||||||
|
function _isWechat() { return false; }
|
||||||
|
function _isWechatWebview() { return false; }
|
||||||
|
function _isAppWebview() { return false; }
|
||||||
|
function _isAppNative() { return false; }
|
||||||
|
function _isBrowser() { return false; }
|
||||||
|
function _isMobile() { return true; }
|
||||||
|
function _getAppName() { return null; }
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
/* ========== 统一暴露接口 ========== */
|
||||||
|
export function getEnvironment() {
|
||||||
|
return _getEnvironment();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function detect() {
|
||||||
|
return _detect();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isWechatMiniProgram() {
|
||||||
|
return _isWechatMiniProgram();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isWechatH5() {
|
||||||
|
return _isWechatH5();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isWechat() {
|
||||||
|
return _isWechat();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isWechatWebview() {
|
||||||
|
return _isWechatWebview();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isAppWebview() {
|
||||||
|
return _isAppWebview();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isAppNative() {
|
||||||
|
return _isAppNative();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isBrowser() {
|
||||||
|
return _isBrowser();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isMobile() {
|
||||||
|
return _isMobile();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getAppName() {
|
||||||
|
return _getAppName();
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
getEnvironment,
|
||||||
|
detect,
|
||||||
|
isWechatMiniProgram,
|
||||||
|
isWechatH5,
|
||||||
|
isWechat,
|
||||||
|
isWechatWebview,
|
||||||
|
isAppWebview,
|
||||||
|
isAppNative,
|
||||||
|
isBrowser,
|
||||||
|
isMobile,
|
||||||
|
getAppName
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
// 预估消费券函数
|
||||||
|
export function estimateCoupon(tkmoney = 0, percentage = 0.3) {
|
||||||
|
let result = (tkmoney * percentage).toFixed(2);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue