diff --git a/src/api/member/brokerage/record/index.ts b/src/api/member/brokerage/record/index.ts new file mode 100644 index 00000000..33b6ab18 --- /dev/null +++ b/src/api/member/brokerage/record/index.ts @@ -0,0 +1,11 @@ +import request from '@/config/axios' + +// 查询佣金记录列表 +export const getMemberBrokerageRecordPage = async (params: any) => { + return await request.get({ url: `/member/member-brokerage-record/page`, params }) +} + +// 查询佣金记录详情 +export const getMemberBrokerageRecord = async (id: number) => { + return await request.get({ url: `/member/member-brokerage-record/get?id=` + id }) +} diff --git a/src/api/member/point/config/index.ts b/src/api/member/point/config/index.ts index 9a3c4c48..9fbd0447 100644 --- a/src/api/member/point/config/index.ts +++ b/src/api/member/point/config/index.ts @@ -6,6 +6,16 @@ export interface ConfigVO { tradeDeductUnitPrice: number tradeDeductMaxPrice: number tradeGivePoint: number + brokerageEnabled: boolean + brokerageEnabledCondition: number + brokerageBindMode: number + brokeragePostUrls: string[] + brokerageFirstPercent: number + brokerageSecondPercent: number + brokerageWithdrawMinPrice: number + brokerageBankNames: number[] + brokerageFrozenDays: number + brokerageWithdrawType: number[] } // 查询积分设置详情 diff --git a/src/utils/constants.ts b/src/utils/constants.ts index 8d783635..022d308c 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -272,3 +272,30 @@ export const PromotionDiscountTypeEnum = { name: '折扣' } } + +/** + * 分销关系绑定模式枚举 + */ +export const BrokerageBindModeEnum = { + ANYTIME: { + mode: 0, + name: '没有推广人' + }, + REGISTER: { + mode: 1, + name: '新用户' + } +} +/** + * 分佣模式枚举 + */ +export const BrokerageEnabledConditionEnum = { + ALL: { + condition: 0, + name: '人人分销' + }, + ADMIN: { + condition: 1, + name: '指定分销' + } +} diff --git a/src/utils/dict.ts b/src/utils/dict.ts index 6b163628..2a7bfd12 100644 --- a/src/utils/dict.ts +++ b/src/utils/dict.ts @@ -143,6 +143,12 @@ export enum DICT_TYPE { // ========== MALL - 会员模块 ========== MEMBER_POINT_BIZ_TYPE = 'member_point_biz_type', // 积分的业务类型 + BROKERAGE_ENABLED_CONDITION = 'brokerage_enabled_condition', // 分佣模式 + BROKERAGE_BIND_MODE = 'brokerage_bind_mode', // 分销关系绑定模式 + BROKERAGE_BANK_NAME = 'brokerage_bank_name', // 佣金提现银行 + BROKERAGE_WITHDRAW_TYPE = 'brokerage_withdraw_type', // 佣金冻结时间 + BROKERAGE_RECORD_BIZ_TYPE = 'brokerage_record_biz_type', // 佣金业务类型 + BROKERAGE_RECORD_STATUS = 'brokerage_record_status', // 佣金状态 // ========== MALL - 商品模块 ========== PRODUCT_UNIT = 'product_unit', // 商品单位 diff --git a/src/views/member/brokerage/record/index.vue b/src/views/member/brokerage/record/index.vue new file mode 100644 index 00000000..f9c8400a --- /dev/null +++ b/src/views/member/brokerage/record/index.vue @@ -0,0 +1,156 @@ +<template> + <ContentWrap> + <!-- 搜索工作栏 --> + <el-form + class="-mb-15px" + :model="queryParams" + ref="queryFormRef" + :inline="true" + label-width="68px" + > + <el-form-item label="用户编号" prop="userId"> + <el-input + v-model="queryParams.userId" + placeholder="请输入用户编号" + clearable + @keyup.enter="handleQuery" + class="!w-240px" + /> + </el-form-item> + <el-form-item label="业务类型" prop="bizType"> + <el-select + v-model="queryParams.bizType" + placeholder="请选择业务类型" + clearable + class="!w-240px" + > + <el-option + v-for="dict in getIntDictOptions(DICT_TYPE.BROKERAGE_RECORD_BIZ_TYPE)" + :key="dict.value" + :label="dict.label" + :value="dict.value" + /> + </el-select> + </el-form-item> + <el-form-item label="状态" prop="status"> + <el-select v-model="queryParams.status" placeholder="请选择状态" clearable class="!w-240px"> + <el-option + v-for="dict in getIntDictOptions(DICT_TYPE.BROKERAGE_RECORD_STATUS)" + :key="dict.value" + :label="dict.label" + :value="dict.value" + /> + </el-select> + </el-form-item> + <el-form-item label="创建时间" prop="createTime"> + <el-date-picker + v-model="queryParams.createTime" + value-format="YYYY-MM-DD HH:mm:ss" + type="daterange" + start-placeholder="开始日期" + end-placeholder="结束日期" + :default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]" + class="!w-240px" + /> + </el-form-item> + <el-form-item> + <el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button> + <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button> + </el-form-item> + </el-form> + </ContentWrap> + + <!-- 列表 --> + <ContentWrap> + <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true"> + <el-table-column label="编号" align="center" prop="id" min-width="60" /> + <el-table-column label="用户编号" align="center" prop="userId" min-width="80" /> + <el-table-column label="业务类型" align="center" prop="bizType" min-width="85"> + <template #default="scope"> + <dict-tag :type="DICT_TYPE.BROKERAGE_RECORD_BIZ_TYPE" :value="scope.row.bizType" /> + </template> + </el-table-column> + <el-table-column label="业务编号" align="center" prop="bizId" min-width="80" /> + <el-table-column label="标题" align="center" prop="title" min-width="110" /> + <el-table-column label="金额" align="center" prop="price" min-width="60" /> + <el-table-column label="说明" align="center" prop="description" min-width="120" /> + <el-table-column label="状态" align="center" prop="status" min-width="85"> + <template #default="scope"> + <dict-tag :type="DICT_TYPE.BROKERAGE_RECORD_STATUS" :value="scope.row.status" /> + </template> + </el-table-column> + <el-table-column + label="解冻时间" + align="center" + prop="unfreezeTime" + :formatter="dateFormatter" + width="170px" + /> + <el-table-column + label="创建时间" + align="center" + prop="createTime" + :formatter="dateFormatter" + width="170px" + /> + </el-table> + <!-- 分页 --> + <Pagination + :total="total" + v-model:page="queryParams.pageNo" + v-model:limit="queryParams.pageSize" + @pagination="getList" + /> + </ContentWrap> +</template> + +<script setup lang="ts"> +import { DICT_TYPE, getIntDictOptions } from '@/utils/dict' +import { dateFormatter } from '@/utils/formatTime' +import * as MemberBrokerageRecordApi from '@/api/member/brokerage/record' + +defineOptions({ name: 'MemberBrokerageRecord' }) + +const loading = ref(true) // 列表的加载中 +const total = ref(0) // 列表的总页数 +const list = ref([]) // 列表的数据 +const queryParams = reactive({ + pageNo: 1, + pageSize: 10, + userId: null, + bizType: null, + price: null, + status: null, + createTime: [] +}) +const queryFormRef = ref() // 搜索的表单 + +/** 查询列表 */ +const getList = async () => { + loading.value = true + try { + const data = await MemberBrokerageRecordApi.getMemberBrokerageRecordPage(queryParams) + list.value = data.list + total.value = data.total + } finally { + loading.value = false + } +} + +/** 搜索按钮操作 */ +const handleQuery = () => { + queryParams.pageNo = 1 + getList() +} + +/** 重置按钮操作 */ +const resetQuery = () => { + queryFormRef.value.resetFields() + handleQuery() +} + +/** 初始化 **/ +onMounted(() => { + getList() +}) +</script> diff --git a/src/views/member/point/config/index.vue b/src/views/member/point/config/index.vue index 27c52085..bd0fe733 100644 --- a/src/views/member/point/config/index.vue +++ b/src/views/member/point/config/index.vue @@ -10,47 +10,149 @@ <el-form-item label="hideId" v-show="false"> <el-input v-model="formData.id" /> </el-form-item> - <el-form-item label="积分抵扣" prop="tradeDeductEnable" class="item-bottom"> - <el-switch v-model="formData.tradeDeductEnable" style="user-select: none" /> - </el-form-item> - <el-form-item> - <el-text class="mx-1" size="small" type="info">下单积分是否抵用订单金额</el-text> - </el-form-item> - <el-form-item label="积分抵扣" prop="tradeDeductUnitPrice" class="item-bottom"> - <el-input-number - v-model="computedTradeDeductUnitPrice" - placeholder="请输入积分抵扣金额" - style="width: 300px" - :precision="2" - /> - </el-form-item> - <el-form-item> - <el-text class="mx-1" size="small" type="info"> - 积分抵用比例(1 积分抵多少金额),单位:元 - </el-text> - </el-form-item> - <el-form-item label="积分抵扣最大值" prop="tradeDeductMaxPrice" class="item-bottom"> - <el-input-number - v-model="formData.tradeDeductMaxPrice" - placeholder="请输入积分抵扣最大值" - style="width: 300px" - /> - </el-form-item> - <el-form-item> - <el-text class="mx-1" size="small" type="info">单次下单积分使用上限,0 不限制</el-text> - </el-form-item> - <el-form-item label="1 元赠送多少分" prop="tradeGivePoint" class="item-bottom"> - <el-input-number - v-model="formData.tradeGivePoint" - placeholder="请输入 1 元赠送多少积分" - style="width: 300px" - /> - </el-form-item> - <el-form-item> - <el-text class="mx-1" size="small" type="info"> - 下单支付金额按比例赠送积分(实际支付 1 元赠送多少积分) - </el-text> - </el-form-item> + + <el-tabs> + <el-tab-pane label="积分"> + <el-form-item label="积分抵扣" prop="tradeDeductEnable" class="item-bottom"> + <el-switch v-model="formData.tradeDeductEnable" style="user-select: none" /> + <el-text class="w-full" size="small" type="info">下单积分是否抵用订单金额</el-text> + </el-form-item> + <el-form-item label="积分抵扣" prop="tradeDeductUnitPrice"> + <el-input-number + v-model="computedTradeDeductUnitPrice" + placeholder="请输入积分抵扣金额" + :precision="2" + /> + <el-text class="w-full" size="small" type="info"> + 积分抵用比例(1 积分抵多少金额),单位:元 + </el-text> + </el-form-item> + <el-form-item label="积分抵扣最大值" prop="tradeDeductMaxPrice"> + <el-input-number + v-model="formData.tradeDeductMaxPrice" + placeholder="请输入积分抵扣最大值" + /> + <el-text class="w-full" size="small" type="info"> + 单次下单积分使用上限,0 不限制 + </el-text> + </el-form-item> + <el-form-item label="1 元赠送多少分" prop="tradeGivePoint"> + <el-input-number + v-model="formData.tradeGivePoint" + placeholder="请输入 1 元赠送多少积分" + /> + <el-text class="w-full" size="small" type="info"> + 下单支付金额按比例赠送积分(实际支付 1 元赠送多少积分) + </el-text> + </el-form-item> + </el-tab-pane> + <el-tab-pane label="分销"> + <el-form-item label="分佣启用" prop="brokerageEnabled"> + <el-switch v-model="formData.brokerageEnabled" style="user-select: none" /> + <el-text class="w-full" size="small" type="info"> 商城是否开启分销模式 </el-text> + </el-form-item> + <el-form-item label="分佣模式" prop="brokerageEnabledCondition"> + <el-radio-group v-model="formData.brokerageEnabledCondition"> + <el-radio + v-for="dict in getIntDictOptions(DICT_TYPE.BROKERAGE_ENABLED_CONDITION)" + :key="dict.value" + :label="dict.value" + > + {{ dict.label }} + </el-radio> + </el-radio-group> + <el-text class="w-full" size="small" type="info"> + 人人分销:每个用户都可以成为推广员 + </el-text> + <el-text class="w-full" size="small" type="info"> + 指定分销:仅可在后台手动设置推广员 + </el-text> + </el-form-item> + <el-form-item label="分销关系绑定" prop="brokerageBindMode"> + <el-radio-group v-model="formData.brokerageBindMode"> + <el-radio + v-for="dict in getIntDictOptions(DICT_TYPE.BROKERAGE_BIND_MODE)" + :key="dict.value" + :label="dict.value" + > + {{ dict.label }} + </el-radio> + </el-radio-group> + <el-text class="w-full" size="small" type="info"> + 没有推广人:只要用户没有推广人,随时都可以绑定推广关系 + </el-text> + <el-text class="w-full" size="small" type="info"> + 新用户:只有新用户注册时或首次进入系统时才可以绑定推广关系 + </el-text> + </el-form-item> + <el-form-item label="分销海报图"> + <UploadImgs v-model="formData.brokeragePostUrls" width="75px" height="125px" /> + <el-text class="w-full" size="small" type="info"> + 个人中心分销海报图片,建议尺寸600x1000 + </el-text> + </el-form-item> + <el-form-item label="一级返佣比例" prop="brokerageFirstPercent"> + <el-input-number + v-model="formData.brokerageFirstPercent" + placeholder="请输入一级返佣比例" + /> + <el-text class="w-full" size="small" type="info"> + 订单交易成功后给推广人返佣的百分比 + </el-text> + </el-form-item> + <el-form-item label="二级返佣比例" prop="brokerageSecondPercent"> + <el-input-number + v-model="formData.brokerageSecondPercent" + placeholder="请输入二级返佣比例" + /> + <el-text class="w-full" size="small" type="info"> + 订单交易成功后给推广人的推荐人返佣的百分比 + </el-text> + </el-form-item> + <el-form-item label="佣金冻结天数" prop="brokerageFrozenDays"> + <el-input-number + v-model="formData.brokerageFrozenDays" + placeholder="请输入佣金冻结天数" + /> + <el-text class="w-full" size="small" type="info"> + 防止用户退款,佣金被提现了,所以需要设置佣金冻结时间,单位:天 + </el-text> + </el-form-item> + <el-form-item label="提现最低金额" prop="brokerageWithdrawMinPrice"> + <el-input-number + v-model="formData.brokerageWithdrawMinPrice" + placeholder="请输入用户提现最低金额" + /> + <el-text class="w-full" size="small" type="info"> + 用户提现最低金额限制,单位:元 + </el-text> + </el-form-item> + <el-form-item label="提现方式" prop="brokerageWithdrawType"> + <el-checkbox-group v-model="formData.brokerageWithdrawType"> + <el-checkbox + v-for="dict in getIntDictOptions(DICT_TYPE.BROKERAGE_WITHDRAW_TYPE)" + :key="dict.value" + :label="dict.value" + > + {{ dict.label }} + </el-checkbox> + </el-checkbox-group> + <el-text class="w-full" size="small" type="info"> 商城开通提现的付款方式 </el-text> + </el-form-item> + <el-form-item label="提现银行" prop="brokerageBankNames"> + <el-select v-model="formData.brokerageBankNames" placeholder="请选择提现银行" multiple> + <el-option + v-for="dict in getIntDictOptions(DICT_TYPE.BROKERAGE_BANK_NAME)" + :key="dict.value" + :label="dict.label" + :value="dict.value" + /> + </el-select> + <el-text class="w-full" size="small" type="info"> 商城开通提现的银行列表 </el-text> + </el-form-item> + </el-tab-pane> + </el-tabs> + <el-form-item> <el-button type="primary" @click="onSubmit">保存</el-button> </el-form-item> @@ -59,6 +161,8 @@ </template> <script lang="ts" setup> import * as ConfigApi from '@/api/member/point/config' +import { DICT_TYPE, getIntDictOptions } from '@/utils/dict' +import { BrokerageBindModeEnum, BrokerageEnabledConditionEnum } from '@/utils/constants' defineOptions({ name: 'MemberPointConfig' }) @@ -68,10 +172,21 @@ const message = useMessage() // 消息弹窗 const dialogVisible = ref(false) // 弹窗的是否展示 const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 const formData = ref({ + id: undefined, tradeDeductEnable: true, tradeDeductUnitPrice: 0, tradeDeductMaxPrice: 0, - tradeGivePoint: 0 + tradeGivePoint: 0, + brokerageEnabled: true, + brokerageEnabledCondition: BrokerageEnabledConditionEnum.ALL.condition, + brokerageBindMode: BrokerageBindModeEnum.ANYTIME.mode, + brokeragePostUrls: [], + brokerageFirstPercent: 0, + brokerageSecondPercent: 0, + brokerageWithdrawMinPrice: 0, + brokerageBankNames: [], + brokerageFrozenDays: 0, + brokerageWithdrawType: [] }) // 创建一个计算属性,用于将 tradeDeductUnitPrice 显示为带两位小数的形式 @@ -95,6 +210,9 @@ const onSubmit = async () => { formLoading.value = true try { const data = formData.value as unknown as ConfigApi.ConfigVO + data.brokeragePostUrls = formData.value.brokeragePostUrls.map((item: any) => { + return item?.url ? item.url : item + }) await ConfigApi.saveConfig(data) message.success(t('common.updateSuccess')) dialogVisible.value = false @@ -110,6 +228,7 @@ const getConfig = async () => { if (data === null) { return } + data.brokeragePostUrls = data.brokeragePostUrls.map((url) => ({ url })) formData.value = data } finally { }