新增营销系统、分销系统、会员功能、门店、提现功能

This commit is contained in:
Shaw
2024-02-08 21:01:37 +08:00
parent 68b3f2dcc3
commit 17c043348a
1398 changed files with 81279 additions and 56269 deletions

View File

@ -0,0 +1,171 @@
<!--
@name: GridCard
@author: kahu4
@date: 2023-11-09 15:09
@descriptionGridCard
@update: 2023-11-09 15:09
-->
<script setup>
import { toRefs } from "vue";
import { useRouter } from "@/hooks/useRouter";
import { useInterface } from "@/hooks/useInterface";
import { storeToRefs } from "pinia";
import { useMainStore } from "@/store/store";
import { useService } from "@/hooks/useService";
const props = defineProps({
list: {
type: Array,
default: () => ([])
},
dotInfo: {
type: Object
},
title: {
type: String,
default: () => ''
},
buttonText: {
type: String,
default: ''
}
})
const {list, title, buttonText, dotInfo} = toRefs(props)
const emits = defineEmits(['buttonClick'])
const {push} = useRouter();
const {toast} = useInterface();
const mainStore = useMainStore();
const {user} = storeToRefs(mainStore);
async function toLink(listItem) {
if (!user.value) return toast({title: '请先登录'})
if (!listItem.path) return toast({title: ' 暂未开放 '})
if (listItem.path === 'kf') {
const {getServiceData,openService} = useService();
await getServiceData()
await openService()
return
}
push({url: listItem.path}, listItem?.params ?? {})
}
</script>
<template>
<view class="grid-container">
<view
class="title-row"
v-if="title || buttonText"
>
<view>
{{ title }}
</view>
<view
class="right"
@click="emits('buttonClick')"
>
{{ buttonText }}
<uv-icon
v-if="buttonText"
name="arrow-right"
color="#ccc"
size="12"
/>
</view>
</view>
<view class="icon-box">
<view
class="icon-item"
v-for="item in list"
:key="item"
@click="toLink(item)"
>
<template v-if="item&&item.rightTopDot">
<view
class="dot"
v-if="dotInfo && dotInfo[item.dotField] && dotInfo[item.dotField]>0"
>
{{ dotInfo[item.dotField] < 100 ? dotInfo[item.dotField] : `${ dotInfo[item.dotField] }+` }}
</view>
</template>
<image
class="icon"
:src="item.icon"
/>
<view class="text">
{{ item.label }}
</view>
</view>
</view>
</view>
</template>
<style
scoped
lang="scss"
>
.grid-container {
width: 100%;
background: $white-color;
border-radius: 15rpx;
margin: 20rpx 0;
.title-row {
@include useFlex(space-between, center);
@include usePadding(30, 30);
font-size: 32rpx;
color: #333;
font-weight: 500;
border-bottom: 1rpx solid #f5f5f5;
.right {
@include useFlex(space-between, center);
font-size: 24rpx;
color: $tips-color;
font-weight: normal;
}
}
.icon-box {
@include usePadding(30, 20);
display: grid;
grid-template-columns: repeat(4, 1fr);
.icon-item {
@include useFlex(space-between, center, column);
width: 100%;
font-size: 24rpx;
color: #333333;
position: relative;
.dot {
z-index: 99;
position: absolute;
background: #EE6D46;
color: #fff;
right: 20%;
top: 0;
transform: translateY(-20%);
font-size: 18rpx;
width: 38rpx;
height: 38rpx;
text-align: center;
line-height: 38rpx;
box-sizing: border-box;
border-radius: 50%;
border: 2rpx solid #ffffff;
}
.icon {
width: 60rpx;
height: 60rpx;
}
.text {
margin: 14rpx 0;
}
}
}
}
</style>

172
root/user/index.data.js Normal file
View File

