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
316 lines
7.4 KiB
Vue
316 lines
7.4 KiB
Vue
<template>
|
||
<view class="tui-swipeout-wrap" :style="{ backgroundColor: backgroundColor }">
|
||
<view class="tui-swipeout-item" :class="[isShowBtn ? 'swipe-action-show' : '']"
|
||
:style="{ transform: 'translate(' + position.pageX + 'px,0)' }">
|
||
<view class="tui-swipeout-content" @touchstart="handlerTouchstart"
|
||
@touchmove="handlerTouchmove" @touchend="handlerTouchend" @mousedown="handlerTouchstart"
|
||
@mousemove="handlerTouchmove" @mouseup="handlerTouchend">
|
||
<slot name="content"></slot>
|
||
</view>
|
||
<view class="tui-swipeout-button-right-group" v-if="actions.length > 0" @touchend.stop="loop">
|
||
<view class="tui-swipeout-button-right-item" v-for="(item, index) in actions" :key="index"
|
||
:style="{ backgroundColor: item.background || '#f7f7f7', color: item.color, width: item.width + 'px' }"
|
||
:data-index="index" @tap="handlerButton">
|
||
<image :src="item.icon" v-if="item.icon"
|
||
:style="{ width: px(item.imgWidth), height: px(item.imgHeight) }"></image>
|
||
<text :style="{ fontSize: px(item.fontsize) }">{{ item.name }}</text>
|
||
</view>
|
||
</view>
|
||
<!--actions长度设置为0,可直接传按钮进来-->
|
||
<view class="tui-swipeout-button-right-group" @touchend.stop="loop" @tap="handlerParentButton"
|
||
v-if="actions.length === 0" :style="{ width: operateWidth + 'px', right: '-' + operateWidth + 'px' }">
|
||
<slot name="button"></slot>
|
||
</view>
|
||
</view>
|
||
<view v-if="isShowBtn && showMask" class="swipe-action_mask" @tap.stop="closeButtonGroup"
|
||
@touchstart.stop.prevent="closeButtonGroup"></view>
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
export default {
|
||
name: 'tuiSwipeAction',
|
||
emits: ['click'],
|
||
props: {
|
||
// name: '删除',
|
||
// color: '#fff',
|
||
// fontsize: 32,//单位rpx
|
||
// width: 80, //单位px
|
||
// icon: 'like.png',//此处为图片地址
|
||
// background: '#ed3f14'
|
||
actions: {
|
||
type: Array,
|
||
default () {
|
||
return [];
|
||
}
|
||
},
|
||
//点击按钮时是否自动关闭
|
||
closable: {
|
||
type: Boolean,
|
||
default: true
|
||
},
|
||
//设为false,可以滑动多行不关闭菜单
|
||
showMask: {
|
||
type: Boolean,
|
||
default: true
|
||
},
|
||
operateWidth: {
|
||
type: Number,
|
||
default: 80
|
||
},
|
||
params: {
|
||
type: Object,
|
||
default () {
|
||
return {};
|
||
}
|
||
},
|
||
//禁止滑动
|
||
forbid: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
//手动开关
|
||
open: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
//背景色
|
||
backgroundColor: {
|
||
type: String,
|
||
default: '#fff'
|
||
}
|
||
},
|
||
watch: {
|
||
actions(newValue, oldValue) {
|
||
this.updateButtonSize();
|
||
},
|
||
open(newValue) {
|
||
this.manualSwitch(newValue);
|
||
}
|
||
},
|
||
data() {
|
||
return {
|
||
//start position
|
||
tStart: {
|
||
pageX: 0,
|
||
pageY: 0
|
||
},
|
||
//限制滑动距离
|
||
limitMove: 0,
|
||
//move position
|
||
position: {
|
||
pageX: 0,
|
||
pageY: 0
|
||
},
|
||
isShowBtn: false,
|
||
move: false
|
||
};
|
||
},
|
||
mounted() {
|
||
this.updateButtonSize();
|
||
},
|
||
methods: {
|
||
swipeDirection(x1, x2, y1, y2) {
|
||
return Math.abs(x1 - x2) >= Math.abs(y1 - y2) ? (x1 - x2 > 0 ? 'Left' : 'Right') : y1 - y2 > 0 ? 'Up' :
|
||
'Down';
|
||
},
|
||
//阻止事件冒泡
|
||
loop() {
|
||
},
|
||
updateButtonSize() {
|
||
const actions = this.actions;
|
||
if (actions.length > 0) {
|
||
const query = uni.createSelectorQuery().in(this);
|
||
let limitMovePosition = 0;
|
||
actions.forEach(item => {
|
||
limitMovePosition += item.width || 0;
|
||
});
|
||
this.limitMove = limitMovePosition;
|
||
} else {
|
||
this.limitMove = this.operateWidth;
|
||
}
|
||
},
|
||
handlerTouchstart(event) {
|
||
if (this.forbid) return;
|
||
let touches = event.touches
|
||
if (touches && touches.length > 1) return;
|
||
this.move = true;
|
||
touches = touches ? event.touches[0] : {};
|
||
if (!touches || (touches.pageX === undefined && touches.pageY === undefined)) {
|
||
touches = {
|
||
pageX: event.pageX,
|
||
pageY: event.pageY
|
||
};
|
||
}
|
||
const tStart = this.tStart;
|
||
if (touches) {
|
||
for (let i in tStart) {
|
||
if (touches[i]) {
|
||
tStart[i] = touches[i];
|
||
}
|
||
}
|
||
}
|
||
},
|
||
swipper(touches) {
|
||
const start = this.tStart;
|
||
const spacing = {
|
||
pageX: touches.pageX - start.pageX,
|
||
pageY: touches.pageY - start.pageY
|
||
};
|
||
if (this.limitMove < Math.abs(spacing.pageX)) {
|
||
spacing.pageX = -this.limitMove;
|
||
}
|
||
this.position = spacing;
|
||
},
|
||
handlerTouchmove(event) {
|
||
if (this.forbid || !this.move) return;
|
||
const start = this.tStart;
|
||
let touches = event.touches ? event.touches[0] : {};
|
||
if (!touches || (touches.pageX === undefined && touches.pageY === undefined)) {
|
||
touches = {
|
||
pageX: event.pageX,
|
||
pageY: event.pageY
|
||
};
|
||
}
|
||
if (touches) {
|
||
const direction = this.swipeDirection(start.pageX, touches.pageX, start.pageY, touches.pageY);
|
||
if (direction === 'Left' && Math.abs(this.position.pageX) !== this.limitMove) {
|
||
this.swipper(touches);
|
||
}
|
||
}
|
||
},
|
||
handlerTouchend(event) {
|
||
if (this.forbid || !this.move) return;
|
||
this.move = false;
|
||
const start = this.tStart;
|
||
let touches = event.changedTouches ? event.changedTouches[0] : {};
|
||
if (!touches || (touches.pageX === undefined && touches.pageY === undefined)) {
|
||
touches = {
|
||
pageX: event.pageX,
|
||
pageY: event.pageY
|
||
};
|
||
}
|
||
if (touches) {
|
||
const direction = this.swipeDirection(start.pageX, touches.pageX, start.pageY, touches.pageY);
|
||
const spacing = {
|
||
pageX: touches.pageX - start.pageX,
|
||
pageY: touches.pageY - start.pageY
|
||
};
|
||
if (Math.abs(spacing.pageX) >= 40 && direction === 'Left') {
|
||
spacing.pageX = spacing.pageX < 0 ? -this.limitMove : this.limitMove;
|
||
this.isShowBtn = true;
|
||
} else {
|
||
spacing.pageX = 0;
|
||
}
|
||
if (spacing.pageX== 0) {
|
||
this.isShowBtn = false;
|
||
}
|
||
this.position = spacing;
|
||
|
||
}
|
||
},
|
||
handlerButton(event) {
|
||
if (this.closable) {
|
||
this.closeButtonGroup();
|
||
}
|
||
const dataset = event.currentTarget.dataset;
|
||
this.$emit('click', {
|
||
index: Number(dataset.index),
|
||
item: this.params
|
||
});
|
||
},
|
||
closeButtonGroup() {
|
||
this.position = {
|
||
pageX: 0,
|
||
pageY: 0
|
||
};
|
||
this.isShowBtn = false;
|
||
},
|
||
//控制自定义按钮菜单
|
||
handlerParentButton(event) {
|
||
if (this.closable) {
|
||
this.closeButtonGroup();
|
||
}
|
||
},
|
||
manualSwitch(isOpen) {
|
||
let x = 0;
|
||
if (isOpen) {
|
||
if (this.actions.length === 0) {
|
||
x = this.operateWidth;
|
||
} else {
|
||
let width = 0;
|
||
this.actions.forEach(item => {
|
||
width += item.width;
|
||
});
|
||
x = width;
|
||
}
|
||
}
|
||
this.position = {
|
||
pageX: -x,
|
||
pageY: 0
|
||
};
|
||
},
|
||
px(num) {
|
||
return uni.upx2px(num) + 'px';
|
||
}
|
||
}
|
||
};
|
||
</script>
|
||
|
||
<style scoped>
|
||
.tui-swipeout-wrap {
|
||
position: relative;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.swipe-action-show {
|
||
position: relative;
|
||
z-index: 998;
|
||
}
|
||
|
||
.tui-swipeout-item {
|
||
width: 100%;
|
||
/* padding: 15px 20px; */
|
||
box-sizing: border-box;
|
||
transition: transform 0.2s ease;
|
||
font-size: 14px;
|
||
cursor: pointer;
|
||
}
|
||
|
||
.tui-swipeout-content {
|
||
white-space: nowrap;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.tui-swipeout-button-right-group {
|
||
position: absolute;
|
||
right: -100%;
|
||
top: 0;
|
||
height: 100%;
|
||
z-index: 1;
|
||
width: 100%;
|
||
}
|
||
|
||
.tui-swipeout-button-right-item {
|
||
height: 100%;
|
||
float: left;
|
||
white-space: nowrap;
|
||
box-sizing: border-box;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
text-align: center;
|
||
}
|
||
|
||
.swipe-action_mask {
|
||
display: block;
|
||
opacity: 0;
|
||
position: fixed;
|
||
z-index: 997;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
}
|
||
</style>
|