增加分销页面,订单增加会员折扣

This commit is contained in:
2025-10-25 16:31:38 +08:00
parent 26532150b5
commit 845d9f7b40
39 changed files with 5988 additions and 43 deletions

View File

@@ -0,0 +1,563 @@
<template>
<view class="container">
<view class="header-wrap">
<view class="u-flex" style="justify-content: flex-end"> </view>
<view class="search-wrap">
<view class="input-wrap" @click="showStatus = true">
<view class="icon right">
<u-icon name="arrow-down" size="12"></u-icon>
</view>
<input
v-model="querForm.statusName"
class="ipt right"
type="text"
placeholder="全部"
placeholder-style="font-size: 28rpx"
disabled
/>
</view>
<view class="input-wrap" @click="showTimeArea = true">
<view class="icon right">
<u-icon name="arrow-down" size="12"></u-icon>
</view>
<input
v-model="querForm.timeArea"
class="ipt right"
type="text"
placeholder="选择日期范围"
placeholder-style="font-size: 28rpx"
disabled
/>
</view>
<view class="input-wrap" @click="show = true">
<view class="icon right">
<u-icon name="arrow-down" size="12"></u-icon>
</view>
<input
v-model="querForm.shopName"
class="ipt right"
type="text"
placeholder="全部店铺"
placeholder-style="font-size: 28rpx"
disabled
/>
</view>
</view>
</view>
<view>
<view class="u-p-t-16 u-p-b-20 u-flex u-p-l-28 u-p-r-28 " style="align-items: baseline;justify-content: flex-end;">
<text class="color-666 font-12"> 总计</text>
<text class="font-16 color-333 font-700"> 999.99</text>
</view>
<view class="list">
<view v-for="(item, index) in 3" :key="index" class="item">
<view class="u-flex justify-between">
<view>
<text class="color-666 ">来源</text>
<text class="color-333 font-700">儿童玩具部落</text>
</view>
<view>
<text class="color-666">待入账</text>
</view>
</view>
<view class="u-flex justify-between u-m-t-16">
<view>
<text class="color-666 ">订单</text>
<text class="color-333 font-700">WEB1942482053783560192</text>
</view>
<view class="money">
<text class="money reduce">+100</text>
<text class="tag">(订单一级分成)</text>
</view>
</view>
<view class="u-flex justify-between u-m-t-16">
<view>
<text class="color-666 ">时间</text>
<text class="color-333 font-700">2025/01/21 04:03</text>
</view>
</view>
</view>
</view>
</view>
<u-loadmore :status="list.status"></u-loadmore>
<u-popup :show="show" round="20" closeable @close="show = false">
<view class="shoplist-popup">
<view class="title">
<text class="t">店铺列表</text>
</view>
<scroll-view
class="popup-list"
direction="vertical"
@scrollend="scrollBottom"
>
<view
class="item"
v-for="item in shopList"
:key="item.shopId"
@click="selectShopHandle(item)"
>
<text class="t">{{ item.shopName }}</text>
<text class="intro">地址{{ item.shopAddress }}</text>
</view>
<u-loadmore status="nomore"></u-loadmore>
</scroll-view>
</view>
</u-popup>
<up-action-sheet
cancelText="取消"
:actions="statusList"
title="选择状态"
:show="showStatus"
closeOnClickAction
@close="showStatus = false"
@select="selectStatusHandle"
round="16"
></up-action-sheet>
<dateAreaSel
:show="showTimeArea"
:minYear="2022"
@close="showTimeArea = false"
@confirm="confirmTimeArea"
></dateAreaSel>
</view>
</template>
<script setup>
import dayjs from "dayjs";
import dateAreaSel from "@/components/date-range-picker/date-range-picker.vue";
import { ref, reactive, onMounted, computed } from "vue";
import {
onLoad,
onReady,
onShow,
onPageScroll,
onReachBottom,
} from "@dcloudio/uni-app";
import {
APIcouponfindByUserId,
APIfindCoupon,
getCouponShops,
} from "@/common/api/member.js";
const show = ref(false);
const showTimeArea = ref(false);
const querForm = ref({
searchValue: "",
shopId: "",
shopName: "",
statusActiveIndex: 0,
status: "",
statusName: "",
startDate: "",
timeArea: "",
endDate: "",
date: [],
});
function confirmTimeArea(e) {
console.log(e);
querForm.value.date = e;
querForm.value.startDate = e[0];
querForm.value.endDate = e[1];
querForm.value.timeArea = e[0] + "-" + e[1];
}
// 状态
const statusList = ref([
{
value: 0,
name: "未使用",
color: "#333",
fontSize: "16",
},
{
value: 1,
name: "已使用",
color: "#333",
fontSize: "16",
},
{
value: 2,
name: "已失效",
color: "#333",
fontSize: "16",
},
]);
const returnStatusName = () => {
let name = "";
statusList.value.forEach((item) => {
if (item.value == querForm.value.status) {
name = item.name;
}
});
return name;
};
function selectStatusHandle(e) {
console.log(e);
querForm.value.status = e.value;
querForm.value.statusName = returnStatusName();
}
const list = reactive({
page: 1,
size: 10,
status: "loading",
data: [],
});
onReachBottom(() => {
if (list.status != "nomore") {
list.page++;
}
});
const showStatus = ref(false);
const selectListItemDetails = ref([]);
// 切换类型
function tabChange(index) {
querForm.value.statusActiveIndex = index;
list.page = 1;
list.status = "loading";
}
// 店铺列表滚动到底部了
function scrollBottom() {
console.log("店铺列表滚动到底部了");
}
// 选择店铺
function selectShopHandle(item) {
querForm.value.shopId = item.shopId;
querForm.value.shopName = item.shopName;
list.page = 1;
show.value = false;
}
// 获取当前店铺会员信息
const shopList = ref([]);
async function getCouponShopsAjax() {
try {
const res = await getCouponShops();
shopList.value = res;
} catch (error) {
console.log(error);
}
}
onShow(() => {});
onLoad(() => {
getCouponShopsAjax();
});
</script>
<style>
page {
background-color: #f7f7f7;
}
</style>
<style scoped lang="scss">
.container {
padding: 130rpx 0 28upx;
}
.list{
font-size: 28rpx;
.item{
background-color: #fff;
padding: 32rpx 28rpx 32rpx 36rpx;
margin-bottom: 16rpx;
.money{
line-height: 44rpx;
color: #FE7E00;
font-size: 48rpx;
font-weight: 700;
position: relative;
&.reduce{
color: #FF1C1C;
}
.tag{
position: absolute;
font-weight: 400;
right: 0;
top: 100%;
font-size: 28rpx;
white-space: nowrap;
color: #999;
transform: translateY(10rpx);
}
}
}
}
.header-wrap {
width: 100%;
background-color: #fff;
position: fixed;
top: 0;
left: 0;
z-index: 99;
padding: 28upx;
.search-wrap {
display: flex;
gap: 20upx;
.input-wrap {
height: 70upx;
border: 1px solid #ececec;
border-radius: 8upx;
position: relative;
box-sizing: border-box;
&:nth-child(1) {
width: 170rpx;
}
&:nth-child(2) {
flex: 1;
}
&:nth-child(3) {
width: 226rpx;
}
.icon {
position: absolute;
top: 50%;
transform: translateY(-50%);
&.left {
left: 14upx;
}
&.right {
right: 14upx;
}
}
.ipt {
width: 100%;
height: 100%;
font-size: 28rpx;
&.left {
padding-left: 68upx;
}
&.right {
padding: 0 56upx 0 24upx;
}
}
}
}
.status-wrap {
display: flex;
padding-top: 28upx;
position: relative;
.icon-wrap {
height: 12upx;
position: absolute;
bottom: 0;
left: 0;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.3s ease-in-out;
.active-icon {
width: 24upx;
height: 12upx;
z-index: 9;
}
}
.item {
flex: 1;
height: 80upx;
display: flex;
align-items: center;
justify-content: center;
.t {
font-size: 32upx;
color: #666666;
transition: all 0.3s ease-in-out 0.1s;
}
&.active {
.t {
color: #e3ad7f;
}
}
}
}
}
.list-wrap {
padding-top: 28upx;
.item {
border-radius: 18upx;
background-color: #fff;
padding: 28upx;
&:not(:first-child) {
margin-top: 28upx;
}
.top {
display: flex;
align-items: center;
padding-bottom: 28upx;
.icon {
$size: 140upx;
width: $size;
height: $size;
margin-right: 28upx;
}
.info {
flex: 1;
display: flex;
justify-content: center;
flex-direction: column;
gap: 8upx;
padding-left: 28upx;
position: relative;
&::after {
$height: 100upx;
content: "";
height: $height;
border-left: 1upx solid #f7f7f7;
position: absolute;
top: 50%;
margin-top: $height * 0.5 * -1;
left: 0;
}
.view {
flex: 1;
&.name {
font-size: 32upx;
color: #333;
}
&.time {
.t {
font-size: 24upx;
color: #999;
}
}
}
}
.btn {
width: 120upx;
height: 48upx;
border-radius: 48upx;
display: flex;
align-items: center;
justify-content: center;
background-color: #333;
.t {
font-size: 24upx;
color: #fff;
}
&.disabled {
background-color: #f8f8f8;
.t {
color: #999999;
}
}
}
}
.btm {
display: flex;
align-items: center;
padding: 28upx 0 14upx;
border-top: 1upx solid #f7f7f7;
.left {
flex: 1;
height: 40upx;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-size: 24upx;
color: #999;
}
.right {
flex: 1;
display: flex;
align-items: center;
padding-left: 28upx;
justify-content: flex-end;
.t {
font-size: 24upx;
color: #333;
}
}
}
}
}
.shoplist-popup {
padding: 0 28upx 28upx;
.title {
padding: 28upx 0;
display: flex;
align-items: center;
justify-content: center;
.t {
font-size: 32upx;
font-weight: bold;
color: #333;
}
}
.popup-list {
max-height: 50vh;
.item {
padding: 28upx;
border-radius: 12upx;
background-color: #f7f7f7;
margin-bottom: 28upx;
display: flex;
flex-direction: column;
padding: 28upx;
.t {
font-size: 28upx;
font-weight: bold;
color: #333;
/* 必须设置宽度 */
width: 600upx; /* 或具体像素值 */
/* 关键属性 */
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 1; /* 显示2行 */
overflow: hidden;
/* 文本溢出省略号 */
text-overflow: ellipsis;
/* 可选:防止行高度,确保计算准确 */
line-height: 1.5;
word-break: break-all; /* 允许在单词内换行 */
word-wrap: break-word; /* 允许长单词或URL换行 */
}
.intro {
font-size: 28upx;
color: #999;
/* 必须设置宽度 */
width: 600upx; /* 或具体像素值 */
/* 关键属性 */
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2; /* 显示2行 */
overflow: hidden;
/* 文本溢出省略号 */
text-overflow: ellipsis;
/* 可选:防止行高度,确保计算准确 */
line-height: 1.5;
word-break: break-all; /* 允许在单词内换行 */
word-wrap: break-word; /* 允许长单词或URL换行 */
}
}
.ul {
.li {
color: #999;
font-size: 28upx;
padding: 8upx 0;
.t {
color: #999;
font-size: 28upx;
}
}
}
}
}
</style>

