feat: 新增拼团

This commit is contained in:
2026-09-23 10:29:41 +08:00
parent f7b9e66172
commit 67f8b3f352
33 changed files with 7491 additions and 858 deletions
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,211 @@
<template xlang="wxml" minapp="mpvue">
<view class="tki-qrcode">
<!-- #ifndef MP-ALIPAY -->
<canvas class="tki-qrcode-canvas" :canvas-id="cid" :style="{width:cpSize+'px',height:cpSize+'px'}" />
<!-- #endif -->
<!-- #ifdef MP-ALIPAY -->
<canvas :id="cid" :width="cpSize" :height="cpSize" class="tki-qrcode-canvas" />
<!-- #endif -->
<image v-show="show" :src="result" :style="{width:cpSize+'px',height:cpSize+'px'}" />
</view>
</template>
<script>
import QRCode from "./qrcode.js"
let qrcode
export default {
name: "tki-qrcode",
props: {
cid: {
type: String,
default: 'tki-qrcode-canvas'
},
size: {
type: Number,
default: 200
},
unit: {
type: String,
default: 'upx'
},
show: {
type: Boolean,
default: true
},
val: {
type: String,
default: ''
},
background: {
type: String,
default: '#ffffff'
},
foreground: {
type: String,
default: '#000000'
},
pdground: {
type: String,
default: '#000000'
},
icon: {
type: String,
default: ''
},
iconSize: {
type: Number,
default: 40
},
lv: {
type: Number,
default: 3
},
onval: {
type: Boolean,
default: false
},
loadMake: {
type: Boolean,
default: false
},
usingComponents: {
type: Boolean,
default: true
},
showLoading: {
type: Boolean,
default: true
},
loadingText: {
type: String,
default: '二维码生成中'
},
},
data() {
return {
result: '',
}
},
methods: {
_makeCode() {
let that = this
if (!this._empty(this.val)) {
qrcode = new QRCode({
context: that, // 上下文环境
canvasId:that.cid, // canvas-id
usingComponents: that.usingComponents, // 是否是自定义组件
showLoading: that.showLoading, // 是否显示loading
loadingText: that.loadingText, // loading文字
text: that.val, // 生成内容
size: that.cpSize, // 二维码大小
background: that.background, // 背景色
foreground: that.foreground, // 前景色
pdground: that.pdground, // 定位角点颜色
correctLevel: that.lv, // 容错级别
image: that.icon, // 二维码图标
imageSize: that.iconSize,// 二维码图标大小
cbResult: function (res) { // 生成二维码的回调
that._result(res)
},
});
} else {
uni.showToast({
title: '二维码内容不能为空',
icon: 'none',
duration: 2000
});
}
},
_clearCode() {
this._result('')
qrcode.clear()
},
_saveCode() {
let that = this;
if (this.result != "") {
uni.saveImageToPhotosAlbum({
filePath: that.result,
success: function () {
uni.showToast({
title: '二维码保存成功',
icon: 'success',
duration: 2000
});
}
});
}
},
_result(res) {
this.result = res;
this.$emit('result', res)
},
_empty(v) {
let tp = typeof v,
rt = false;
if (tp == "number" && String(v) == "") {
rt = true
} else if (tp == "undefined") {
rt = true
} else if (tp == "object") {
if (JSON.stringify(v) == "{}" || JSON.stringify(v) == "[]" || v == null) rt = true
} else if (tp == "string") {
if (v == "" || v == "undefined" || v == "null" || v == "{}" || v == "[]") rt = true
} else if (tp == "function") {
rt = false
}
return rt
}
},
watch: {
size: function (n, o) {
if (n != o && !this._empty(n)) {
this.cSize = n
if (!this._empty(this.val)) {
setTimeout(() => {
this._makeCode()
}, 100);
}
}
},
val: function (n, o) {
if (this.onval) {
if (n != o && !this._empty(n)) {
setTimeout(() => {
this._makeCode()
}, 0);
}
}
}
},
computed: {
cpSize() {
if(this.unit == "upx"){
return uni.upx2px(this.size)
}else{
return this.size
}
}
},
mounted: function () {
if (this.loadMake) {
if (!this._empty(this.val)) {
setTimeout(() => {
this._makeCode()
}, 0);
}
}
},
}
</script>
<style>
.tki-qrcode {
position: relative;
}
.tki-qrcode-canvas {
position: fixed;
top: -99999upx;
left: -99999upx;
z-index: -99999;
}
</style>
+274
View File
@@ -0,0 +1,274 @@
<template>
<view class="content-two">
<!-- 顶部轮播图 + 返回按钮 -->
<view class="product_info_header_warp">
<up-swiper :list="swiperList" height="660rpx" class="product_info_header_swiper" @click="onSwiper"
@change="(e) => (currentNum = e.current)">
<template #indicator>
<view class="indicator-num">
<text class="indicator-num__text">{{ currentNum + 1 }}/{{ swiperList.length }}</text>
<qiaobao-assistant page-key="pages/active/group-buying/group-buying" title="超值拼团" />
</view>
</template>
</up-swiper>
<view class="img_comm left_warp">
<up-image src="/static/common/left_b_st.png" width="30" height="30" bgColor="#f1f6ff00"
@click="jumpLeft"></up-image>
</view>
</view>
<!-- 拼团商品列表 -->
<mescroll-uni ref="mescrollRef" top="660rpx" @down="downCallback" @up="upCallback" :up="upOption"
:down="downOption" @init="mescrollInit">
<view class="setting-view">
<view class="name-box">
<view class="mescrollUniView">
<productGroupCard v-for="item in dataList"
:key="item.activityGoodsId || item.goodsId || item.id" :item="item"
@onGroupBuy="onDetail" />
</view>
</view>
</view>
</mescroll-uni>
<up-toast ref="uToastRef"></up-toast>
</view>
</template>
<script>
import { getCarouselImage } from "@/api/common.js";
import { getGroupBuyList } from "@/api/product.js";
import MescrollMixin from "@/components/mescroll-uni/mescroll-mixins.js";
import MescrollUni from "@/components/mescroll-uni/mescroll-uni.vue";
import productGroupCard from "@/components/common/product-group-card.vue";
import { APP_PAGE_TYPE } from "@/utils/enumUtils.js";
export default {
name: "group-buying",
mixins: [MescrollMixin],
components: {
MescrollUni,
productGroupCard
},
data() {
return {
swiperList: [
"/pages/active/static/group_banner_cart.jpg"
],
currentNum: 0,
dataList: [],
activityInfo: {},
upOption: {
auto: false,
page: {
size: 10 // 每页数据的数量
},
noMoreSize: 5,
empty: {
tip: "暂无拼团商品"
},
textColor: "#333",
bgColor: "rgba(0,0,0,0)"
},
downOption: {
auto: false,
textColor: "#333",
bgColor: "rgba(0,0,0,0)"
},
mescroll: null,
activity: {},
fromBuyAlone: false
};
},
onLoad(options) {
if (options?.fromBuyAlone) {
this.fromBuyAlone = true;
}
this.getCarouselImage();
this.upCallback({ num: 1, size: 10 });
},
onBackPress() {
const pages = getCurrentPages();
if (this.fromBuyAlone || pages.length <= 1) {
this.fromBuyAlone = false;
uni.switchTab({
url: "/pages/home/home"
});
return true;
}
},
methods: {
// 查询轮播图
async getCarouselImage() {
const params = {
pageUi: APP_PAGE_TYPE.GROUP_BUY // 可配置为对应拼团页面类型
};
try {
const resp = await getCarouselImage(params);
if (resp && resp.bizcode === 100 && resp.data && resp.data.length > 0) {
this.swiperList = resp.data.map((obj) => obj.carouselImage);
}
} catch (e) {
console.log("getCarouselImage error:", e);
}
},
// 获取拼团商品列表
upCallback(page) {
let params = {
page: page.num,
pageSize: page.size
};
getGroupBuyList(params)
.then((res) => {
if (res && res.bizcode === 100 && res.data) {
this.activity = res.data.activity;
const pageData = res.data.page || {};
const entitys = pageData.entitys || [];
const curPageLen = entitys.length;
const totalCount = pageData.totalCount !== undefined ? pageData.totalCount : 0;
if (page.num === 1) {
this.dataList = [];
if (res.data.activity) {
this.activityInfo = res.data.activity;
}
}
this.dataList = this.dataList.concat(entitys);
this.mescroll && this.mescroll.endBySize(curPageLen, totalCount);
} else {
this.mescroll && this.mescroll.endErr();
}
})
.catch((err) => {
console.log("getGroupBuyList error:", err);
this.mescroll && this.mescroll.endErr();
});
},
onDetail(item) {
const goodsId = item.goodsId || item.id;
uni.navigateTo({
url: "/pages/other_package/productInfo/productInfo?id=" + goodsId + "&activedType=groupBuy" + "&activityId=" + this.activity.activityId
});
},
downCallback() {
this.mescroll && this.mescroll.resetUpScroll();
},
mescrollInit(mescroll) {
this.mescroll = mescroll;
},
jumpLeft() {
if (this.fromBuyAlone) {
uni.switchTab({
url: "/pages/home/home"
});
return;
}
const pages = getCurrentPages();
if (pages.length <= 1) {
uni.switchTab({
url: "/pages/home/home"
});
return;
}
uni.navigateBack({
delta: 1,
fail: () => {
uni.switchTab({
url: "/pages/home/home"
});
}
});
},
onSwiper(index) {
if (this.swiperList && this.swiperList.length > 0) {
uni.previewImage({
current: index,
loop: true,
urls: this.swiperList
});
}
}
}
};
</script>
<style lang="scss" scoped>
.content-two {
height: 100vh;
background-color: #fff;
position: relative;
overflow: hidden;
}
.product_info_header_warp {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 660rpx;
z-index: 10;
background-color: #fff;
.product_info_header_swiper {
height: 660rpx !important;
border-radius: 0 !important;
::v-deep(.u-swiper__wrapper) {
height: 660rpx !important;
.u-swiper__wrapper {
height: 660rpx !important;
.u-swiper__wrapper__item__wrapper__image {
height: 660rpx !important;
border-radius: 0 !important;
}
}
}
}
.img_comm {
position: absolute;
top: 140rpx;
}
.left_warp {
left: 40rpx;
}
.right_warp {
right: 40rpx;
}
}
.setting-view {
width: 100%;
padding: 32rpx 32rpx 20rpx;
box-sizing: border-box;
position: relative;
background-color: #fff;
border-top-left-radius: 32rpx;
border-top-right-radius: 32rpx;
min-height: calc(100vh - 600rpx);
.name-box {
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
.mescrollUniView {
width: 100%;
display: flex;
flex-direction: column;
}
}
}
</style>
File diff suppressed because it is too large Load Diff
Binary file not shown.

After

Width:  |  Height:  |  Size: 564 KiB