@ -0,0 +1,172 @@
/**
* @name: index.data
* @author: kahu4
* @date: 2023-11-09 15:21
* @descriptionindex.data
* @update: 2023-11-09 15:21
* */
import {
myScan,
toDZIcon,
toFHIcon,
toKFIcon,
toKJIcon,
toPayIcon,
toPJIcon,
toSCIcon,
toSHIcon,
toSHOIcon,
toTGIcon,
toYHQIcon,
toZBIcon,
toZHIcon,
toZJIcon
} from "@/utils/images";
import { useJump } from "@/hooks/useJump";
const {goIntegral, goBalance, goCoupon} = useJump()
export const orderIconList = [
{
id: 1,
label: '待付款',
icon: toPayIcon,
rightTopDot: true,
dotField: 'unpaidCount',
path: '/pages/orderList/orderList',
params: {data: {type: 0}}
},
{
id: 2,
label: '待发货',
icon: toFHIcon,
rightTopDot: true,
dotField: 'unshippedCount',
path: '/pages/orderList/orderList',
params: {data: {type: 1}}
},
{
id: 3,
label: '待收货',
icon: toSHIcon,
rightTopDot: true,
dotField: 'receivedCount',
path: '/pages/orderList/orderList',
params: {data: {type: 2}}
},
{
id: 4,
label: '待评价',
icon: toPJIcon,
rightTopDot: true,
dotField: 'evaluatedCount',
path: '/pages/orderList/orderList',
params: {data: {type: 3}}
},
]
export const cardOneList = [
{
id: 1,
label: '我的足迹',
icon: toZJIcon,
path: '/pages/footprint/footprint',
},
{
id: 2,
label: '优惠券',
icon: toYHQIcon,
path: '/pages/discountCoupon/index',
},
{
id: 3,
label: '我的收藏',
icon: toSCIcon,
rightTopDot: false,
dotField: 'receivedCount',
path: '/pages/collect/collect',
},
{
id: 4,
label: '开启直播',
icon: toZBIcon,
rightTopDot: false,
dotField: 'evaluatedCount',
path: '',
params: {data: {type: 3}}
},
{
id: 5,
label: '我的推广',
icon: toTGIcon,
path: '/views/distribution/center/index',
params: {}
},
{
id: 6,
label: '砍价记录',
icon: toKJIcon,
path: '',
params: {data: {type: 1}}
},
{
id: 7,
label: '售后记录',
icon: toSHOIcon,
rightTopDot: false,
dotField: 'receivedCount',
path: '/pages/refundList/refundList',
params: {data: {type: -1}}
},
{
id: 8,
label: '联系客服',
icon: toKFIcon,
rightTopDot: false,
dotField: 'evaluatedCount',
path: 'kf',
params: {data: {type: 3}}
},
]
export const cardTwoList = [
{
id: 1,
label: '地址管理',
icon: toDZIcon,
path: '/pages/address/address',
},
{
id: 2,
label: '账号管理',
icon: toZHIcon,
path: '/pages/userInfo/index',
},
{
id: 3,
label: '订单核销',
icon: myScan,
path: '/views/activity/afterVerification/index',
},
]
export const accountList = [
{
id: 1,
label: '我的余额',
field: 'nowMoney',
path: goBalance
},
{
id: 2,
label: '我的积分',
field: 'integral',
path: goIntegral
},
{
id: 3,
label: '优惠券',
field: 'couponNumber',
path: goCoupon
}
]

388
root/user/user.vue Normal file
View File