View File

@@ -1,39 +1,265 @@
<template>
<view>
<up-navbar
bg-color="transparent"
title="分销中心"
@leftClick="back"
:fixed="true"
></up-navbar>
<view class="top">
<up-navbar
bg-color="transparent"
title="分销中心"
@leftClick="back"
:fixed="false"
></up-navbar>
<image class="top_bg" src="/distribution/static/top_bg.png"></image>
<view class="top_content"></view>
<view class="top_content">
<view class="u-flex justify-between">
<view>
<view class="u-flex" @click="toShouyiDetail">
<text class="font-12 color-666 u-m-r-6">总收益</text>
<up-icon
name="question-circle"
size="12"
color="#666"
@click="questionClick('总收益')"
></up-icon>
</view>
<view class="price">9925.56</view>
</view>
<view>
<view class="u-flex" @click="toShouyiDetail">
<text class="font-12 color-666 u-m-r-6">待入账</text>
<up-icon
name="question-circle"
size="12"
color="#666"
@click="questionClick('待入账')"
></up-icon>
</view>
<view class="price">1000.55</view>
</view>
</view>
<view class="u-flex justify-between u-m-t-16">
<view>
<view class="u-flex">
<text class="font-12 color-666 u-m-r-6">可提现金额</text>
</view>
<view class="u-flex" style="align-items: baseline">
<text class="price">9999.99</text>
<view class="u-flex" @click="toTixian">
<text class="font-12 color-666 u-m-r-6">去提现</text>
<up-icon
name="arrow-right"
size="12"
color="#666"
@click="toTixian"
></up-icon>
</view>
</view>
</view>
</view>
</view>
</view>
<view class="bottom">
<view class="u-flex">
<view class="title">我的分销</view>
</view>
<view class="u-m-t-36 small-title">已成为100家店铺的分销员</view>
<view class="list">
<view v-for="(item, index) in 3" :key="index" class="shop-item">
<up-image width="104rpx" height="104rpx" radius="8rpx"></up-image>
<view class="u-flex-1 u-m-l-14">
<view class="u-flex justify-between">
<view>
<view class="shop-name">儿童玩具部落</view>
<view class="address u-line-1 u-m-t-16"
>山西省大同市云冈区奥体西路 2666 号中国铁建国际中心</view
>
</view>
<view>
<view class="shouyi">收益</view>
<view class="price">¥99.999</view>
</view>
</view>
</view>
</view>
<view
class="u-flex justify-center font-12 color-666"
style="align-items: baseline"
>
<view>查看全部店铺</view>
<up-icon name="arrow-right" size="12" color="#666"></up-icon>
</view>
</view>
<view class="u-flex">
<view class="title">更多店铺解锁</view>
</view>
<view class="list">
<view
v-for="(item, index) in 3"
:key="index"
class="shop-item"
@click="toShopDetail"
>
<up-image width="104rpx" height="104rpx" radius="8rpx"></up-image>
<view class="u-flex-1 u-m-l-14">
<view class="u-flex justify-between align-center">
<view>
<view class="shop-name">儿童玩具部落</view>
<view class="u-flex">
<view class="tag">曾进入过店铺</view>
</view>
<view class="address u-line-1"
>山西省大同市云冈区奥体西路 2666 号中国铁建国际中心</view
>
</view>
<view class="u-flex u-flex-col justify-center">
<view class="fufei" v-if="true">付费开通</view>
<template v-else>
<view class="font-12 color-333 font-700">自动开通</view>
<view class="u-m-t-8 color-666 font-12">还差10人开通</view>
</template>
</view>
</view>
</view>
</view>
</view>
</view>
<up-popup :show="showPopup" @close="showPopup = false" mode="center" :safe-area-inset-bottom="false">
<view class="u-p-30" style="width: 500rpx;">
{{ popupText }}
总收益即您在所有店铺通过分销获得总金额包括待入账金额但不包含已退款订单
</view>
</up-popup>
</view>
</template>
<script setup>
import { ref } from "vue";
const showPopup = ref(false);
const popupText = ref('');
function questionClick(title) {
if(title=='总收益'){
popupText.value='总收益:即您在所有店铺通过分销获得总金额,包括待入账金额,但不包含已退款订单'
}
if(title=='待入账'){
popupText.value='待入账:即已通过订单分销获得但未达到结算时间的金额,结算时间达到后将会计入可提现金额'
}
showPopup.value=true
}
function back() {
uni.navigateBack({
delta: 1,
});
}
function toShouyiDetail(){
uni.navigateTo({
url: "/distribution/income-details/index",
});
}
function toShopDetail() {
uni.navigateTo({
url: "/distribution/shop-detail/index",
});
}
function toTixian() {
uni.navigateTo({
url: "/distribution/withdraw/index",
});
}
</script>
<style scoped lang="scss">
.top{
position: relative;
.top_content{
position: absolute;
left: 28rpx;
right: 28rpx;
bottom: 0;
padding: 32rpx 28rpx;
.list {
.shop-item {
padding: 16rpx 0;
border-top: 2rpx solid #ededed;
font-size: 24rpx;
color: #666;
display: flex;
align-items: center;
&:first-child {
border-top: none;
}
.fufei {
color: #e8ad7b;
}
.tag {
font-size: 24rpx;
color: #ff1c1c;
background-color: #ffe4e4;
padding: 8rpx 20rpx;
border-radius: 8rpx;
}
.shop-name {
}
.address {
max-width: 390rpx;
}
.shouyi {
font-size: 24rpx;
color: #666;
text-align: center;
}
.price {
font-size: 20rpx;
color: #333;
font-weight: 500;
}
}
}
.price {
font-weight: 700;
color: #333;
font-size: 40rpx;
margin-top: 16rpx;
}
.top {
position: relative;
.top_content {
background-color: #faf7f4;
position: absolute;
left: 28rpx;
right: 28rpx;
bottom: 0;
padding: 32rpx 106rpx 32rpx 56rpx;
border-radius: 24rpx 24rpx 0 0;
}
}
.top_bg {
width: 100%;
height: 530rpx;
}
.bottom {
padding: 34rpx 28rpx;
}
.title {
font-size: 32rpx;
font-weight: 700;
color: #333;
position: relative;
overflow: hidden;
&::after {
display: block;
content: "";
position: absolute;
right: 0;
bottom: 4rpx;
background-color: #9ee708;
border-radius: 10rpx;
z-index: -1;
width: 94.2rpx;
height: 13.98rpx;
flex-shrink: 0;
stroke-width: 4rpx;
stroke: #9ee708d6;
}
}
.small-title {
font-size: 28rpx;
font-weight: 700;
color: #333;
}
</style>

279
distribution/poster.vue Normal file
View File

@@ -0,0 +1,279 @@
<template>
<view class="poster-page">
<view class="preview-container" v-if="posterUrl">
<image :src="posterUrl" mode="widthFix" class="poster-img"></image>
<button class="save-btn" @click="saveToAlbum">保存到相册</button>
</view>
<button class="generate-btn" @click="generatePoster" v-if="!posterUrl">生成海报</button>
<canvas
id="posterCanvas"
type="2d"
:style="{ width: canvasWidth + 'px', height: canvasHeight + 'px' }"
class="canvas-hidden"
></canvas>
</view>
</template>
<script setup>
import { ref, getCurrentInstance } from 'vue'
import { onReady } from '@dcloudio/uni-app'
// 基础变量(不变)
const canvasWidth = ref(375)
const canvasHeight = ref(667)
const posterUrl = ref('')
let canvasNode = null
let ctx = null
const instance = getCurrentInstance()
// 海报配置(不变)
const posterConfig = {
bgImage: 'https://cashier-oss.oss-cn-beijing.aliyuncs.com/upload/1/677c4a5ae43a45eb98c0ae1a6d242021.png',
title: { text: '限时优惠活动', x: 20, y: 40, fontSize: 24, color: '#fff', fontWeight: 'bold' },
desc: { text: '全场商品满200减50限时3天', x: 20, y: 80, fontSize: 16, color: '#fff' },
mainImage: { url: 'https://img.yzcdn.cn/vant/apple-1.jpg', x: 20, y: 120, width: 335, height: 335, radius: 10 },
qrcode: { url: 'https://czg-oss.oss-cn-hangzhou.aliyuncs.com/catering/store/HQ200龙虾仔.JPG', x: 265, y: 500, width: 90, height: 90 },
footerText: { text: '扫码立即参与活动', x: 20, y: 550, fontSize: 14, color: '#333' }
}
// 初始化 Canvas不变
onReady(() => {
const query = uni.createSelectorQuery().in(instance)
query
.select('#posterCanvas')
.fields({ node: true, size: true })
.exec((res) => {
if (!res[0] || !res[0].node) {
console.error('未找到 Canvas 节点')
uni.showToast({ title: 'Canvas节点初始化失败', icon: 'none' })
return
}
canvasNode = res[0].node
canvasNode.width = canvasWidth.value
canvasNode.height = canvasHeight.value
ctx = canvasNode.getContext('2d')
if (!ctx) {
console.error('获取 2D 上下文失败')
uni.showToast({ title: 'Canvas上下文初始化失败', icon: 'none' })
}
})
})
// 绘制背景(依赖修复后的 drawImage
const drawBackground = () => {
return new Promise((resolve, reject) => {
if (!posterConfig.bgImage) return reject('无背景图')
drawImage({
url: posterConfig.bgImage,
x: 0,
y: 0,
width: canvasWidth.value,
height: canvasHeight.value
}).then(resolve).catch(reject)
})
}
// 绘制文字(不变)
const drawText = (options) => {
const { text, x, y, fontSize, color, fontWeight = 'normal', fontFamily = 'sans-serif' } = options
ctx.font = `${fontWeight} ${fontSize}px ${fontFamily}`
ctx.fillStyle = color
ctx.fillText(text, x, y)
}
// 绘制圆角矩形(不变)
const drawRoundRect = (x, y, width, height, radius) => {
ctx.beginPath()
ctx.moveTo(x + radius, y)
ctx.arcTo(x + width, y, x + width, y + height, radius)
ctx.arcTo(x + width, y + height, x, y + height, radius)
ctx.arcTo(x, y + height, x, y, radius)
ctx.arcTo(x, y, x + width, y, radius)
ctx.closePath()
}
// ---------------------- 核心修复drawImage 方法(无需 Image 实例) ----------------------
const drawImage = (options) => {
return new Promise((resolve, reject) => {
const { url, x, y, width, height, radius = 0 } = options
if (!url) return reject('图片路径为空')
if (width <= 0 || height <= 0) return reject(`图片尺寸无效:${width}x${height}`)
// 关键:确保 canvasNode 已初始化(否则无法创建图片对象)
if (!canvasNode) return reject('Canvas 节点未初始化,无法创建图片对象')
// 1. 获取图片临时路径(不变)
uni.getImageInfo({
src: url,
success: (imgInfo) => {
if (imgInfo.width <= 0 || imgInfo.height <= 0) {
return reject(`无效图片:${url}`)
}
// 2. 关键修复:用 Canvas 节点的 createImage() 方法创建图片对象
// 这是微信小程序 Canvas 2D 节点原生支持的方法,类型完全匹配
const image = canvasNode.createImage()
// 3. 监听图片加载完成(接口与标准 Image 一致)
image.onload = () => {
try {
if (radius > 0) {
ctx.save()
drawRoundRect(x, y, width, height, radius)
ctx.clip()
}
// 4. 绘制图片:传入 Canvas 节点创建的图片实例(类型匹配)
ctx.drawImage(image, x, y, width, height)
if (radius > 0) ctx.restore()
resolve()
} catch (drawErr) {
reject(`绘制图片失败:${drawErr.message}`)
}
}
// 监听加载失败
image.onerror = (err) => {
reject(`图片加载失败:${err.message || '未知错误'}`)
}
// 5. 赋值临时路径,触发加载
image.src = imgInfo.path
},
fail: (err) => {
reject(`获取图片信息失败:${err.errMsg}(路径:${url}`)
}
})
})
}
// 生成海报(不变)
const generatePoster = async () => {
try {
if (!canvasNode || !ctx) throw new Error('Canvas 未初始化完成')
ctx.clearRect(0, 0, canvasWidth.value, canvasHeight.value)
// 绘制背景(失败则用纯色兜底)
await drawBackground().catch(() => {
ctx.fillStyle = '#f5f5f5'
ctx.fillRect(0, 0, canvasWidth.value, canvasHeight.value)
})
// 绘制文字
drawText(posterConfig.title)
drawText(posterConfig.desc)
drawText(posterConfig.footerText)
// 绘制图片(主图 + 二维码)
await drawImage(posterConfig.mainImage)
await drawImage(posterConfig.qrcode)
// 延迟确保绘制生效
await new Promise(resolve => setTimeout(resolve, 200))
await convertToImage()
uni.showToast({ title: '海报生成成功', icon: 'success' })
} catch (err) {
console.error('生成失败:', err)
uni.showToast({ title: `生成失败:${err.message}`, icon: 'none' })
}
}
// 转图片(不变)
const convertToImage = () => {
return new Promise((resolve, reject) => {
if (!canvasNode) return reject('Canvas 节点不存在')
uni.canvasToTempFilePath({
canvas: canvasNode,
width: canvasWidth.value,
height: canvasHeight.value,
destWidth: canvasWidth.value * 2,
destHeight: canvasHeight.value * 2,
success: (res) => {
posterUrl.value = res.tempFilePath
resolve()
},
fail: (err) => {
reject(`转图片失败:${err.errMsg}`)
}
}, instance)
})
}
// 保存相册相关方法(不变)
const saveToAlbum = () => {
if (!posterUrl.value) return
uni.getSetting({
success: (res) => {
if (!res.authSetting['scope.writePhotosAlbum']) {
uni.authorize({
scope: 'scope.writePhotosAlbum',
success: saveImage,
fail: () => {
uni.showModal({
title: '权限申请',
content: '需要相册权限才能保存海报',
success: (modalRes) => modalRes.confirm && uni.openSetting()
})
}
})
} else {
saveImage()
}
}
})
}
const saveImage = () => {
uni.saveImageToPhotosAlbum({
filePath: posterUrl.value,
success: () => uni.showToast({ title: '保存成功', icon: 'success' }),
fail: (err) => {
console.error('保存失败:', err)
uni.showToast({ title: '保存失败', icon: 'none' })
}
})
}
</script>
<style scoped>
/* 样式不变 */
.poster-page {
display: flex;
flex-direction: column;
align-items: center;
padding: 20rpx;
}
.canvas-hidden {
position: absolute;
left: -9999rpx;
top: -9999rpx;
z-index: -1;
width: 375px;
height: 667px;
}
.preview-container {
display: flex;
flex-direction: column;
align-items: center;
width: 100%;
}
.poster-img {
width: 100%;
border-radius: 16rpx;
box-shadow: 0 4rpx 10rpx rgba(0, 0, 0, 0.1);
}
.generate-btn, .save-btn {
width: 600rpx;
height: 88rpx;
line-height: 88rpx;
margin-top: 40rpx;
background-color: #07c160;
color: #ffffff;
font-size: 32rpx;
border-radius: 44rpx;
}
.save-btn {
background-color: #1677ff;
margin-top: 20rpx;
}
</style>

View File

@@ -0,0 +1,73 @@
<template>
<view class="min-h-100vh bg-gray">
<view class="box">
<view class="u-flex">
<view class="title">姓名</view>
<view class="u-flex-1 input-box">
<input type="text" class="input" placeholder="请输入姓名" />
</view>
</view>
<view class="u-flex u-m-t-32">
<view class="title">身份证号</view>
<view class="u-flex-1 input-box">
<input type="text" class="input" placeholder="请输入身份证号" />
</view>
</view>
<view class="tips">请输入和当前微信账号为同一实名否则将会提现失败</view>
</view>
<view class="u-m-t-60 u-p-l-28 u-p-r-28">
<view class="submit">提交</view>
<view class="cancel">取消</view>
</view>
</view>
</template>
<style scoped lang="scss">
.min-h-100vh {
padding: 32rpx 28rpx;
}
.tips{
font-size: 24rpx;
line-height: 40rpx;
color: #ff1c1c;
margin-top: 32rpx;
}
.box {
background-color: #fff;
padding: 32rpx 28rpx;
font-size: 28rpx;
color: #333;
.title{
min-width: 160rpx;
}
.input-box {
border-bottom: 2rpx solid #ededed;
.input{
}
}
}
.submit{
background: #FFD158;
border-radius: 100rpx;
padding: 22rpx 288rpx;
font-size: 32rpx;
line-height: 46rpx;
color: #ffffff;
text-align: center;
font-weight: 700;
white-space: nowrap;
}
.cancel{
color: #999999;
white-space: nowrap;
border-radius: 100rpx;
padding: 22rpx 288rpx;
font-size: 32rpx;
line-height: 46rpx;
text-align: center;
}
</style>

View File

@@ -0,0 +1,110 @@
<template>
<view class="">
<up-popup
:show="show"
bgColor="transparent"
:safeAreaInsetBottom="false"
:closeOnClickOverlay="false"
@close="close"
mode="center"
>
<view class="box">
<view class="u-flex top justify-between u-p-32">
<text class="title">绑定上级</text>
<up-icon name="close" @click="close"></up-icon>
</view>
<view class="info">
<view class="u-flex u-col-center">
<view class="color-333 small-title">上级邀请码</view>
<view class="u-m-l-32 u-flex-1 border">
<input placeholder="请输入上级邀请码" v-model="code" />
</view>
</view>
<view class="u-m-t-32 u-flex u-col-center" style="gap: 54rpx">
<view class="cancel" @click="close">取消</view>
<view class="confirm" @click="confirm">确认</view>
</view>
</view>
</view>
</up-popup>
</view>
</template>
<script setup>
import { ref } from "vue";
const show = defineModel({
type: Boolean,
default: false,
});
const code = ref("");
const emits = defineEmits(["cancel", "confirm"]);
function close() {
show.value = false;
emits("cancel");
}
function confirm() {
if (code.value == "") {
uni.showToast({
title: "请输入上级邀请码",
icon: "none",
});
return;
}
show.value = false;
emits("confirm", code.value);
}
</script>
<style lang="scss" scoped>
.border {
border: 2rpx solid #d9d9d9;
padding: 18rpx;
border-radius: 4rpx;
}
.box {
width: 586rpx;
background-color: #fff;
border-radius: 16rpx;
overflow: hidden;
.title {
color: #000000;
font-size: 32rpx;
font-weight: 700;
}
.top {
border-bottom: 2rpx solid #ededed;
}
.info {
padding: 32rpx 40rpx 40rpx 40rpx;
font-size: 28rpx;
}
.small-title {
min-width: 84rpx;
text-align: right;
}
}
.cancel {
padding: 14rpx 76rpx;
border-radius: 36rpx;
border: 2rpx solid #e8ad7b;
color: #e8ad7b;
font-size: 32rpx;
font-weight: 400;
white-space: nowrap;
line-height: 48rpx;
}
.confirm {
padding: 14rpx 76rpx;
border-radius: 36rpx;
background-color: #e8ad7b;
border: 2rpx solid #e8ad7b;
color: #fff;
font-size: 32rpx;
font-weight: 400;
line-height: 48rpx;
white-space: nowrap;
}
</style>

View File

@@ -0,0 +1,174 @@
<template>
<view class="">
<view class="w-qrcode">
<w-qrcode
:options="codeOptions"
:opacity="0"
ref="wQrcode"
@generate="(e) => qrcodeResult(e)"
></w-qrcode>
</view>
<up-popup
:show="show"
bgColor="transparent"
:safeAreaInsetBottom="false"
:closeOnClickOverlay="false"
@close="close"
mode="center"
>
<view class="box">
<view class="info">
<view class="u-flex justify-center">
<up-avatar size="214rpx"></up-avatar>
</view>
<view
class="u-m-t-48 font-14 font-700 color-333 text-center line-height-54"
>
<view>躺平小王子 </view>
<view>158****0001</view>
</view>
<view class="u-m-t-16 font-14 line-height-54 text-center">
<text class="color-666">邀请码</text>
<text class="u-m-l-16 u-m-r-16 color-333 font-16 font-700"
>59124551</text
>
<text class="" style="color: #fe6d11" @click="copyCode">复制</text>
</view>
<view class="u-flex justify-center" style="margin-top: 90rpx">
<!-- <w-qrcode
:options="codeOptions"
:opacity="1"
ref="wQrcode"
@generate="(e) => qrcodeResult(e)"
></w-qrcode> -->
<up-image width="322rpx" height="322rpx" :src="code"></up-image>
</view>
<view class="u-m-t-60 u-flex u-col-center justify-center">
<view class="confirm" @click="save">保存图片</view>
</view>
</view>
</view>
</up-popup>
</view>
</template>
<script setup>
import wQrcode from "@/uni_modules/wmf-code/components/w-qrcode/w-qrcode.vue";
import { ref } from "vue";
const codeOptions = ref({
size: 200,
code: "1234",
});
function copyCode() {
uni.setClipboardData({
data: "hello",
success: function () {
console.log("success");
},
});
}
const code = ref("");
function qrcodeResult(e) {
console.log("qrcodeResult", e);
code.value=e.img.tempFilePath
console.log('code',code.value)
}
const show = defineModel({
type: Boolean,
default: false,
});
const emits = defineEmits(["cancel", "confirm"]);
function close() {
show.value = false;
emits("cancel");
}
function save() {
show.value = false;
uni.saveImageToPhotosAlbum({
filePath: code.value,
success: function () {
uni.showToast({
title: "保存成功",
});
},
fail: function () {
uni.showToast({
title: "保存失败",
});
},
});
emits("confirm", code.value);
}
</script>
<style lang="scss" scoped>
:deep(.info .canvas) {
opacity: 1;
}
.border {
border: 2rpx solid #d9d9d9;
padding: 18rpx;
border-radius: 4rpx;
}
.box {
width: 638rpx;
background-color: #fff;
border-radius: 16rpx;
overflow: hidden;
.title {
color: #000000;
font-size: 32rpx;
font-weight: 700;
}
.top {
border-bottom: 2rpx solid #ededed;
}
.info {
padding: 96rpx 40rpx 96rpx 40rpx;
font-size: 28rpx;
}
.small-title {
min-width: 84rpx;
text-align: right;
}
}
.cancel {
padding: 14rpx 76rpx;
border-radius: 36rpx;
border: 2rpx solid #e8ad7b;
color: #e8ad7b;
font-size: 32rpx;
font-weight: 400;
white-space: nowrap;
line-height: 48rpx;
}
.confirm {
padding: 14rpx 76rpx;
border-radius: 16rpx;
background-color: #e8ad7b;
border: 2rpx solid #e8ad7b;
color: #fff;
font-size: 32rpx;
font-weight: 400;
line-height: 48rpx;
white-space: nowrap;
}
.line-height-54 {
line-height: 54rpx;
}
.w-qrcode{
position: fixed;
left: -9999px;
top: -9999px;
z-index:-1;
}
</style>

View File

@@ -0,0 +1,400 @@
<template>
<view class="min-h-100vh bg-gray">
<up-navbar
bg-color="transparent"
title="分销中心"
@leftClick="back"
:fixed="true"
></up-navbar>
<view class="top">
<image class="top_bg" src="/distribution/static/top_bg.png"></image>
<view class="box type1">
<view class="u-flex align-center justify-between">
<view class="u-flex align-center">
<up-avatar size="62rpx" />
<text class="u-m-l-14 font-14 color-333 font-700"
>这里是店铺名称</text
>
</view>
<view>
<template v-if="false">
<view class="font-12 color-666"> 上级用户昵称15812340001 </view>
</template>
<template v-if="true">
<view class="bind" @click="showBindShangji = true">
绑定上级
</view>
</template>
</view>
</view>
<view class="top_content u-m-t-32" v-if="false">
<template v-if="false">
<view class="color-333 font-16 font-700"> 如何成为分销员 </view>
<view class="u-m-t-16 color-666 font-14">
<view> 需要邀请人数1000</view>
<view>是否需要邀请人数下单/</view>
<view>每人可获得的分销奖励次数永久/X次</view>
</view>
</template>
<template v-if="false">
<view class="color-333 font-16 font-700 text-center">
如何成为分销员
</view>
<view class="u-m-t-16 color-666 font-14 text-center">
<view>只需付费X元即可成为分销员</view>
</view>
</template>
<template v-if="false">
<view class="color-333 font-16 font-700 text-center">
如何成为分销员
</view>
<view class="u-m-t-16 color-666 font-14 text-center">
<view>请联系商家咨询详情</view>
</view>
</template>
</view>
<view class="top_content type1 u-m-t-32">
<template v-if="true">
<view class="font-14">
<view>
<text class="color-666">我的分销等级</text>
<text class="color-333 font-700">一级 普通分销员</text>
</view>
<view class="u-m-t-28 u-flex align-center">
<text class="color-666">距离下一级还差</text>
<text class="color-333 font-700 u-m-r-18">100 </text>
<up-icon name="question-circle" size="24rpx" color="#666" />
</view>
<view class="u-flex u-m-t-28">
<view class="u-flex-1">
<view class="u-flex align-center">
<text class="u-m-r-10 font-12 color-666">总收益</text>
<up-icon name="question-circle" size="24rpx" color="#666" />
</view>
<view class="u-m-t-16 price">9925.56</view>
</view>
<view class="u-flex-1">
<view class="u-flex align-center">
<text class="u-m-r-10 font-12 color-666">待入账</text>
<up-icon name="question-circle" size="24rpx" color="#666" />
</view>
<view class="u-m-t-16 price">9925.56</view>
</view>
</view>
</view>
</template>
</view>
</view>
</view>
<template v-if="false">
<view class="bottom">
<view class="u-flex">
<view class="title">规则说明</view>
</view>
<view>
<template v-if="true">
<view class="font-12 color-666 u-m-t-16">
<view>
<view> 我的收益什么时候可以到账</view>
<view> 分销的结算时长为X天</view>
</view>
<view class="u-m-t-40">
<view>怎么样才能升级分销员等级</view>
<view>邀请的有效人数达到X人即可升级</view>
<view> 消费金额总计达到X元即可升级</view>
<view> 什么是有效邀请人数</view>
<view>被邀请人在店铺消费过即有一笔订单完成才算有效</view>
</view>
<view class="u-m-t-40">
<view>消费金额如何计算</view>
<view
>消费金额是计算您和您邀请的人在店铺消费的总金额但退款订单不计入</view
>
</view>
</view>
</template>
</view>
</view>
<view class="parse-html">
<up-parse :content="content"></up-parse>
</view>
</template>
<view class="bottom type1" v-if="true">
<view class="u-flex justify-between align-center">
<view class="u-flex align-center">
<view class="color-333 font-16 u-m-r-6">我的下级30/10</view>
<up-icon name="question-circle" size="24rpx" color="#666" />
</view>
<view class="font-10 color-999">一级分成1.99%二级分成3.99%</view>
</view>
<view class="u-m-t-48 font-14">
<view class="u-flex justify-between color-333">
<view>用户</view>
<view>获得利益</view>
<view>邀请时间</view>
</view>
<view class="u-m-t-16">
<view
v-for="(item, index) in 3"
:key="index"
class="u-flex justify-between align-center recoder-item color-666 font-12"
>
<view class="">
<view>用户昵称</view>
<view>158****0001</view>
</view>
<view>0一级</view>
<view>2025/03/18 19:12</view>
</view>
</view>
</view>
</view>
<view class="tips u-m-t-32"
>您的分销员身份已取消不再获得分成有疑问可联系商家</view
>
<view class="u-flex justify-center bottom-btn">
<view class="copy" @click="copyCode">
<view>复制邀请码</view>
<view>123457891231</view>
</view>
<view class="u-flex u-flex-col justify-center">
<view class="share" @click="showSharePopup=true">分享邀请</view>
</view>
</view>
<bindShangji v-model="showBindShangji"></bindShangji>
<sharePopup v-model="showSharePopup"></sharePopup>
</view>
</template>
<script setup>
import bindShangji from "./components/bind-shangji.vue";
import sharePopup from "./components/share-popup.vue";
const showBindShangji = ref(false);
const showSharePopup=ref(false);
import { ref } from "vue";
const content = ref("123");
function back() {
uni.navigateBack({
delta: 1,
});
}
function copyCode() {
uni.setClipboardData({
data: "hello",
success: function () {
console.log("success");
},
});
}
</script>
<style scoped lang="scss">
.input-number-box {
width: 428rpx;
padding-bottom: 10rpx;
border-bottom: 1px solid #999;
font-size: 28rpx;
align-items: baseline;
.fuhao {
font-size: 64rpx;
color: #333;
}
.input-number {
flex: 1;
height: 100%;
font-size: 28rpx;
color: #333;
padding-left: 24rpx;
padding-right: 10rpx;
}
.all-in {
font-size: 28rpx;
color: #fe7e00;
}
}
.list {
.shop-item {
padding: 32rpx 28rpx;
border-bottom: 2rpx solid #ededed;
font-size: 28rpx;
color: #666;
display: flex;
align-items: center;
&:last-child {
border-bottom: none;
}
.fufei {
color: #e8ad7b;
}
.tag {
font-size: 24rpx;
color: #ff1c1c;
background-color: #ffe4e4;
padding: 8rpx 20rpx;
border-radius: 8rpx;
}
.name {
color: #333;
font-weight: 700;
}
.shouxufei {
}
.shouyi {
font-size: 24rpx;
color: #666;
text-align: center;
}
}
}
.status {
font-size: 28rpx;
font-weight: 700;
text-align: right;
color: #333333;
&.fail {
color: #ff1c1c;
}
}
.lingqu {
font-size: 28rpx;
border-radius: 8rpx;
background: #fe6d11;
padding: 8rpx 16rpx;
color: #ffffff;
}
.price {
font-weight: 700;
font-size: 40rpx;
color: #333;
}
.top {
position: relative;
.box {
position: absolute;
left: 0;
right: 0;
bottom: 0;
padding: 28rpx 28rpx 52rpx 28rpx;
&.type1 {
padding-bottom: 0;
}
}
.top_content {
border: 1px solid rgba(255, 255, 255, 0.8);
border-radius: 16rpx;
flex-shrink: 0;
fill: #ffffff3b;
stroke-width: 2rpx;
padding: 32rpx 28rpx;
stroke: #fff;
filter: drop-shadow(2rpx -4rpx 13.4rpx #ff6f0124);
backdrop-filter: blur(5.1rpx);
&.type1 {
filter: none;
border: none;
background-color: #fcf5ed;
border-radius: 36rpx 36rpx 0 0 ;
padding: 32rpx 36rpx;
}
}
}
.btn-group {
position: absolute;
right: 28rpx;
top: 50%;
transform: translateY(-50%);
.btn {
padding: 8rpx 16rpx;
border-radius: 8rpx;
font-size: 24rpx;
border: 2rpx solid #fe6d11;
&.shiming {
color: #fe6d11;
}
&.tixian {
color: #fff;
background-color: #fe6d11;
}
}
}
.tips {
padding: 16rpx 18rpx;
background: #ffe2e2;
padding: 16rpx 18rpx;
font-size: 28rpx;
line-height: 48rpx;
color: #ff1c1c;
}
.bind {
padding: 8rpx 32rpx;
border-radius: 8rpx;
font-size: 24rpx;
border: 2rpx solid #fe6d11;
color: #fff;
background-color: #fe6d11;
}
.top_bg {
width: 100%;
height: 580rpx;
}
.bottom {
margin: 0 28rpx;
border-radius: 36rpx;
background-color: #fff;
transform: translateY(-20rpx);
padding: 32rpx 28rpx;
&.type1 {
transform: translateY(0);
margin: 0;
padding-bottom: 42rpx;
}
}
.title {
font-size: 32rpx;
font-weight: 700;
color: #333;
}
.small-title {
font-size: 28rpx;
font-weight: 700;
color: #333;
}
.parse-html {
margin: 32rpx 28rpx;
font-size: 28rpx;
}
.recoder-item {
padding: 16rpx 0;
border-bottom: 2rpx solid #ededed;
}
.share {
border-radius: 16rpx;
background: #e8ad7b;
padding: 14rpx 76rpx;
font-size: 32rpx;
line-height: 48rpx;
color: #fff;
}
.copy {
padding: 4rpx 30rpx;
border-radius: 18rpx;
border: 2rpx solid #e8ad7b;
background: #fff;
font-size: 28rpx;
color: #e8ad7b;
line-height: 48rpx;
text-align: center;
}
.bottom-btn {
position: fixed;
left: 84rpx;
right: 84rpx;
bottom: 100rpx;
white-space: nowrap;
gap: 54rpx;
}
</style>

View File

@@ -0,0 +1,226 @@
<template>
<view class="min-h-100vh bg-gray">
<up-navbar
bg-color="transparent"
title="提现"
@leftClick="back"
:fixed="true"
></up-navbar>
<view class="top">
<image class="top_bg" src="/distribution/static/top_bg.png"></image>
<view class="top_content">
<view class="color-333 font-16"> 提现金额 </view>
<view class="u-m-t-32 u-flex input-number-box">
<text class="fuhao">¥</text>
<input
type="digit"
class="input-number"
placeholder="最小提现金额为30"
/>
<text class="all-in">全部提现</text>
</view>
<view class="color-666 font-12 u-m-t-16">
<text>可提现金额399.99</text>
<text class="u-m-l-20">手续费为8%</text>
</view>
<view class="btn-group">
<view class="btn shiming" @click="toShiming">实名认证</view>
<view class="btn tixian u-m-t-32">立即提现</view>
</view>
</view>
</view>
<view class="bottom">
<view class="u-flex">
<view class="title">提现记录</view>
</view>
<view class="list">
<view v-for="(item, index) in 3" :key="index" class="shop-item">
<view class="u-flex-1">
<view class="u-flex justify-between">
<view>
<view class="name">提现</view>
<view class="shouxufei u-m-t-16"
>手续费9.99</view
>
<view class="font-12 color-999 u-m-t-10">
时间2017/8/9 21:02
</view>
</view>
<view class="u-flex u-flex-col justify-center">
<template v-if="!true">
<view class="lingqu">点击领取</view>
<view class="price reduce">-100</view>
</template>
<template v-else>
<view class="status fail">提现失败</view>
<view class="price">-100</view>
</template>
</view>
</view>
</view>
</view>
</view>
</view>
</view>
</template>
<script setup>
function back() {
uni.navigateBack({
delta: 1,
});
}
function toShiming() {
uni.navigateTo({
url: "/distribution/shiming/index",
});
}
</script>
<style scoped lang="scss">
.input-number-box {
width: 428rpx;
padding-bottom: 10rpx;
border-bottom: 1px solid #999;
font-size: 28rpx;
align-items: baseline;
.fuhao {
font-size: 64rpx;
color: #333;
}
.input-number {
flex: 1;
height: 100%;
font-size: 28rpx;
color: #333;
padding-left: 24rpx;
padding-right: 10rpx;
}
.all-in {
font-size: 28rpx;
color: #fe7e00;
}
}
.list {
.shop-item {
padding: 32rpx 28rpx;
border-bottom: 2rpx solid #ededed;
font-size: 28rpx;
color: #666;
display: flex;
align-items: center;
&:last-child {
border-bottom: none;
}
.fufei {
color: #e8ad7b;
}
.tag {
font-size: 24rpx;
color: #ff1c1c;
background-color: #ffe4e4;
padding: 8rpx 20rpx;
border-radius: 8rpx;
}
.name {
color: #333;
font-weight: 700;
}
.shouxufei {
}
.shouyi {
font-size: 24rpx;
color: #666;
text-align: center;
}
}
}
.status{
font-size: 28rpx;
font-weight: 700;
text-align: right;
color: #333333;
&.fail{
color: #ff1c1c;
}
}
.lingqu{
font-size: 28rpx;
border-radius: 8rpx;
background: #FE6D11;
padding: 8rpx 16rpx;
color: #ffffff;
}
.price {
font-weight: 700;
font-size: 48rpx;
margin-top: 16rpx;
color: #FE7E00;
text-align: right;
&.reduce{
color: #666;
}
}
.top {
position: relative;
.top_content {
border: 1px solid rgba(255, 255, 255, 0.8);
position: absolute;
left: 28rpx;
right: 28rpx;
bottom: 52rpx;
padding: 28rpx 28rpx 52rpx 28rpx;
border-radius: 16rpx;
flex-shrink: 0;
fill: #ffffff3b;
stroke-width: 2rpx;
stroke: #fff;
filter: drop-shadow(2rpx -4rpx 13.4rpx #ff6f0124);
backdrop-filter: blur(5.1rpx);
.btn-group {
position: absolute;
right: 28rpx;
top: 50%;
transform: translateY(-50%);
.btn {
padding: 8rpx 16rpx;
border-radius: 8rpx;
font-size: 24rpx;
border: 2rpx solid #fe6d11;
&.shiming {
color: #fe6d11;
}
&.tixian {
color: #fff;
background-color: #fe6d11;
}
}
}
}
}
.top_bg {
width: 100%;
height: 580rpx;
}
.bottom {
margin: 0 28rpx;
border-radius: 16rpx 16rpx 0 0;
background-color: #fff;
transform: translateY(-20rpx);
}
.title {
font-size: 32rpx;
font-weight: 700;
color: #333;
padding: 28rpx;
}
.small-title {
font-size: 28rpx;
font-weight: 700;
color: #333;
}
</style>