Phase1 后端核心:
- 新增 fsgx_v1.sql 迁移脚本(is_queue_goods/frozen_points/available_points/no_assess)
- SystemConfigServices 返佣设置扩展(周期人数/分档比例/范围/时机)
- StoreOrderCreateServices 周期循环佣金计算
- StoreOrderTakeServices 佣金发放后同步冻结积分
- StoreProductServices/StoreProduct 保存 is_queue_goods
Phase2 后端接口:
- GET /api/hjf/brokerage/progress 佣金周期进度
- GET /api/hjf/assets/overview 资产总览
- HjfPointsServices 每日 frozen_points 0.4‰ 释放定时任务
- PUT /adminapi/hjf/member/{uid}/no_assess 不考核接口
- GET /adminapi/hjf/points/release_log 积分日志接口
Phase3 前端清理:
- hjfCustom.js 路由精简(仅保留 points/log)
- hjfQueue.js/hjfMember.js API 清理/重定向至 CRMEB 原生接口
- pages.json 公排→推荐佣金/佣金记录/佣金规则
Phase4-5 前端改造:
- queue/status.vue 推荐佣金进度页整体重写
- 商品详情/订单确认/支付结果页文案与逻辑改造
- 个人中心/资产页/引导页/规则页文案改造
- HjfQueueProgress/HjfRefundNotice/HjfAssetCard 组件改造
- 推广中心嵌入佣金进度摘要
- hjfMockData.js 全量更新(公排字段→佣金字段)
Phase6 Admin 增强:
- 用户列表新增 frozen_points/available_points 列及不考核操作按钮
- hjfPoints.js USE_MOCK=false 对接真实积分日志接口
Phase7 配置文档:
- docs/fsgx-phase7-config-checklist.md 后台配置与全链路验收清单
Made-with: Cursor
511 lines
11 KiB
Vue
511 lines
11 KiB
Vue
<template>
|
|
<!-- 悬浮导航按钮 -->
|
|
<view :style="colorStyle" v-if="is_show && showMenu">
|
|
<view style="touch-action: none">
|
|
<view class="home" style="position: fixed" :style="{ top: top + 'px' }" id="right-nav"
|
|
@touchmove.stop.prevent="setTouchMove">
|
|
<view class="menus" :class="isOpen ? `menus_isOpen-${menus.length}` : ''" v-if="index == 3 ">
|
|
<view id="fab3"></view>
|
|
<!-- style="height: 298rpx;" -->
|
|
<view class="menu menu_main" @click="changeStatus">
|
|
<image :src="isOpen ? main_after_image : main_ago_image" class="image"
|
|
style="border-radius: 50%;" />
|
|
</view>
|
|
|
|
<view v-for="(menu, index) in menus" :key="index"
|
|
:class="['menu menu_a', `menu_a--${index + 1}`]" @click.stop="jump(menu.url)">
|
|
<image :src="menu.img" class="icon-img"></image>
|
|
</view>
|
|
</view>
|
|
<view id="fab1" class="fab1 p-10" v-if="index == 1" @touchmove.stop.prevent="setTouchMove">
|
|
<view v-show="isOpen" class="img-list">
|
|
<view v-for="(item, index) in menus" :key="index" class="img-box" @click="jump(item.url)">
|
|
<image class="img" :src="item.img" mode=""></image>
|
|
</view>
|
|
</view>
|
|
<view class="btn-box" @click="changeStatus">
|
|
<view class="img-box">
|
|
<image class="img" :src="main_ago_image" mode=""></image>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
<view id="fab2" class="fab2 flex-y-center relative p-10" :class="{ hide: isHide }" v-if="index == 2">
|
|
<view v-if="isOpen" class="img-list">
|
|
<view v-for="(item, index) in menus" :key="index" class="img-box" @click="jump(item.url)">
|
|
<image class="img" :src="item.img" mode=""></image>
|
|
</view>
|
|
</view>
|
|
<view v-if="!isOpen" class="btn-box" @click="changeStatus">
|
|
<view class="img-box">
|
|
<image class="img" :src="main_ago_image" mode=""></image>
|
|
</view>
|
|
</view>
|
|
<view v-if="isOpen" class="icon" @click="changeStatus">
|
|
<text class="iconfont icon-ic_rightarrow"></text>
|
|
</view>
|
|
</view>
|
|
<view class="menus fab4" :class="isOpen ? `menus_isOpen menus_isOpen-${menus.length}` : ''"
|
|
v-if="index == 4">
|
|
<view id="fab4" style="height: 350rpx;width: 1px;"></view>
|
|
<view class="menu_bag" :class="isOpen ? ' has-bag' : opend ? 'close-bag' : ''"></view>
|
|
<view class="menu menu_main menu_main4" @click="changeStatus">
|
|
<text v-if="isOpen" class="iconfont icon-ic_close"></text>
|
|
</view>
|
|
|
|
<view v-for="(menu, index) in menus" :key="index"
|
|
:style="{ zIndex: isOpen ? 9999 : '-1' }"
|
|
:class="['menu menu_a', `menu_a--${index + 1}`]" @click.stop="jump(menu.url)">
|
|
<image :src="menu.img" class="icon-img"></image>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</template>
|
|
<script>
|
|
import {mapGetters} from 'vuex';
|
|
import colors from '@/mixins/color.js';
|
|
import {HTTP_REQUEST_URL} from '@/config/app';
|
|
import Vue from 'vue';
|
|
import {getSuspended} from '@/api/api.js';
|
|
export default {
|
|
name: 'fabHome',
|
|
props: {
|
|
// 风格样式
|
|
styleType: {
|
|
type: Number,
|
|
default: 2
|
|
},
|
|
},
|
|
mixins: [colors],
|
|
data: function() {
|
|
return {
|
|
top: '545',
|
|
imgHost: HTTP_REQUEST_URL,
|
|
isOpen: false, //菜单是否展开
|
|
opend: false, //是否打开过
|
|
menus: [],
|
|
main_image: '',
|
|
shifting: 10,
|
|
index: 4,
|
|
is_show: 1,
|
|
main_after_image: '',
|
|
main_ago_image: '',
|
|
windowHeight: 0,
|
|
fabHeight: 0,
|
|
isHide: false,
|
|
menu:{
|
|
color:'rgba(0, 0, 0, 0.10)'
|
|
},
|
|
showMenu: true
|
|
};
|
|
},
|
|
computed:{
|
|
...mapGetters(['isLogin'])
|
|
},
|
|
mounted() {
|
|
uni.$on('scroll', this.scrollFn);
|
|
if(this.isLogin && [1,2].includes(this.$store.state.app.identity)){
|
|
this.showMenu = false;
|
|
}else{
|
|
setTimeout(()=>{
|
|
this.getSuspended();
|
|
},500)
|
|
}
|
|
uni.getSystemInfo({
|
|
success: (res) => {
|
|
this.windowHeight = res.windowHeight;
|
|
}
|
|
});
|
|
},
|
|
beforeDestroy() {
|
|
this.$Cache.set('homeTop',this.top);
|
|
},
|
|
methods: {
|
|
changeStatus() {
|
|
this.isOpen = !this.isOpen;
|
|
this.opend = true;
|
|
this.isHide = false;
|
|
},
|
|
setTouchMove(e) {
|
|
let that = this;
|
|
let clientY = e.touches[0].clientY;
|
|
if (clientY > (that.fabHeight / 2) && clientY < (that.windowHeight - that.fabHeight / 2)) {
|
|
that.top = clientY;
|
|
}
|
|
},
|
|
scrollFn() {
|
|
this.isOpen = false;
|
|
this.isHide = true;
|
|
},
|
|
jump(url) {
|
|
this.$util.JumpPath(url);
|
|
},
|
|
// 获取配置信息
|
|
getSuspended() {
|
|
getSuspended().then(res => {
|
|
const data = res.data;
|
|
this.menus = data.button;
|
|
this.index = data.index;
|
|
this.is_show = data.is_show;
|
|
this.main_image = data.main_image;
|
|
this.main_after_image = data.main_after_image || '';
|
|
this.main_ago_image = data.main_ago_image || '';
|
|
this.shifting = data.shifting;
|
|
if(data.is_show){
|
|
this.$nextTick(() => {
|
|
const query = uni.createSelectorQuery().in(this);
|
|
query
|
|
.select(`#fab${this.index}`)
|
|
.boundingClientRect((data) => {
|
|
this.fabHeight = data.height;
|
|
if(this.$Cache.get('homeTop')){
|
|
this.top = this.$Cache.get('homeTop')
|
|
}else{
|
|
if (this.shifting == 100) {
|
|
this.top = this.windowHeight - data.height / 2
|
|
} else if (this.shifting == 0) {
|
|
this.top = data.height / 2
|
|
} else {
|
|
this.top = this.shifting / 100 * this.windowHeight
|
|
}
|
|
}
|
|
})
|
|
.exec();
|
|
});
|
|
}
|
|
|
|
});
|
|
},
|
|
}
|
|
};
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.home {
|
|
position: fixed;
|
|
color: white;
|
|
text-align: center;
|
|
z-index: 9999;
|
|
display: flex;
|
|
right: 0;
|
|
transform: translateY(-50%);
|
|
}
|
|
|
|
.menus {
|
|
width: 70rpx;
|
|
height: 70rpx;
|
|
line-height: 70rpx;
|
|
position: relative;
|
|
right: 20rpx;
|
|
font-size: 24rpx;
|
|
color: #ffffff;
|
|
margin: auto;
|
|
}
|
|
|
|
.menus .menu {
|
|
display: block;
|
|
text-align: center;
|
|
border-radius: 50%;
|
|
position: absolute;
|
|
top: 50%;
|
|
left: 50%;
|
|
transform: translate(-50%, -50%);
|
|
transition-timing-function: cubic-bezier(0.25, 1.2, 0.45, 1);
|
|
transition-duration: 1s;
|
|
}
|
|
|
|
.menu_bag {
|
|
width: 70rpx;
|
|
height: 70rpx;
|
|
// border-radius: 90rpx 0 0 90rpx;
|
|
border-radius: 70rpx;
|
|
background: rgba(51, 51, 51, 0.1);
|
|
box-shadow: 0rpx 0rpx 17rpx 0rpx rgba(0, 0, 0, 0.1);
|
|
position: absolute;
|
|
}
|
|
|
|
.close-bag {
|
|
animation: close 0.5s ease forwards;
|
|
}
|
|
|
|
.has-bag {
|
|
border-radius: 50%;
|
|
animation: show 0.4s ease forwards;
|
|
}
|
|
|
|
@keyframes show {
|
|
0% {
|
|
width: 70rpx;
|
|
height: 70rpx;
|
|
}
|
|
|
|
100% {
|
|
width: 350rpx;
|
|
height: 350rpx;
|
|
}
|
|
}
|
|
|
|
@keyframes close {
|
|
0% {
|
|
width: 350rpx;
|
|
height: 350rpx;
|
|
}
|
|
|
|
100% {
|
|
width: 70rpx;
|
|
height: 70rpx;
|
|
}
|
|
}
|
|
|
|
.menus .menu_main {
|
|
z-index: 2;
|
|
width: 70rpx;
|
|
height: 70rpx;
|
|
line-height: 70rpx;
|
|
font-size: 36px;
|
|
box-shadow: 0px 0px 8px 0px rgba(0, 0, 0, 0.1);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
|
|
image {
|
|
width: 100%;
|
|
height: 100%;
|
|
}
|
|
}
|
|
|
|
.menus .menu_a {
|
|
width: 70rpx;
|
|
height: 70rpx;
|
|
line-height: 70rpx;
|
|
color: inherit;
|
|
// box-shadow: 0 0 6rpx var(--color);
|
|
// transform: translate(-50%, -50%) rotate(180deg);
|
|
transform: translate(-50%, -50%);
|
|
}
|
|
|
|
/* 点击悬浮球主菜单按钮后样式 */
|
|
.menus_isOpen .menu_main {
|
|
width: 70rpx;
|
|
height: 70rpx;
|
|
line-height: 70rpx;
|
|
font-size: 28rpx;
|
|
color: #607d88;
|
|
background-color: #ffffff;
|
|
}
|
|
|
|
.menus_isOpen-3 {
|
|
.menu_a--1 {
|
|
transform: translate(-100%, calc(-50% - 93rpx));
|
|
}
|
|
|
|
.menu_a--2 {
|
|
transform: translate(calc(-50% - 100rpx), calc(-50%));
|
|
}
|
|
|
|
.menu_a--3 {
|
|
transform: translate(-100%, calc(-50% + 93rpx));
|
|
}
|
|
}
|
|
|
|
.menus_isOpen-4 {
|
|
.menu_a--1 {
|
|
transform: translate(-50%, calc(-50% - 100rpx));
|
|
box-shadow: 0px 0px 8px 0px rgba(0, 0, 0, 0.1);
|
|
}
|
|
|
|
.menu_a--2 {
|
|
transform: translate(calc(-50% - 90rpx), calc(-50% - 50rpx));
|
|
box-shadow: 0px 0px 8px 0px rgba(0, 0, 0, 0.1);
|
|
}
|
|
|
|
.menu_a--3 {
|
|
transform: translate(calc(-50% - 90rpx), calc(-50% + 50rpx));
|
|
box-shadow: 0px 0px 8px 0px rgba(0, 0, 0, 0.1);
|
|
}
|
|
|
|
.menu_a--4 {
|
|
transform: translate(-50%, calc(-50% + 100rpx));
|
|
box-shadow: 0px 0px 8px 0px rgba(0, 0, 0, 0.1);
|
|
}
|
|
|
|
.menu_a--5 {
|
|
transform: translate(calc(-50% + 100rpx), calc(-50% + 50rpx));
|
|
box-shadow: 0px 0px 8px 0px rgba(0, 0, 0, 0.1);
|
|
}
|
|
|
|
.menu_a--6 {
|
|
transform: translate(calc(-50% + 100rpx), calc(-50% - 50rpx));
|
|
box-shadow: 0px 0px 8px 0px rgba(0, 0, 0, 0.1);
|
|
}
|
|
}
|
|
|
|
.menus_isOpen-5 {
|
|
.menu_a--1 {
|
|
transform: translate(-50%, calc(-50% - 110rpx));
|
|
}
|
|
|
|
.menu_a--2 {
|
|
transform: translate(calc(-50% - 76rpx), calc(-50% - 76rpx));
|
|
}
|
|
|
|
.menu_a--3 {
|
|
transform: translate(calc(-50% - 106rpx), -50%);
|
|
}
|
|
|
|
.menu_a--4 {
|
|
transform: translate(calc(-50% - 76rpx), calc(-50% + 76rpx));
|
|
}
|
|
|
|
.menu_a--5 {
|
|
transform: translate(-50%, calc(-50% + 110rpx));
|
|
}
|
|
}
|
|
|
|
.icon-img {
|
|
width: 70rpx;
|
|
height: 70rpx;
|
|
border-radius: 50%;
|
|
}
|
|
|
|
.fab1 {
|
|
position: absolute;
|
|
right: 20rpx;
|
|
bottom: 0;
|
|
border-radius: 45rpx;
|
|
background: rgba(255, 255, 255, 0.5);
|
|
backdrop-filter: blur(10rpx);
|
|
|
|
|
|
.img-box {
|
|
width: 70rpx;
|
|
height: 70rpx;
|
|
border-radius: 50%;
|
|
background: #FFFFFF;
|
|
|
|
.img {
|
|
width: 100%;
|
|
height: 100%;
|
|
border-radius: 50%;
|
|
}
|
|
}
|
|
|
|
.img-list {
|
|
position: relative;
|
|
z-index: 2;
|
|
|
|
.img-box {
|
|
margin: 0 0 16rpx 0;
|
|
box-shadow: 0rpx 0rpx 16rpx 0rpx rgba(0, 0, 0, 0.1);
|
|
}
|
|
}
|
|
|
|
.btn-box {
|
|
position: relative;
|
|
z-index: 2;
|
|
|
|
.img-box {}
|
|
}
|
|
}
|
|
|
|
.fab2 {
|
|
border-radius: 45rpx 0 0 45rpx;
|
|
background: rgba(255, 255, 255, 0.5);
|
|
backdrop-filter: blur(10rpx);
|
|
|
|
&.hide {
|
|
transform: translateX(42rpx);
|
|
opacity: 0.5;
|
|
}
|
|
|
|
.img-box {
|
|
position: relative;
|
|
z-index: 2;
|
|
width: 70rpx;
|
|
height: 70rpx;
|
|
border-radius: 50%;
|
|
background: #FFFFFF;
|
|
|
|
.img {
|
|
width: 100%;
|
|
height: 100%;
|
|
border-radius: 50%;
|
|
}
|
|
}
|
|
|
|
.img-list {
|
|
position: relative;
|
|
z-index: 2;
|
|
display: flex;
|
|
|
|
.img-box {
|
|
box-shadow: 0rpx 0rpx 16rpx 0rpx rgba(0, 0, 0, 0.1);
|
|
|
|
+.img-box {
|
|
margin: 0 0 0 16rpx;
|
|
}
|
|
}
|
|
}
|
|
|
|
.icon {
|
|
position: relative;
|
|
z-index: 2;
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
width: 28rpx;
|
|
height: 28rpx;
|
|
margin-left: 16rpx;
|
|
}
|
|
|
|
.iconfont {
|
|
font-size: 28rpx;
|
|
color: #333333;
|
|
}
|
|
}
|
|
|
|
.menu_main4 {
|
|
background: #FFFFFF;
|
|
|
|
.icon-ic_close {
|
|
font-size: 34rpx;
|
|
color: #333333;
|
|
}
|
|
}
|
|
|
|
.fab4 {
|
|
.menu_bag {
|
|
top: 50%;
|
|
left: 50%;
|
|
transform: translate(-50%, -50%);
|
|
width: 70rpx;
|
|
height: 70rpx;
|
|
border-radius: 50%;
|
|
background: rgba(51, 51, 51, 0.1);
|
|
backdrop-filter: blur(7.78rpx);
|
|
box-shadow: 0rpx 0rpx 13rpx 0rpx rgba(0, 0, 0, 0.1);
|
|
}
|
|
|
|
.close-bag {
|
|
background: rgba(51, 51, 51, 0.1);
|
|
backdrop-filter: blur(7.78rpx);
|
|
}
|
|
|
|
.has-bag {
|
|
background: rgba(255, 255, 255, 0.5);
|
|
}
|
|
|
|
.menu_main {
|
|
width: 48rpx;
|
|
height: 48rpx;
|
|
}
|
|
|
|
&.menus_isOpen {
|
|
.menu_main {
|
|
width: 70rpx;
|
|
height: 70rpx;
|
|
}
|
|
}
|
|
}
|
|
</style> |