Initial commit: MSH System\n\n- msh_single_uniapp: Vue 2 + UniApp 前端(微信小程序/H5/App/支付宝小程序)\n- msh_crmeb_22: Spring Boot 2.2 后端(C端API/管理端/业务逻辑)\n- models-integration: AI服务集成(Coze/KieAI/腾讯ASR)\n- docs: 产品文档与设计稿

This commit is contained in:
2026-02-28 05:40:21 +08:00
commit 14d29d51c0
2182 changed files with 482509 additions and 0 deletions

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,356 @@
<template>
<view :data-theme="theme">
<view class="promoter-list">
<view class='promoterHeader'>
<view class='headerCon acea-row row-between'>
<view>
<view class='name'>推广人数</view>
<view><text class='num'>{{peopleData.count}}</text></view>
</view>
<view class='iconfont icon-tuandui'></view>
</view>
</view>
<view class="pad30">
<view class='nav acea-row row-around'>
<view :class="grade == 0 ? 'item on' : 'item'" @click='setType(0)'>一级({{peopleData.total}})</view>
<view :class="grade == 1 ? 'item on' : 'item'" @click='setType(1)'>二级({{peopleData.totalLevel}})
</view>
</view>
<view class='search acea-row row-between-wrapper'>
<view class='input'>
<input placeholder='点击搜索会员名称' placeholder-class='placeholder' v-model="keyword"
@confirm="submitForm" confirm-type='search' name="search" maxlength="10"></input>
</view>
<button class='iconfont icon-sousuo2' @click="submitForm"></button>
</view>
<view class='list'>
<view class="sortNav acea-row row-middle">
<view class="sortItem" @click='setSort("childCount","ASC")' v-if="sort == 'childCountDESC'">团队排序
<image :src="urlDomain+'crmebimage/perset/staticImg/sort1.png'"></image>
</view>
<view class="sortItem" @click='setSort("childCount")' v-else-if="sort == 'childCountASC'">团队排序
<image :src="urlDomain+'crmebimage/perset/staticImg/sort3.png'"></image>
</view>
<view class="sortItem" @click='setSort("childCount","DESC")' v-else>团队排序
<image :src="urlDomain+'crmebimage/perset/staticImg/sort2.png'"></image>
</view>
<view class="sortItem" @click='setSort("numberCount","ASC")' v-if="sort == 'numberCountDESC'">
金额排序
<image :src="urlDomain+'crmebimage/perset/staticImg/sort1.png'"></image>
</view>
<view class="sortItem" @click='setSort("numberCount")' v-else-if="sort == 'numberCountASC'">金额排序
<image :src="urlDomain+'crmebimage/perset/staticImg/sort3.png'"></image>
</view>
<view class="sortItem" @click='setSort("numberCount","DESC")' v-else>金额排序
<image :src="urlDomain+'crmebimage/perset/staticImg/sort2.png'"></image>
</view>
<view class="sortItem" @click='setSort("orderCount","ASC")' v-if="sort == 'orderCountDESC'">订单排序
<image :src="urlDomain+'crmebimage/perset/staticImg/sort1.png'"></image>
</view>
<view class="sortItem" @click='setSort("orderCount")' v-else-if="sort == 'orderCountASC'">订单排序
<image :src="urlDomain+'crmebimage/perset/staticImg/sort3.png'"></image>
</view>
<view class="sortItem" @click='setSort("orderCount","DESC")' v-else>订单排序
<image :src="urlDomain+'crmebimage/perset/staticImg/sort2.png'"></image>
</view>
</view>
<block v-for="(item,index) in recordList" :key="index">
<view class='item acea-row row-between-wrapper'>
<view class="picTxt acea-row row-between-wrapper">
<view class='pictrue'>
<image :src='item.avatar'></image>
</view>
<view class='text'>
<view class='name line1'>{{item.nickname}}</view>
<view>加入时间: {{item.time ? item.time.split(' ')[0] : ''}}</view>
</view>
</view>
<view class="right">
<view><text class='num font-color'>{{item.childCount ? item.childCount : 0}}</text>
</view>
<view><text class="num">{{item.orderCount ? item.orderCount : 0}}</text></view>
<view><text class="num">{{item.numberCount ? item.numberCount : 0}}</text></view>
</view>
</view>
</block>
<Loading :loaded="status" :loading="loadingList"></Loading>
<block v-if="recordList.length == 0 && isShow">
<emptyPage title="暂无推广人数~"></emptyPage>
</block>
</view>
</view>
</view>
</view>
</template>
<script>
import {
spreadPeople,
spreadPeoCount
} from '@/api/user.js';
import {toLogin} from '@/libs/login.js';
import {mapGetters} from "vuex";
import emptyPage from '@/components/emptyPage.vue'
import Loading from "@/components/Loading";
import {setThemeColor} from '@/utils/setTheme.js'
const app = getApp();
export default {
components: {
Loading,
emptyPage
},
data() {
return {
urlDomain: this.$Cache.get("imgHost"),
page: 1,
limit: 20,
keyword: '',
sort: '',
isAsc: '',
sortKey: '',
grade: 0,
status: false,
loadingList: false,
recordList: [],
peopleData: {},
isShow: false,
theme:app.globalData.theme,
bgColor:'#e93323'
};
},
computed: mapGetters(['isLogin']),
onLoad() {
if (this.isLogin) {
this.userSpreadNewList();
this.spreadPeoCount();
} else {
toLogin();
}
let that = this;
that.bgColor = setThemeColor();
uni.setNavigationBarColor({
frontColor: '#ffffff',
backgroundColor:that.bgColor,
});
},
onShow: function() {
if (this.is_show) this.userSpreadNewList();
},
onHide: function() {
this.is_show = true;
},
methods: {
setSort: function(sortKey, isAsc) {
let that = this;
that.isAsc = isAsc;
that.sort = sortKey + isAsc;
that.sortKey = sortKey;
that.page = 1;
that.limit = 20;
that.status = false;
that.$set(that, 'recordList', []);
that.userSpreadNewList();
},
submitForm: function() {
this.page = 1;
this.limit = 20;
this.status = false;
this.$set(this, 'recordList', []);
this.userSpreadNewList();
},
setType: function(grade) {
if (this.grade != grade) {
this.grade = grade;
this.page = 1;
this.limit = 20;
this.keyword = '';
this.sort = '';
this.isAsc = '';
this.status = false;
this.loadingList = false;
this.$set(this, 'recordList', []);
this.userSpreadNewList();
}
},
spreadPeoCount() {
spreadPeoCount().then(res => {
this.peopleData = res.data;
});
},
userSpreadNewList: function() {
let that = this;
let page = that.page;
let limit = that.limit;
let status = that.status;
let keyword = that.keyword;
let isAsc = that.isAsc;
let sortKey = that.sortKey;
let grade = that.grade;
let recordList = that.recordList;
let recordListNew = [];
if (that.loadingList) return;
if (status == true) return;
spreadPeople({
page: page,
limit: limit,
keyword: keyword,
grade: grade,
sortKey: sortKey,
isAsc: isAsc
}).then(res => {
let recordListData = res.data.list ? res.data.list : [];
let len = recordListData.length;
recordListNew = recordList.concat(recordListData);
that.status = limit > len;
that.page = page + 1;
that.$set(that, 'recordList', recordListNew || []);
that.loadingList = false;
if(that.recordList.length===0) that.isShow = true;
});
}
},
onReachBottom: function() {
this.userSpreadNewList();
}
}
</script>
<style scoped lang="scss">
.promoter-list .nav {
background-color: #fff;
height: 86rpx;
line-height: 86rpx;
font-size: 28rpx;
color: #282828;
border-bottom: 1rpx solid #eee;
border-top-left-radius: 14rpx;
border-top-right-radius: 14rpx;
margin-top: -30rpx;
}
.promoterHeader{
@include main_bg_color(theme);
}
.promoter-list .nav .item.on {
@include main_color(theme);
@include tab_border_bottom(theme);
}
.promoter-list .search {
width: 100%;
background-color: #fff;
height: 100rpx;
padding: 0 24rpx;
box-sizing: border-box;
border-bottom-left-radius: 14rpx;
border-bottom-right-radius: 14rpx;
}
.promoter-list .search .input {
width: 592rpx;
height: 60rpx;
border-radius: 50rpx;
background-color: #f5f5f5;
text-align: center;
position: relative;
}
.promoter-list .search .input input {
height: 100%;
font-size: 26rpx;
width: 610rpx;
text-align: center;
}
.promoter-list .search .input .placeholder {
color: #bbb;
}
.promoter-list .search .input .iconfont {
position: absolute;
right: 28rpx;
color: #999;
font-size: 28rpx;
top: 50%;
transform: translateY(-50%);
}
.promoter-list .search .iconfont {
font-size: 32rpx;
color: #515151;
height: 60rpx;
line-height: 60rpx;
}
.promoter-list .list {
margin-top: 20rpx;
}
.promoter-list .list .sortNav {
background-color: #fff;
height: 76rpx;
border-bottom: 1rpx solid #eee;
color: #333;
font-size: 28rpx;
border-top-left-radius: 14rpx;
border-top-right-radius: 14rpx;
}
.promoter-list .list .sortNav .sortItem {
text-align: center;
flex: 1;
}
.promoter-list .list .sortNav .sortItem image {
width: 24rpx;
height: 24rpx;
margin-left: 6rpx;
vertical-align: -3rpx;
}
.promoter-list .list .item {
background-color: #fff;
border-bottom: 1rpx solid #eee;
height: 152rpx;
padding: 0 24rpx;
font-size: 24rpx;
color: #666;
}
.promoter-list .list .item .picTxt .pictrue {
width: 106rpx;
height: 106rpx;
border-radius: 50%;
}
.promoter-list .list .item .picTxt .pictrue image {
width: 100%;
height: 100%;
border-radius: 50%;
border: 3rpx solid #fff;
box-shadow: 0 0 10rpx #aaa;
box-sizing: border-box;
}
.promoter-list .list .item .picTxt .text {
// width: 304rpx;
font-size: 24rpx;
color: #666;
margin-left: 14rpx;
}
.promoter-list .list .item .picTxt .text .name {
font-size: 28rpx;
color: #333;
margin-bottom: 13rpx;
}
.promoter-list .list .item .right {
text-align: right;
font-size: 22rpx;
color: #333;
}
.promoter-list .list .item .right .num {
margin-right: 7rpx;
}
</style>

