feat(fsgx): HJF queue merge, brokerage timing, cycle commission, points release
- Add HJF jobs, services, DAOs, models, admin/API controllers, release command - Respect brokerage_timing (on_pay vs confirm); dispatch HjfOrderPayJob for queue goods - Queue-only cycle commission and position index fix in StoreOrderCreateServices - UserBill income types: frozen_points_brokerage, frozen_points_release - Timer: fsgx_release_frozen_points -> PointsReleaseServices - Agent tasks: no_assess filtering for direct/umbrella counts - Migrations: queue_pool, points_release_log, fsgx_v1 checklist updates - Admin/uniapp: crontab preset, membership level, user list, finance routes, docs Made-with: Cursor
This commit is contained in:
@@ -3,7 +3,7 @@ NODE_ENV=production
|
||||
VUE_APP_ENV='production'
|
||||
|
||||
# 页面 title
|
||||
VUE_APP_TITLE=CRMEB
|
||||
VUE_APP_TITLE=fsgx-shop
|
||||
# socket 系统连接地址 (ws)或(wss)://www.crmeb.com(换成你的域名)/ws 非独立部署默认为空
|
||||
VUE_APP_WS_ADMIN_URL='ws://fsgx.uj345.com/ws'
|
||||
# 接口请求地址 (http)或 (https)://www.crmeb.com(换成你的域名)/adminapi 非独立部署默认为空
|
||||
|
||||
@@ -679,6 +679,17 @@ export function timerTask() {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 手动立即触发定时任务
|
||||
* @param {number} id
|
||||
* @returns
|
||||
*/
|
||||
export function runTimerNow(id) {
|
||||
return request({
|
||||
url: `system/timer/run_now/${id}`,
|
||||
});
|
||||
}
|
||||
|
||||
export function systemFormData(id, params) {
|
||||
return request({
|
||||
url: `/system/form/data/${id}`,
|
||||
|
||||
@@ -63,6 +63,11 @@
|
||||
baseURL: Setting.apiBaseURL.replace(/adminapi/, '')
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
if (Array.isArray(this.valueModel) || (typeof this.valueModel === 'object' && this.valueModel !== null)) {
|
||||
this.valueModel = JSON.stringify(this.valueModel);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleCopySuccess(){
|
||||
this.$Message.success('复制成功');
|
||||
|
||||
@@ -27,7 +27,10 @@
|
||||
name: "radioBuild",
|
||||
mixins:[build, components],
|
||||
mounted() {
|
||||
this.valueModel = parseFloat(this.valueModel);
|
||||
const parsed = parseFloat(this.valueModel);
|
||||
if (!isNaN(parsed) && String(parsed) === String(this.valueModel)) {
|
||||
this.valueModel = parsed;
|
||||
}
|
||||
},
|
||||
components: {
|
||||
useComponent:() => import('./useComponent'),
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
</Form>
|
||||
<Drawer v-model="guideShow" :title="`${title}引导`" width="800">
|
||||
<component :is="types"></component>
|
||||
<component :is="types" v-if="guideComponents.includes(types)"></component>
|
||||
</Drawer>
|
||||
</div>
|
||||
</template>
|
||||
@@ -91,8 +91,13 @@
|
||||
loading: false,
|
||||
errorsValidate: [],
|
||||
title: this.$parent.title,
|
||||
types: this.$parent.typeMole || this.$parent.type,
|
||||
types: (() => {
|
||||
const t = this.$parent.typeMole || this.$parent.type;
|
||||
const allowed = ['app', 'routine', 'wechat', 'work'];
|
||||
return allowed.includes(t) ? t : (this.$parent.type || '');
|
||||
})(),
|
||||
guideShow: false,
|
||||
guideComponents: ['app', 'routine', 'wechat', 'work'],
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
|
||||
@@ -6,18 +6,6 @@ const chalk = require("chalk");
|
||||
console.log(
|
||||
chalk
|
||||
.hex("#DEADED")
|
||||
.underline("😄 Hello ~ 欢迎使用CRMEB Pro版,我们将竭诚为您服务!")
|
||||
);
|
||||
console.log(
|
||||
chalk.yellow("[提示] 点击这里可以我们的更多产品~ ") +
|
||||
chalk.blue.underline("https://www.crmeb.com")
|
||||
);
|
||||
console.log(
|
||||
chalk.yellow("[提示] 点击这里可以查看开发文档喔~ ") +
|
||||
chalk.blue.underline("https://doc.crmeb.com")
|
||||
);
|
||||
console.log(
|
||||
chalk.yellow("[提示] 点击这里可以进入我们的论坛社区~ ") +
|
||||
chalk.blue.underline("https://www.crmeb.com/ask")
|
||||
.underline("😄 Hello ~ 欢迎使用Pro版,我们将竭诚为您服务!")
|
||||
);
|
||||
console.log(chalk.blue("info - [你知道吗?] 按 Ctrl+C 可以停止服务呢~"));
|
||||
|
||||
@@ -101,6 +101,7 @@ export const defaultObj = {
|
||||
is_sub: 0, //返佣方式
|
||||
is_vip: 0,
|
||||
level_type: 1, //默认比例 自定义
|
||||
is_queue_goods: 0, //是否报单商品:1=是,0=否
|
||||
};
|
||||
|
||||
export const GoodsTableHead = [
|
||||
|
||||
@@ -239,6 +239,16 @@ export default {
|
||||
minWidth: 130,
|
||||
title: "二级返佣比例(上浮后)(%)",
|
||||
},
|
||||
{
|
||||
key: "direct_reward_points",
|
||||
minWidth: 120,
|
||||
title: "直推奖励积分",
|
||||
},
|
||||
{
|
||||
key: "umbrella_reward_points",
|
||||
minWidth: 120,
|
||||
title: "伞下奖励积分",
|
||||
},
|
||||
{
|
||||
slot: "status",
|
||||
minWidth: 80,
|
||||
|
||||
@@ -29,6 +29,10 @@
|
||||
>编辑</router-link
|
||||
>
|
||||
<Divider type="vertical" />
|
||||
<a @click="handleRunNow(row)" :class="{ 'running': row._running }">
|
||||
{{ row._running ? '执行中…' : '手动触发' }}
|
||||
</a>
|
||||
<Divider type="vertical" />
|
||||
<a @click="handleDelete(row, '删除定时任务', index)">删除</a>
|
||||
</template>
|
||||
</Table>
|
||||
@@ -46,7 +50,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { timerIndex, showTimer } from '@/api/system';
|
||||
import { timerIndex, showTimer, runTimerNow } from '@/api/system';
|
||||
|
||||
export default {
|
||||
name: 'system_crontab',
|
||||
@@ -79,7 +83,7 @@ export default {
|
||||
slot: 'action',
|
||||
align: 'center',
|
||||
fixed: 'right',
|
||||
minWidth: 100,
|
||||
minWidth: 200,
|
||||
},
|
||||
],
|
||||
tableData: [],
|
||||
@@ -133,6 +137,21 @@ export default {
|
||||
this.$Message.error(res.msg);
|
||||
});
|
||||
},
|
||||
// 手动立即触发
|
||||
handleRunNow(row) {
|
||||
this.$set(row, '_running', true);
|
||||
runTimerNow(row.id)
|
||||
.then((res) => {
|
||||
this.$Message.success(res.msg || '触发成功,任务已执行');
|
||||
this.getList();
|
||||
})
|
||||
.catch((res) => {
|
||||
this.$Message.error(res.msg || '触发失败');
|
||||
})
|
||||
.finally(() => {
|
||||
this.$set(row, '_running', false);
|
||||
});
|
||||
},
|
||||
// 是否开启
|
||||
handleChange({ id, is_open }) {
|
||||
showTimer(id, is_open)
|
||||
@@ -152,4 +171,9 @@ export default {
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped></style>
|
||||
<style lang="stylus" scoped>
|
||||
.running
|
||||
color #999
|
||||
cursor not-allowed
|
||||
pointer-events none
|
||||
</style>
|
||||
@@ -54,15 +54,15 @@
|
||||
<div class="iconfont iconxiayi"></div>
|
||||
</div>
|
||||
</FormItem>
|
||||
<FormItem label="会员等级:" label-for="hjf_member_level">
|
||||
<FormItem label="分销等级:" label-for="hjf_member_level">
|
||||
<Select
|
||||
v-model="userFrom.hjf_member_level"
|
||||
placeholder="请选择"
|
||||
placeholder="请选择分销等级"
|
||||
element-id="hjf_member_level"
|
||||
clearable
|
||||
class="input-add"
|
||||
>
|
||||
<Option :value="0">普通会员</Option>
|
||||
<Option :value="0">普通(无分销等级)</Option>
|
||||
<Option :value="1">创客</Option>
|
||||
<Option :value="2">云店</Option>
|
||||
<Option :value="3">服务商</Option>
|
||||
@@ -420,14 +420,15 @@
|
||||
</div>
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column field="member_level" title="HJF等级" min-width="110">
|
||||
<vxe-column field="member_level_name" title="分销等级" min-width="160">
|
||||
<template v-slot="{ row }">
|
||||
<HjfMemberBadge
|
||||
v-if="row.member_level != null"
|
||||
v-if="row.member_level != null && row.member_level > 0"
|
||||
:level="Number(row.member_level)"
|
||||
:level-name="row.member_level_name || ''"
|
||||
size="small"
|
||||
/>
|
||||
<span v-else class="level-none">普通会员</span>
|
||||
<span v-else class="level-none">普通(无等级)</span>
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column field="direct_count" title="直推人数" min-width="90">
|
||||
@@ -452,22 +453,22 @@
|
||||
title="推荐人"
|
||||
min-width="100"
|
||||
></vxe-column>
|
||||
<vxe-column field="available_points" title="可用积分" min-width="100">
|
||||
<template v-slot="{ row }">
|
||||
<span>{{ row.available_points != null ? row.available_points : 0 }}</span>
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column field="frozen_points" title="待释放(冻结)积分" min-width="150">
|
||||
<template v-slot="{ row }">
|
||||
<span>{{ row.frozen_points != null ? row.frozen_points : 0 }}</span>
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column field="now_money" title="余额" min-width="100"></vxe-column>
|
||||
<vxe-column field="frozen_points" title="待释放积分" min-width="100">
|
||||
<template v-slot="{ row }">
|
||||
<span>{{ row.frozen_points != null ? row.frozen_points : '-' }}</span>
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column field="available_points" title="已释放积分" min-width="100">
|
||||
<template v-slot="{ row }">
|
||||
<span>{{ row.available_points != null ? row.available_points : '-' }}</span>
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column
|
||||
field="action"
|
||||
title="操作"
|
||||
align="center"
|
||||
width="240"
|
||||
width="180"
|
||||
fixed="right"
|
||||
>
|
||||
<template v-slot="{ row }">
|
||||
@@ -475,8 +476,6 @@
|
||||
<Divider type="vertical" />
|
||||
<a @click="changeMenu(row, '10')">编辑</a>
|
||||
<Divider type="vertical" />
|
||||
<a @click="openLevelModal(row)">调整等级</a>
|
||||
<Divider type="vertical" />
|
||||
<a @click="handleNoAssess(row)" :style="row.no_assess ? 'color:#19be6b' : 'color:#ed4014'">
|
||||
{{ row.no_assess ? '取消不考核' : '不考核' }}
|
||||
</a>
|
||||
@@ -791,7 +790,7 @@ import {
|
||||
exportUserData,
|
||||
} from "@/api/user";
|
||||
import { agentSpreadApi } from "@/api/agent";
|
||||
import { memberList, memberSetLevel, memberSetNoAssess } from "@/api/hjfMember";
|
||||
import { memberList, memberSetLevel, memberSetNoAssess } from "@/api/hjfMember"; // memberSetNoAssess 仍保留供 handleNoAssess 使用
|
||||
import editFrom from "../../../components/from/from";
|
||||
import sendFrom from "@/components/sendCoupons/index";
|
||||
import userDetails from "./handle/userDetails";
|
||||
@@ -881,7 +880,7 @@ export default {
|
||||
group_id: "",
|
||||
field_key: "",
|
||||
is_channel: "",
|
||||
/** 会员等级筛选(HJF 扩展字段):0 普通 1 创客 2 云店 3 服务商 4 分公司,空串表示全部 */
|
||||
/** 分销等级筛选(对应 eb_agent_level.grade / eb_user.agent_level),空串表示全部 */
|
||||
hjf_member_level: "",
|
||||
},
|
||||
field_key: "",
|
||||
@@ -1455,12 +1454,13 @@ export default {
|
||||
this.userFrom.group_id === "all" ? "" : this.userFrom.group_id;
|
||||
userList(this.userFrom)
|
||||
.then(async (res) => {
|
||||
let data = res.data;
|
||||
data.list.forEach((item) => {
|
||||
const data = res.data || {};
|
||||
const rows = Array.isArray(data.list) ? data.list : [];
|
||||
rows.forEach((item) => {
|
||||
item.checkBox = false;
|
||||
});
|
||||
this.userLists = data.list;
|
||||
this.total = data.count;
|
||||
this.userLists = rows;
|
||||
this.total = data.count != null ? data.count : 0;
|
||||
this.loading = false;
|
||||
this.$nextTick(function () {
|
||||
if (this.isAll == 1) {
|
||||
|
||||
@@ -59,5 +59,17 @@ export default {
|
||||
},
|
||||
component: () => import('@/pages/finance/commission/index')
|
||||
},
|
||||
{
|
||||
path: 'extract_setting',
|
||||
name: `${pre}extractSetting`,
|
||||
meta: {
|
||||
auth: ['finance-extract-setting'],
|
||||
title: '提现设置'
|
||||
},
|
||||
component: () => import('@/components/fromSubmit/commonForm.vue'),
|
||||
props: {
|
||||
typeMole: 'advance'
|
||||
}
|
||||
},
|
||||
]
|
||||
};
|
||||
|
||||
@@ -20,8 +20,9 @@
|
||||
<button form-type="submit" class="confirmBnt bg-color">确认修改</button>
|
||||
</form>
|
||||
</view>
|
||||
<Verify @success="success" :captchaType="captchaType" :imgSize="{ width: '330px', height: '155px' }"
|
||||
ref="verify"></Verify>
|
||||
<!-- 安全验证组件保留备用,当前修改密码流程已跳过 -->
|
||||
<!-- <Verify @success="success" :captchaType="captchaType" :imgSize="{ width: '330px', height: '155px' }"
|
||||
ref="verify"></Verify> -->
|
||||
<!-- #ifdef MP -->
|
||||
<authorize v-if="isShowAuth" @authColse="authColse" @onLoadFun="onLoadFun"></authorize>
|
||||
<!-- #endif -->
|
||||
@@ -120,53 +121,66 @@
|
||||
that.phone = phone;
|
||||
});
|
||||
},
|
||||
success(data) {
|
||||
console.log(data,'data');
|
||||
this.$refs.verify.hide()
|
||||
getCodeApi()
|
||||
.then(res => {
|
||||
this.keyCode = res.data.key;
|
||||
this.getCode(data);
|
||||
})
|
||||
.catch(res => {
|
||||
this.$util.Tips({
|
||||
title: res
|
||||
});
|
||||
success(data) {
|
||||
this.$refs.verify.hide()
|
||||
getCodeApi()
|
||||
.then(res => {
|
||||
this.keyCode = res.data.key;
|
||||
this.getCode(data);
|
||||
})
|
||||
.catch(res => {
|
||||
this.$util.Tips({
|
||||
title: res
|
||||
});
|
||||
},
|
||||
/**
|
||||
* 发送验证码
|
||||
*
|
||||
*/
|
||||
code(data) {
|
||||
let that = this;
|
||||
if (!that.userInfo.phone) return that.$util.Tips({
|
||||
title: '手机号码不存在,无法发送验证码!'
|
||||
});
|
||||
this.$refs.verify.show()
|
||||
},
|
||||
async getCode(data){
|
||||
let that = this;
|
||||
await registerVerify({
|
||||
phone: that.userInfo.phone,
|
||||
type: 'reset',
|
||||
key: that.key,
|
||||
captchaType: CAPTCHA_TYPE,
|
||||
captchaVerification: data.captchaVerification,
|
||||
})
|
||||
.then(res => {
|
||||
that.$util.Tips({
|
||||
title: res.msg
|
||||
});
|
||||
that.sendCode();
|
||||
})
|
||||
.catch(res => {
|
||||
this.$refs.verify.refresh()
|
||||
that.$util.Tips({
|
||||
title: res
|
||||
});
|
||||
},
|
||||
/**
|
||||
* 发送验证码(直接发送,不弹安全验证)
|
||||
*/
|
||||
code() {
|
||||
let that = this;
|
||||
if (!that.userInfo.phone) return that.$util.Tips({
|
||||
title: '手机号码不存在,无法发送验证码!'
|
||||
});
|
||||
registerVerify({
|
||||
phone: that.userInfo.phone,
|
||||
type: 'reset',
|
||||
key: that.key,
|
||||
})
|
||||
.then(res => {
|
||||
that.$util.Tips({
|
||||
title: res.msg
|
||||
});
|
||||
},
|
||||
that.sendCode();
|
||||
})
|
||||
.catch(res => {
|
||||
that.$util.Tips({
|
||||
title: res
|
||||
});
|
||||
});
|
||||
},
|
||||
async getCode(data){
|
||||
let that = this;
|
||||
await registerVerify({
|
||||
phone: that.userInfo.phone,
|
||||
type: 'reset',
|
||||
key: that.key,
|
||||
captchaType: CAPTCHA_TYPE,
|
||||
captchaVerification: data.captchaVerification,
|
||||
})
|
||||
.then(res => {
|
||||
that.$util.Tips({
|
||||
title: res.msg
|
||||
});
|
||||
that.sendCode();
|
||||
})
|
||||
.catch(res => {
|
||||
this.$refs.verify.refresh()
|
||||
that.$util.Tips({
|
||||
title: res
|
||||
});
|
||||
});
|
||||
},
|
||||
/**
|
||||
* H5登录 修改密码
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user