@ -0,0 +1,388 @@
<template>
<view class="mine-container">
<Header :show-return="false">我的</Header>
<!-- user info -->
<view class="userinfo-box">
<!-- 未登录 -->
<view
class="userinfo-box__inner no-login"
v-if="!(user && user.id)"
@click="toLogin"
>
<view class="flex flex-ai__center">
<image
class="head"
:src="defaultAvatarIcon"
/>
<view class="user-info">
点击登录
</view>
</view>
</view>
<!-- 已登录 -->
<view
class="userinfo-box__inner"
@click="toUserCenter"
v-else
>
<view class="flex flex-ai__center">
<image
class="head"
:src="user.avatar"
/>
<view class="user-info">
{{ user.nickname }}
</view>
</view>
<view
class="sign-box"
@click.stop="goSignIn">
<image :src="mySignIn" />
签到
</view>
</view>
</view>
<!-- 账户信息 -->
<view class="account-box">
<template
v-for="item in accountList"
:key="item.id">
<view
v-if="user"
class="account-item"
@click.stop="handleJump(item)">
<view class="count">
{{ user[item.field] || 0 }}
</view>
<view class="title">
{{ item.label }}
</view>
</view>
</template>
</view>
<!-- VIP 信息 未激活 -->
<view
class="vip-box vip-none"
@click="goMemberCenter"
v-if="!memberLeverInfo.currentLevel">
<view
class="vip-box__inner flex flex-ai__center flex-jc__sb"
:style="{backgroundImage:`url(${myVip1})`}">
<image
class="icon"
:src="noneVip" />
<view class="vip-text">
开通享更多特权省钱又省心
</view>
<view class="vip-button">
立即激活
</view>
</view>
</view>
<!-- VIP 信息 激活 -->
<view
class="vip-box"
@click="goMemberCenter"
v-else>
<view
class="vip-box__inner "
:style="{backgroundImage:`url(${myVip1})`}">
<view class="flex flex-ai__center flex-jc__sb">
<image
class="icon"
:src="memberLeverInfo.currentLevel.iconUrl" />
<view class="vip-text flex flex-ai__center">
{{ memberLeverInfo.currentLevel.levelName }}
<view class="process">
<view
class="schedule"
:style="{width: `${memberLeverInfo.needGrowthValue/memberLeverInfo.nextLevel.growthValue}%`}"></view>
</view>
</view>
<view class="vip-button">
查看权益 >
</view>
</view>
<view class="tips">
再获取{{ memberLeverInfo.needGrowthValue }}经验可升级为{{ memberLeverInfo.nextLevel.levelName }}会员
</view>
</view>
</view>
<!-- 大卡片 -->
<view class="big-card">
<!-- order card -->
<GridCard
:list="orderIconList"
:dot-info="orderUserCountData"
title="我的订单"
button-text="查看所有订单"
@button-click="toAllOrder"
/>
<!-- footprint card -->
<GridCard
:list="cardOneList"
:dot-info="orderUserCountData"
/>
<GridCard
:list="filterCardTwo"
:dot-info="orderUserCountData"
/>
</view>
</view>
</template>
<script setup>
import { computed, ref } from 'vue'
import Header from '@/components/Header/index.vue'
import { orderUserCount } from '@/api/order'
import { onShow } from '@dcloudio/uni-app'
import { useMainStore } from '@/store/store'
import { useRouter } from "@/hooks/useRouter";
import { storeToRefs } from "pinia";
import GridCard from "@/root/user/components/GridCard.vue";
import { accountList, cardOneList, cardTwoList, orderIconList } from "@/root/user/index.data";
import { defaultAvatarIcon, mySignIn, myVip1, myVipNone, noneVip } from "@/utils/images";
import { useInterface } from "@/hooks/useInterface";
import { useJump } from "@/hooks/useJump";
import { getUserMemberLevel } from "@/api/member";
const mainStore = useMainStore()
const {user} = storeToRefs(mainStore);
const {push} = useRouter()
const {toast} = useInterface();
const {goSignIn, goMemberCenter} = useJump()
const orderUserCountData = ref(null)
const filterCardTwo = computed(() => {
if (!user.value) return []
// 判断是否又核销权限
if (user.value.writeOffAuthority) {
return cardTwoList
} else {
return cardTwoList.filter(item => item.label !== '订单核销')
}
})
function toAllOrder() {
if (!user.value) return toast({title: '请先登录'})
push({url: '/pages/orderList/orderList'}, {data: {type: -1}})
}
function toUserCenter() {
if (!user.value) return toast({title: '请先登录'})
push({url: '/pages/userInfo/index'})
}
function toLogin() {
push({url: '/pages/login/guid'})
}
const handleOrderUserCount = async () => {
orderUserCountData.value = await orderUserCount()
}
// =============================== 会员信息 ====================================
const memberLeverInfo = ref({
currentGrowthValue: 0,
currentLevel: null,
needGrowthValue: 0,
nextLevel: null
})
async function doGetUserMemberLevel() {
memberLeverInfo.value = await getUserMemberLevel()
}
function handleJump(item) {
if (!item.path) return toast({title: '暂未开放~'})
if (typeof item.path === 'function') {
item.path()
}
}
onShow(() => {
mainStore.getUserInfo()
handleOrderUserCount()
doGetUserMemberLevel()
})
</script>
<style
lang="scss"
scoped
>
.mine-container {
background: linear-gradient(to bottom, #fff 0%, #fff 28%, #b0b0b0 40%, #fff 41%);
.userinfo-box {
@include usePadding(32, 0);
width: 100%;
margin: 50rpx 0 0 0;
&__inner {
@include useFlex(space-between, center);
.head {
width: 120rpx;
height: 120rpx;
border-radius: 50%;
border: 5rpx solid #fff;
}
.user-info {
@include useFlex(space-between, center);
padding-left: 30rpx;
font-size: 34rpx;
}
.sign-box {
@include useFlex(center, center);
@include usePadding(16, 12);
border: 1rpx solid #E5E5E5;
font-weight: bold;
font-size: 24rpx;
border-radius: 50rpx;
image {
width: 40rpx;
height: 40rpx;
}
}
&.no-login {
}
}
}
.account-box {
width: 100%;
display: grid;
grid-template-columns: repeat(3, 1fr);
margin: 32rpx 0;
.account-item {
@include useFlex(center, center, column);
font-weight: bold;
font-size: 32rpx;
color: #333;
position: relative;
&:after {
content: '';
position: absolute;
right: 0;
top: 50%;
transform: translateY(-50%);
width: 1rpx;
height: 50%;
background: $tips-color;
}
&:last-child {
&:after {
width: 0;
}
}
.title {
color: $tips-color;
font-size: 24rpx;
font-weight: normal;
}
}
}
.vip-box {
width: 100%;
height: 132rpx;
@include usePadding(32, 0);
&__inner {
@include usePadding(32, 25);
width: 100%;
height: 100%;
background-repeat: no-repeat;
background-size: 100% 100%;
.icon {
width: 56rpx;
height: 56rpx;
}
.vip-text {
width: 60%;
color: #FFF8E8;
font-weight: bold;
font-size: 28rpx;
.process {
flex-grow: 1;
height: 10rpx;
margin-left: 12rpx;
border-radius: 10rpx;
overflow: hidden;
background: rgba(255, 255, 255, 0.1);
position: relative;
.schedule {
height: 100%;
background: #FFF8E8;
border-radius: 10rpx;
left: 0;
top: 0;
}
}
}
.vip-button {
@include usePadding(0, 10);
color: #FFF8E8;
font-size: 24rpx;
border-radius: 8rpx;
}
.tips {
margin-top: 10rpx;
color: #FFF8E8;
font-size: 20rpx
}
}
}
.vip-none {
.vip-button {
@include usePadding(24, 10);
background: linear-gradient(45deg, #FAEECB, #F2D7A9);
color: #272A3F;
font-size: 24rpx;
border-radius: 8rpx;
}
}
.big-card {
width: 100%;
background: #f6f6f6;
border-radius: 30rpx;
@include usePadding(32, 32);
}
}
</style>