View File

@@ -0,0 +1,204 @@
<template>
<view :data-theme="theme">
<view class="promoter-order">
<view class='promoterHeader'>
<view class='headerCon acea-row row-between-wrapper'>
<view>
<view class='name'>累积推广订单</view>
<view><text class='num'>{{recordCount || 0}}</text></view>
</view>
<view class='iconfont icon-2'></view>
</view>
</view>
<view class='list pad30' v-if="recordList.length>0">
<block v-for="(item,index) in recordList" :key="index">
<view class='item'>
<view class='title acea-row row-column row-center'>
<view class='data'>{{item.time}}</view>
<view>本月累计推广订单{{item.count || 0}}</view>
</view>
<view class='listn'>
<block v-for="(child,indexn) in item.child" :key="indexn">
<view class='itenm borRadius14'>
<view class='top acea-row row-between-wrapper'>
<view class='pictxt acea-row row-between-wrapper'>
<view class='pictrue'>
<image :src='child.avatar'></image>
</view>
<view class='text line1'>{{child.nickname}}</view>
</view>
<view class='money'>返佣<text class='font-color'>{{child.number}}</text></view>
<!-- <view class='money' v-if="child.type == 'brokerage'">返佣<text class='font-color'>{{child.number}}</text></view>
<view class='money' v-else>暂未返佣<text class='font-color'>{{child.number}}</text></view> -->
</view>
<view class='bottom'>
<view><text class='name'>订单编号</text>{{child.orderId}}</view>
<view><text class='name'>下单时间</text>{{child.time}}</view>
</view>
</view>
</block>
</view>
</view>
</block>
</view>
<view v-if="recordList.length == 0">
<emptyPage title="暂无推广订单~"></emptyPage>
</view>
</view>
</view>
</template>
<script>
import {spreadOrder} from '@/api/user.js';
import {toLogin} from '@/libs/login.js';
import {mapGetters} from "vuex";
import emptyPage from '@/components/emptyPage.vue'
import {setThemeColor} from '@/utils/setTheme.js'
const app = getApp();
export default {
components: {
emptyPage
},
data() {
return {
page: 1,
limit: 20,
status: false,
recordList: [],
recordCount: 0,
time: 0,
theme:app.globalData.theme,
bgColor:'#e93323'
};
},
computed: mapGetters(['isLogin']),
onLoad() {
if (this.isLogin) {
this.getRecordOrderList();
} else {
toLogin();
}
let that = this;
that.bgColor = setThemeColor();
uni.setNavigationBarColor({
frontColor: '#ffffff',
backgroundColor:that.bgColor,
});
},
methods: {
stringToDate : function(data){
let str = data.replace(/-/g,'/');
let date = new Date(str);
return data;
},
getRecordOrderList: function() {
let that = this;
let page = that.page;
let limit = that.limit;
let status = that.status;
let recordList = that.recordList;
let newList = [];
if (status == true) return;
spreadOrder({
page: page,
limit: limit
}).then(res => {
let recordListData = res.data.list ? res.data.list : [];
// 每页返回的总条数;
let len = 0;
for(let i = 0;i<recordListData.length;i++) {
len = len + recordListData[i].child.length;
let str = recordListData[i].time.replace(/-/g,'/');
let date = new Date(str).getTime();
if(that.time === date){
that.$set(that.recordList[i],'child',that.recordList[i].child.concat(recordListData[i].child));
}else{
recordListData.forEach((item,index)=>{
if(recordListData[i]==item){
newList.push(item);
}
})
that.$set(that, 'recordList', recordList.concat(newList));
}
that.time = date;
};
that.recordCount = res.data.count || 0;
that.status = limit > len;
that.page = page + 1;
});
}
},
onReachBottom() {
this.getRecordOrderList()
}
}
</script>
<style scoped lang="scss">
.promoter-order .list .item .title {
height: 133rpx;
font-size: 26rpx;
color: #999;
}
.promoterHeader{
@include main_bg_color(theme);
}
.promoter-order .list .item .title .data {
font-size: 28rpx;
color: #282828;
margin-bottom: 5rpx;
}
.promoter-order .list .item .listn .itenm {
background-color: #fff;
}
.promoter-order .list .item .listn .itenm~.itenm {
margin-top: 20rpx;
}
.promoter-order .list .item .listn .itenm .top {
padding: 0 24rpx;
border-bottom: 1rpx solid #eee;
height: 100rpx;
}
.promoter-order .list .item .listn .itenm .top .pictxt {
width: 320rpx;
}
.promoter-order .list .item .listn .itenm .top .pictxt .text {
width: 230rpx;
font-size: 30rpx;
color: #282828;
}
.promoter-order .list .item .listn .itenm .top .pictxt .pictrue {
width: 66rpx;
height: 66rpx;
}
.promoter-order .list .item .listn .itenm .top .pictxt .pictrue image {
width: 100%;
height: 100%;
border-radius: 50%;
border: 3rpx solid #fff;
box-sizing: border-box;
box-shadow: 0 0 15rpx #aaa;
}
.promoter-order .list .item .listn .itenm .top .money {
font-size: 28rpx;
}
.promoter-order .list .item .listn .itenm .bottom {
padding: 20rpx 24rpx;
font-size: 28rpx;
color: #666;
line-height: 1.6;
}
.promoter-order .list .item .listn .itenm .bottom .name {
color: #999;
}
</style>

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,396 @@
<template>
<view class="page">
<view class='distribution-posters'>
<swiper :indicator-dots="indicatorDots" :autoplay="autoplay" :circular="circular" :interval="interval"
:duration="duration" @change="bindchange" previous-margin="40px" next-margin="40px">
<block v-for="(item,index) in spreadList" :key="index">
<swiper-item>
<image :src="item.pic" class="slide-image" :class="swiperIndex == index ? 'active' : 'quiet'"
mode='aspectFill' />
</swiper-item>
</block>
</swiper>
<!-- #ifdef APP-PLUS || MP-->
<view class='keep' :style="{backgroundColor:bgColor}" @click='savePhoto(spreadList[swiperIndex].pic)'>保存海报</view>
<!-- #endif -->
<!-- #ifndef MP || APP-PLUS -->
<div class="preserve acea-row row-center-wrapper">
<div class="line"></div>
<div class="tip">长按保存图片</div>
<div class="line"></div>
</div>
<!-- #endif -->
</view>
<view class="canvas" v-if="canvasStatus">
<canvas style="width:750px;height:1190px;" canvas-id="canvasOne"></canvas>
<canvas canvas-id="qrcode" :style="{width: `${qrcodeSize}px`, height: `${qrcodeSize}px`}" />
</view>
</view>
</template>
<script>
// #ifdef H5 || APP-PLUS
import uQRCode from '@/js_sdk/Sansnn-uQRCode/uqrcode.js'
// #endif
// #ifdef APP-PLUS
import {
HTTP_H5_URL
} from '@/config/app.js';
// #endif
import {
getUserInfo,
spreadBanner
} from '@/api/user.js';
import {
toLogin
} from '@/libs/login.js';
import {
mapGetters
} from "vuex";
// #ifdef MP
import {
base64src
} from '@/utils/base64src.js'
import {
getQrcode
} from '@/api/api.js';
// #endif
import {setThemeColor} from '@/utils/setTheme.js'
import {
imageBase64
} from "@/api/public";
export default {
data() {
return {
imgUrls: [],
indicatorDots: false,
circular: false,
autoplay: false,
interval: 3000,
duration: 500,
swiperIndex: 0,
spreadList: [],
poster: '',
qrcodeSize: 1000,
PromotionCode: '',
base64List: [],
canvasStatus: true, //海报绘图标签
bgColor:'#e93323'
};
},
computed: mapGetters(['isLogin', 'uid', 'userInfo']),
watch: {
isLogin: {
handler: function(newV, oldV) {
if (newV) {
this.userSpreadBannerList();
}
},
deep: true
}
},
onLoad() {
this.bgColor = setThemeColor();
if (this.isLogin) {
this.userSpreadBannerList();
} else {
toLogin();
}
},
/**
* 用户点击右上角分享
*/
// #ifdef MP
onShareAppMessage: function() {
return {
title: this.userInfo.nickname + '-分销海报',
imageUrl: this.spreadList[0].pic,
path: '/pages/index/index?spread=' + this.uid,
};
},
// #endif
onReady() {},
methods: {
userSpreadBannerList: function() {
let that = this;
uni.showLoading({
title: '获取中',
mask: true,
})
spreadBanner({
page: 1,
limit: 5
}).then(res => {
uni.hideLoading();
that.$set(that, 'spreadList', res.data);
that.getImageBase64(res.data);
}).catch(err => {
uni.hideLoading();
});
},
getImageBase64: function(images) {
uni.showLoading({
title: '海报生成中',
mask: true
});
let that = this;
// #ifdef H5
let spreadList = []
// 生成一个Promise对象的数组
images.forEach(item => {
const oneApi = imageBase64({
url: item.pic
}).then(res => {
return res.data.code;
})
spreadList.push(oneApi)
})
Promise.all(spreadList).then(result => {
that.$set(that, 'base64List', result);
that.make();
that.setShareInfoStatus();
})
// #endif
// #ifdef MP || APP-PLUS
that.base64List = images.map(item => {
return item.pic
});
// #endif
// #ifdef MP
that.getQrcode();
// #endif
// #ifdef APP-PLUS
that.make();
// #endif
},
// 小程序二维码
getQrcode() {
let that = this;
let data = {
pid: that.uid,
path: 'pages/index/index'
}
let arrImagesUrl = "";
uni.downloadFile({
url: this.base64List[0],
success: (res) => {
arrImagesUrl = res.tempFilePath;
}
});
getQrcode(data).then(res => {
base64src(res.data.code,Date.now(), res => {
that.PromotionCode = res;
setTimeout(() => {
that.PosterCanvas(arrImagesUrl, that.PromotionCode, that.userInfo.nickname, 0);
}, 1000);
});
}).catch(err => {
uni.hideLoading();
that.$util.Tips({
title: err
});
that.$set(that, 'canvasStatus', false);
});
},
// 生成二维码;
make() {
let that = this;
let href = '';
// #ifdef H5
href = window.location.href.split('/pages')[0];
// #endif
// #ifdef APP-PLUS
href = HTTP_H5_URL;
let routes = getCurrentPages(); // 获取当前打开过的页面路由数组
let curRoute = routes[routes.length - 1].route //获取当前页面路由
let curParam = routes[routes.length - 1].options; //获取路由参数
// #endif
uQRCode.make({
canvasId: 'qrcode',
text: href + '/pages/index/index?spread=' + that.uid,
size: this.qrcodeSize,
margin: 10,
success: res => {
that.PromotionCode = res;
setTimeout(() => {
that.PosterCanvas(this.base64List[0], that.PromotionCode, that.userInfo.nickname, 0);
}, 1000);
},
complete: (res) => {},
fail: res => {
uni.hideLoading();
that.$util.Tips({
title: '海报二维码生成失败!'
});
}
})
},
PosterCanvas: function(arrImages, code, nickname, index) {
let context = uni.createCanvasContext('canvasOne')
context.clearRect(0, 0, 0, 0);
let that = this;
uni.getImageInfo({
src: arrImages,
success: function(res) {
context.drawImage(arrImages, 0, 0, 750, 1190);
context.save();
context.drawImage(code, 110, 925, 140, 140);
context.restore();
context.setFontSize(28);
context.fillText(nickname, 270, 980);
context.fillText('邀请您加入', 270, 1020);
setTimeout(() => {
context.draw(true, function() {
uni.canvasToTempFilePath({
destWidth: 750,
destHeight: 1190,
canvasId: 'canvasOne',
fileType: 'jpg',
success: function(res) {
// 在H5平台下tempFilePath 为 base64
uni.hideLoading();
that.spreadList[index].pic = res.tempFilePath;
that.$set(that, 'poster', res.tempFilePath);
that.$set(that, 'canvasStatus', false);
}
})
})
}, 100);
},
fail: function(err) {
uni.hideLoading();
that.$util.Tips({
title: '无法获取图片信息'
});
}
});
},
bindchange(e) {
let base64List = this.base64List;
let index = e.detail.current;
this.swiperIndex = index;
let arrImagesUrl = "";
uni.downloadFile({
url: base64List[index],
success: (res) => {
arrImagesUrl = res.tempFilePath;
setTimeout(() => {
this.$set(this, 'canvasStatus', true);
this.PosterCanvas(arrImagesUrl, this.PromotionCode, this.userInfo.nickname,index);
}, 300);
}
});
},
// 点击保存海报
savePhoto:function(url){
let that = this;
uni.saveImageToPhotosAlbum({
filePath: url,
success: function(res) {
that.$util.Tips({
title: '保存成功',
icon: 'success'
});
},
fail: function(res) {
that.$util.Tips({
title: '保存失败'
});
}
});
},
setShareInfoStatus: function() {
if (this.$wechat.isWeixin()) {
let configAppMessage = {
desc: '分销海报',
title: this.userInfo.nickname + '-分销海报',
link: '/pages/index/index?spread=' + this.uid,
imgUrl: this.spreadList[0].pic
};
this.$wechat.wechatEvevt(["updateAppMessageShareData", "updateTimelineShareData"],
configAppMessage)
}
}
}
}
</script>
<style lang="scss" scoped>
// page {
// background-color: #A2A2A2 !important;
// height: 100vh;
// overflow: auto;
// }
.page{
height: 100vh;
overflow: auto;
background-color: #A2A2A2 !important;
}
.canvas {
position: relative;
}
.distribution-posters {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.distribution-posters swiper {
width: 100%;
height: 1000rpx;
position: relative;
margin-top: 40rpx;
}
.distribution-posters .slide-image {
width: 100%;
height: 100%;
margin: 0 auto;
border-radius: 15rpx;
}
.distribution-posters .slide-image.active {
transform: none;
transition: all 0.2s ease-in 0s;
}
.distribution-posters .slide-image.quiet {
transform: scale(0.8333333);
transition: all 0.2s ease-in 0s;
}
.distribution-posters .keep {
font-size: 30rpx;
color: #fff;
width: 600rpx;
height: 80rpx;
border-radius: 50rpx;
text-align: center;
line-height: 80rpx;
margin: 38rpx auto;
}
.distribution-posters .preserve {
color: #fff;
text-align: center;
margin-top: 38rpx;
}
.distribution-posters .preserve .line {
width: 100rpx;
height: 1px;
background-color: #fff;
}
.distribution-posters .preserve .tip {
margin: 0 30rpx;
}
</style>

View File

@@ -0,0 +1,237 @@
<template>
<view :data-theme="theme">
<view class='commission-details'>
<view class='promoterHeader'>
<view class='headerCon acea-row row-between-wrapper'>
<view>
<view class='name'>{{name}}</view>
<view class='money' v-if="recordType == 4"><text class='num'>{{extractCount}}</text></view>
<view class='money' v-else><text class='num'>{{commissionCount}}</text></view>
</view>
<view class='iconfont icon-jinbi1'></view>
</view>
</view>
<view class='sign-record' v-if="recordType == 4">
<block v-for="(item,index) in recordList" :key="index" v-if="recordList.length>0">
<view class='list pad30'>
<view class='item'>
<view class='data'>{{item.date}}</view>
<view class='listn borRadius14'>
<block v-for="(child,indexn) in item.list" :key="indexn">
<view class='itemn acea-row row-between-wrapper'>
<view>
<view class='name line1'>{{child.status | statusFilter}}</view>
<view>{{child.createTime}}</view>
</view>
<view class='num font_color' v-if="child.status == -1">+{{child.extractPrice}}
</view>
<view class='num' v-else>-{{child.extractPrice}}</view>
<!-- <view>
<view class='name line1'>{{child.status === -1 ? '提现失败' : '提现成功'}}<span
v-show="child.status === -1"
style="font-size: 12px;color: red;">{{'('+child.failMsg+')'}}</span>
</view>
<view>{{child.createTime}}</view>
</view>
<view class='num font-color' v-if="child.status == -1">+{{child.extractPrice}}
</view>
<view class='num' v-else>-{{child.extractPrice}}</view> -->
</view>
</block>
</view>
</view>
</view>
</block>
<view v-if="recordList.length == 0">
<emptyPage title='暂无提现记录~'></emptyPage>
</view>
</view>
<view class='sign-record' v-else>
<block v-for="(item,index) in recordList" :key="index" v-if="recordList.length>0">
<view class='list pad30'>
<view class='item'>
<view class='data'>{{item.date}}</view>
<view class='listn borRadius14'>
<block v-for="(child,indexn) in item.list" :key="indexn">
<view class='itemn acea-row row-between-wrapper'>
<view>
<view class='name line1'>{{child.title}}</view>
<view>{{child.updateTime}}</view>
</view>
<view class='num font_color' v-if="child.type == 1">+{{child.price}}
</view>
<view class='num' v-else>-{{child.price}}</view>
</view>
</block>
</view>
</view>
</view>
</block>
<view v-if="recordList.length == 0">
<emptyPage title='暂无佣金记录~'></emptyPage>
</view>
</view>
</view>
</view>
</template>
<script>
import {
getCommissionInfo,
getRecordApi,
} from '@/api/user.js';
import {
toLogin
} from '@/libs/login.js';
import {
mapGetters
} from "vuex";
import emptyPage from '@/components/emptyPage.vue'
import {setThemeColor} from '@/utils/setTheme.js'
const app = getApp();
export default {
components: {
emptyPage
},
filters: {
statusFilter(status) {
const statusMap = {
'-1': '未通过',
'0': '审核中',
'1': '已提现'
}
return statusMap[status]
}
},
data() {
return {
name: '',
type: 0,
page: 1,
limit: 10,
recordList: [],
recordType: 0,
statuss: false,
extractCount: 0,
theme:app.globalData.theme,
commissionCount:0,
bgColor:'#e93323'
};
},
computed: mapGetters(['isLogin']),
onLoad(options) {
if (this.isLogin) {
this.type = options.type;
this.extractCount = options.extractCount;
this.commissionCount = options.commissionCount;
} else {
toLogin();
}
let that = this;
that.bgColor = setThemeColor();
uni.setNavigationBarColor({
frontColor: '#ffffff',
backgroundColor:that.bgColor,
});
},
onShow: function() {
let type = this.type;
if (type == 1) {
uni.setNavigationBarTitle({
title: "提现记录"
});
this.name = '提现总额';
this.recordType = 4;
this.getList();
} else if (type == 2) {
uni.setNavigationBarTitle({
title: "佣金记录"
});
this.name = '佣金明细';
this.recordType = 3;
this.getRecordList();
} else {
uni.showToast({
title: '参数错误',
icon: 'none',
duration: 1000,
mask: true,
success: function(res) {
setTimeout(function() {
// #ifndef H5
uni.navigateBack({
delta: 1,
});
// #endif
// #ifdef H5
history.back();
// #endif
}, 1200)
},
});
}
},
methods: {
getList: function() {
let that = this;
let recordList = that.recordList;
let recordListNew = [];
if (that.statuss == true) return;
getRecordApi({
page: that.page,
limit: that.limit
}).then(res => {
let len = res.data.list ? res.data.list.length : 0;
let recordListData = res.data.list || [];
recordListNew = recordList.concat(recordListData);
that.statuss = that.limit > len;
that.page = that.page + 1;
that.$set(that, 'recordList', recordListNew);
});
},
getRecordList: function() {
let that = this;
let page = that.page;
let limit = that.limit;
let statuss = that.statuss;
let recordType = that.recordType;
let recordList = that.recordList;
let recordListNew = [];
if (statuss == true) return;
getCommissionInfo({
page: page,
limit: limit
}).then(res => {
if (res.data.list) {
let len = res.data.list ? res.data.list.length : 0;
let recordListData = res.data.list || [];
recordListNew = recordList.concat(recordListData);
that.statuss = limit > len;
that.page = page + 1;
that.$set(that, 'recordList', recordListNew);
}
});
}
},
onReachBottom: function() {
this.getRecordList();
}
}
</script>
<style scoped lang="scss">
.commission-details .promoterHeader .headerCon .money {
font-size: 36rpx;
}
.promoterHeader{
@include main_bg_color(theme);
}
.commission-details .promoterHeader .headerCon .money .num {
font-family: 'Guildford Pro';
}
.font_color{
color: #E93323 !important;
}
</style>

View File

@@ -0,0 +1,232 @@
<template>
<view :data-theme="theme">
<view class='my-promotion'>
<view class="header">
<image class="head_img" :src="urlDomain+'crmebimage/maintain/2021/07/13/48e81e3e2e374d48820b7a9a56905365k2qa9yj8n5.png'"></image>
<navigator :url="'/pages/promoter/user_spread_money/index?type=1&extractCount='+spreadInfo.extractCount" hover-class="none" class='record'>提现记录<text class='iconfont icon-xiangyou'></text></navigator>
<view class="head_box">
<view class='name acea-row row-center-wrapper'>
<view>当前佣金</view>
</view>
<view class='num'>{{spreadInfo.commissionCount}}</view>
<view class='profit acea-row row-between-wrapper'>
<view class='item'>
<view>昨日收益</view>
<view class='money'>{{spreadInfo.lastDayCount ? Number(spreadInfo.lastDayCount).toFixed(2) : 0}}</view>
</view>
<view class='item'>
<view>累积已提</view>
<view class='money'>{{spreadInfo.extractCount ? Number(spreadInfo.extractCount).toFixed(2) : 0}}</view>
</view>
</view>
</view>
</view>
<!-- #ifdef APP-PLUS || H5 -->
<navigator url="/pages/users/user_cash/index" hover-class="none" class='bnt bg_color'>立即提现</navigator>
<!-- #endif -->
<!-- #ifdef MP -->
<view @click="openSubscribe('/pages/users/user_cash/index')" class='bnt bg_color'>立即提现</view>
<!-- #endif -->
<view class='list acea-row row-between-wrapper'>
<navigator url='/pages/promoter/user_spread_code/index' hover-class="none" class='item acea-row row-center-wrapper row-column'>
<text class='iconfont icon-erweima'></text>
<view>推广名片</view>
</navigator>
<navigator url='/pages/promoter/promoter-list/index' hover-class="none" class='item acea-row row-center-wrapper row-column'>
<text class='iconfont icon-tongji'></text>
<view>推广人统计</view>
</navigator>
<navigator :url="'/pages/promoter/user_spread_money/index?type=2&commissionCount='+spreadInfo.commissionCount" hover-class="none" class='item acea-row row-center-wrapper row-column'>
<text class='iconfont icon-qiandai'></text>
<view>佣金明细</view>
</navigator>
<navigator url='/pages/promoter/promoter-order/index' hover-class="none" class='item acea-row row-center-wrapper row-column'>
<text class='iconfont icon-dingdan'></text>
<view>推广人订单</view>
</navigator>
<navigator url='/pages/promoter/promoter_rank/index' hover-class="none" class='item acea-row row-center-wrapper row-column'>
<text class='iconfont icon-paihang1'></text>
<view>推广人排行</view>
</navigator>
<navigator url='/pages/promoter/commission_rank/index' hover-class="none" class='item acea-row row-center-wrapper row-column'>
<text class='iconfont icon-paihang'></text>
<view>佣金排行</view>
</navigator>
</view>
</view>
</view>
</template>
<script>
import { getSpreadInfo } from '@/api/user.js';
import { openExtrctSubscribe } from '@/utils/SubscribeMessage.js';
import {toLogin} from '@/libs/login.js';
import {mapGetters} from "vuex";
import {setThemeColor} from '@/utils/setTheme.js'
const app = getApp();
export default {
data() {
return {
urlDomain: this.$Cache.get("imgHost"),
spreadInfo: {},
theme:app.globalData.theme,
bgColor:'#e93323'
};
},
computed: mapGetters(['isLogin']),
watch: {
isLogin: {
handler: function(newV, oldV) {
if (newV) {
this.getSpreadInfo();
}
},
deep: true
}
},
onShow() {
let that = this;
that.bgColor = setThemeColor();
uni.setNavigationBarColor({
frontColor: '#ffffff',
backgroundColor:that.bgColor,
});
if (this.isLogin) {
this.getSpreadInfo();
} else {
toLogin();
}
},
methods: {
openSubscribe: function(page) {
uni.navigateTo({
url: page,
});
},
/**
* 获取个人用户信息
*/
getSpreadInfo: function() {
let that = this;
getSpreadInfo().then(res => {
that.$set(that,'spreadInfo',res.data);
});
}
}
}
</script>
<style scoped lang="scss">
.my-promotion .header {
width: 100%;
height: 375rpx;
position: relative;
}
.head_img{
width: 100%;
height: 375rpx;
position: absolute;
top: 0;
z-index: 2;
}
.head_box{
width: 100%;
height: 375rpx;
position: absolute;
top: 0;
z-index: 0;
@include main_bg_color(theme);
}
.my-promotion .header .name {
font-size: 30rpx;
color: #fff;
padding-top: 57rpx;
position: relative;
}
.my-promotion .header .record {
font-size: 26rpx;
color: rgba(255, 255, 255, 0.8);
position: absolute;
right: 20rpx;
top:60rpx;
z-index:10;
}
.my-promotion .header .record .iconfont {
font-size: 25rpx;
margin-left: 10rpx;
vertical-align: 2rpx;
}
.my-promotion .header .num {
text-align: center;
color: #fff;
margin-top: 28rpx;
font-size: 90rpx;
font-family: 'Guildford Pro';
}
.my-promotion .header .profit {
padding: 0 20rpx;
margin-top: 35rpx;
font-size: 24rpx;
color: rgba(255, 255, 255, 0.8);
}
.my-promotion .header .profit .item {
min-width: 200rpx;
text-align: center;
}
.my-promotion .header .profit .item .money {
font-size: 34rpx;
color: #fff;
margin-top: 5rpx;
}
.my-promotion .bnt {
font-size: 28rpx;
color: #fff;
width: 278rpx;
height: 108rpx;
box-sizing: border-box;
border: 20rpx solid #f5f5f5;
border-radius: 50rpx;
text-align: center;
line-height: 68rpx;
margin: -52rpx auto 0 auto;
box-sizing: border-box;
position: absolute;
left: 0;
right: 0;
z-index: 3;
}
.bg_color{
@include main_bg_color(theme);
}
.my-promotion .list {
padding: 0 30rpx 50rpx 30rpx;
margin-top: 60rpx;
}
.my-promotion .list .item {
width: 335rpx;
height: 240rpx;
border-radius: 14rpx;
background-color: #fff;
margin-top: 20rpx;
font-size: 30rpx;
color: #666;
}
.my-promotion .list .item .iconfont {
font-size: 70rpx;
// background: linear-gradient(to right, #fc4d3d 0%, #e93323 100%);
@include main_bg_color(theme);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
margin-bottom: 20rpx;
}
</style>