新增分销配置

This commit is contained in:
gyq 2025-10-27 13:29:15 +08:00
parent 1e85fd06c8
commit 0cafd8651b
28 changed files with 1517 additions and 65 deletions

View File

@ -39,6 +39,7 @@
"@vueuse/core": "^12.5.0",
"@wangeditor-next/editor": "^5.6.31",
"@wangeditor-next/editor-for-vue": "^5.1.14",
"@wangeditor/editor": "^5.1.23",
"ali-oss": "^6.22.0",
"axios": "^1.7.9",
"bignumber.js": "^9.3.1",

View File

@ -630,9 +630,41 @@ export function suggestDel(params) {
});
}
// 分销-配置信息详情
export function distributionGet(params) {
return request({
url: `${Market_BaseUrl}/admin/distribution`,
method: 'get',
params
});
}
// 分销-配置信息修改
export function distributionPut(data) {
return request({
url: `${Market_BaseUrl}/admin/distribution`,
method: 'put',
data
});
}
// 分销-金额记录
export function distributionFlow(params) {
return request({
url: `${Market_BaseUrl}/admin/distribution/flow`,
method: 'get',
params
});
}
// 分销-现金充值
export function distributionCashPay(data) {
return request({
url: `${Market_BaseUrl}/admin/distribution/cashPay`,
method: 'post',
data
});
}

View File

@ -8,7 +8,7 @@ const FileAPI = {
*/
upload(formData: FormData) {
return request<any, FileInfo>({
url: "/api/v1/files",
url: "/account/admin/common/upload",
method: "post",
data: formData,
headers: {
@ -24,7 +24,7 @@ const FileAPI = {
const formData = new FormData();
formData.append("file", file);
return request<any, FileInfo>({
url: "/api/v1/files",
url: "/account/admin/common/upload",
method: "post",
data: formData,
headers: {

BIN
src/assets/fenxiao/1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

BIN
src/assets/fenxiao/2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

BIN
src/assets/fenxiao/3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

BIN
src/assets/fenxiao/4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

BIN
src/assets/fenxiao/5.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

BIN
src/assets/fenxiao/6.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

BIN
src/assets/fenxiao/7.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

View File

@ -11,24 +11,15 @@
<template>
<div style="z-index: 999; border: 1px solid #ccc">
<!-- 工具栏 -->
<Toolbar
:editor="editorRef"
mode="simple"
:default-config="toolbarConfig"
style="border-bottom: 1px solid #ccc"
/>
<Toolbar :editor="editorRef" mode="simple" :default-config="toolbarConfig" style="border-bottom: 1px solid #ccc" />
<!-- 编辑器 -->
<Editor
v-model="modelValue"
:style="{ height: height, overflowY: 'hidden' }"
:default-config="editorConfig"
mode="simple"
@on-created="handleCreated"
/>
<Editor v-model="modelValue" :style="{ height: height, overflowY: 'hidden' }" :default-config="editorConfig"
mode="simple" @on-created="handleCreated" />
</div>
</template>
<script setup lang="ts">
import dayjs from "dayjs";
import "@wangeditor-next/editor/dist/css/style.css";
import { Toolbar, Editor } from "@wangeditor-next/editor-for-vue";
import { IToolbarConfig, IEditorConfig } from "@wangeditor-next/editor";
@ -37,7 +28,7 @@ import { IToolbarConfig, IEditorConfig } from "@wangeditor-next/editor";
import FileAPI from "@/api/file";
//
type InsertFnType = (url: string, alt: string, href: string) => void;
type InsertFnType = (url: any, alt: any, href: any) => void;
defineProps({
height: {
@ -68,8 +59,11 @@ const editorConfig = ref<Partial<IEditorConfig>>({
customUpload(file: File, insertFn: InsertFnType) {
//
FileAPI.uploadFile(file).then((res) => {
console.log('上传图', res);
//
insertFn(res.url, res.name, res.url);
insertFn(res, dayjs().valueOf(), res);
});
},
} as any,

View File

@ -0,0 +1,111 @@
<template>
<div class="tab_wrap">
<div class="item_wrap" :style="{ gap: `${gap}px` }">
<div class="item" :ref="el => itemRefs[index] = el" v-for="(item, index) in props.list" :key="index"
:class="{ modelValue: modelValue == index }" @click="changeHandle(index)">
<span>{{ item.label }}</span>
</div>
<div class="active_wrap" :style="{ '--left': `${leftValue}px`, '--width': `${itemsWidth[modelValue]}px` }">
</div>
</div>
</div>
</template>
<script setup>
import { ref, onMounted, nextTick } from 'vue'
const props = defineProps({
// {label: '',value: 1}
list: {
type: Array,
default: []
}
})
const emits = defineEmits(['update:modelValue'])
const modelValue = defineModel('modelValue', {
type: [String, Number],
required: true
})
//
function changeHandle(index) {
modelValue.value = index
let left = 0
itemsWidth.value.forEach((val, i) => {
if (i < index) {
left += val
}
})
leftValue.value = left + gap.value * index
}
const itemRefs = ref([])
const itemsWidth = ref([])
const gap = ref(24)
const leftValue = ref(0)
onMounted(() => {
nextTick(() => {
itemRefs.value.forEach((el, index) => {
itemsWidth.value.push(el.offsetWidth)
})
console.log('itemRefs===', itemRefs.value);
console.log('itemsWidth===', itemsWidth.value);
changeHandle(modelValue.value)
})
})
</script>
<style scoped lang="scss">
.tab_wrap {
padding: 12px;
background-color: #F8F8F8;
border-radius: 2px;
.item_wrap {
position: relative;
display: flex;
align-items: center;
.item {
padding: 0 12px;
height: 36px;
display: flex;
align-items: center;
justify-content: center;
position: relative;
z-index: 2;
cursor: pointer;
&.modelValue {
span {
color: #fff;
}
}
span {
font-size: 16px;
color: #999;
transition: all .1s ease-in-out .15s;
}
}
.active_wrap {
width: var(--width);
background-color: var(--el-color-primary);
height: 34px;
position: absolute;
top: 0;
left: var(--left);
z-index: 1;
border-radius: 2px;
transition: all .3s ease-in-out;
}
}
}
</style>

View File

@ -2,8 +2,7 @@
<el-dialog :title="form.id ? '编辑兑换码' : '添加兑换码'" width="500px" v-model="visible" @closed="onClosedHandle">
<el-form ref="formRef" :model="form" :rules="rules" label-width="100px" label-position="right">
<el-form-item label="名称" prop="name">
<el-input v-model="form.name" placeholder="请输入" :maxlength="50" show-word-limit
style="width: 350px;"></el-input>
<el-input v-model="form.name" placeholder="请输入" :maxlength="50" style="width: 350px;"></el-input>
</el-form-item>
<el-form-item label="活动日期" prop="timeScope">
<div style="width: 350px;">
@ -14,7 +13,7 @@
</el-form-item>
<el-form-item label="发行数量" prop="total">
<div class="column">
<el-input v-model="form.total" placeholder="请输入" :maxlength="8" show-word-limit style="width: 350px;"
<el-input v-model="form.total" placeholder="请输入" :maxlength="8" style="width: 350px;"
@input="e => form.total = filterNumberInput(e, true)" :disabled="!!form.id">
<template #append></template>
</el-input>
@ -22,7 +21,7 @@
</div>
</el-form-item>
<el-form-item label="库存" prop="stock" v-if="form.id">
<el-input v-model="form.stock" placeholder="请输入" :maxlength="4" show-word-limit style="width: 350px;"
<el-input v-model="form.stock" placeholder="请输入" :maxlength="4" style="width: 350px;"
@input="e => form.stock = filterNumberInput(e, true)"></el-input>
</el-form-item>
<el-form-item label="赠送优惠券" style="margin-top: 14px;" prop="coupon">

View File

@ -168,16 +168,20 @@ async function rechargeRedemptionEnableStatusAjax() {
const res = await couponRedemptionEnableStatus();
queryForm.value.isEnable = res.isEnable;
if (res.useType == 'all') {
if (shopInfo.value.shopType == 'only') {
isUse.value = true
} else {
let currentShopId = shopInfo.value.shopId;
res.shopIdList.some((item) => {
if (item == currentShopId) {
isUse.value = true;
return true;
}
});
if (res.useType == 'all') {
isUse.value = true
} else {
let currentShopId = shopInfo.value.shopId;
res.shopIdList.some((item) => {
if (item == currentShopId) {
isUse.value = true;
return true;
}
});
}
}
} catch (error) {
console.log(error);

View File

@ -4,7 +4,7 @@
<div class="scroll" ref="scrollRef">
<el-form ref="formRef" :model="form" :rules="formRules" label-width="120px" class="dialog-form">
<el-form-item label="活动名称" prop="title">
<el-input v-model="form.title" :maxlength="20" show-word-limit placeholder="请输入活动名称" style="width: 300px" />
<el-input v-model="form.title" :maxlength="20" placeholder="请输入活动名称" style="width: 300px" />
</el-form-item>
<el-form-item label="可用门店" prop="useShopType" v-if="shopInfo.isHeadShop && shopInfo.shopType != 'only'">
<el-radio-group v-model="form.useShopType">

View File

@ -0,0 +1,155 @@
<!-- 开通记录 -->
<template>
<div>
<div class="row">
<div class="info_wrap">
<div class="item">
<div class="icon">
<img class="img" src="@/assets/fenxiao/1.png">
</div>
<div class="info">
<div>支付开通人数</div>
<div>90000</div>
</div>
</div>
<div class="item">
<div class="icon">
<img class="img" src="@/assets/fenxiao/2.png">
</div>
<div class="info">
<div>已支付金额</div>
<div>99.99</div>
</div>
</div>
</div>
</div>
<div class="row mt14">
<el-form inline>
<el-form-item>
<el-date-picker style="width: 300px" v-model="times" type="daterange" range-separator=""
start-placeholder="开始日期" end-placeholder="结束日期" value-format="YYYY-MM-DD"
@change="selectTimeChange"></el-date-picker>
</el-form-item>
<el-form-item>
<el-input placeholder="请输入用户ID/昵称" style="width: 200px;"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="searchHandle">搜索</el-button>
<el-button @click="resetHandle">重置</el-button>
</el-form-item>
</el-form>
</div>
<div class="row">
<el-table :data="tableData.list" stripe border v-loading="tableData.loading">
<el-table-column label="订单号" prop="id" width="200"></el-table-column>
<el-table-column label="用户" prop="nickName" width="200"></el-table-column>
<el-table-column label="支付金额" prop="nickName"></el-table-column>
<el-table-column label="支付时间" prop="nickName"></el-table-column>
</el-table>
</div>
<div class="row mt14">
<el-pagination v-model:current-page="tableData.page" v-model:page-size="tableData.size"
:page-sizes="[10, 20, 50, 100, 500]" background layout="total, sizes, prev, pager, next, jumper"
:total="tableData.total" @size-change="handleSizeChange" @current-change="handleCurrentChange" />
</div>
</div>
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue'
const queryForm = ref({
user: '',
startTime: '',
endTime: ''
})
function searchHandle() {
tableData.page = 1;
getTableData()
}
function resetHandle() {
queryForm.value.user = ''
queryForm.value.startTime = ''
queryForm.value.startTime = ''
times.value = []
searchHandle()
}
const tableData = reactive({
loading: false,
page: 1,
size: 10,
total: 0,
list: []
})
//
const times = ref([])
function selectTimeChange(e) {
queryForm.value.startTime = dayjs(e[0]).format('YYYY-MM-DD 00:00:00')
queryForm.value.endTime = dayjs(e[1]).format('YYYY-MM-DD 23:59:59')
}
//
function handleSizeChange(e) {
tableData.size = e;
getTableData();
}
//
function handleCurrentChange(e) {
tableData.page = e;
getTableData();
}
async function getTableData() {
try {
} catch (error) {
console.log(error);
}
}
onMounted(() => {
getTableData()
})
</script>
<style scoped lang="scss">
.row {
&.mt14 {
margin-top: 14px;
}
}
.info_wrap {
display: flex;
gap: 48px;
.item {
display: flex;
align-items: center;
border: 1px solid #D9D9D9;
border-radius: 8px;
padding: 0 10px;
.icon {
width: 44px;
height: 44px;
.img {
width: 100%;
height: 100%;
}
}
.info {
flex: 1;
padding: 10px;
}
}
}
</style>

View File

@ -0,0 +1,199 @@
<!-- 分销明细 -->
<template>
<div>
<div class="row">
<div class="info_wrap">
<div class="item">
<div class="icon">
<img class="img" src="@/assets/fenxiao/3.png">
</div>
<div class="info">
<div>已入账金额</div>
<div>90000</div>
</div>
</div>
<div class="item">
<div class="icon">
<img class="img" src="@/assets/fenxiao/4.png">
</div>
<div class="info">
<div>待入账金额</div>
<div>99.99</div>
</div>
</div>
<div class="item">
<div class="icon">
<img class="img" src="@/assets/fenxiao/5.png">
</div>
<div class="info">
<div>运营余额</div>
<div>99.99</div>
</div>
</div>
</div>
</div>
<div class="row mt14">
<el-form inline>
<el-form-item>
<el-date-picker style="width: 300px" v-model="times" type="daterange" range-separator=""
start-placeholder="开始日期" end-placeholder="结束日期" value-format="YYYY-MM-DD"
@change="selectTimeChange"></el-date-picker>
</el-form-item>
<el-form-item>
<el-input placeholder="请输入用户ID/昵称" style="width: 200px;"></el-input>
</el-form-item>
<el-form-item>
<div class="between">
<div class="center">
<el-button type="primary" @click="searchHandle">搜索</el-button>
<el-button @click="resetHandle">重置</el-button>
</div>
<div class="center">
<el-button type="primary" @click="rechargeDialogRef.show()">充值</el-button>
<el-button type="primary" plain>查看记录</el-button>
</div>
</div>
</el-form-item>
</el-form>
</div>
<div class="row">
<el-table :data="tableData.list" stripe border v-loading="tableData.loading">
<el-table-column label="分销员" prop="id" width="200"></el-table-column>
<el-table-column label="用户" prop="nickName" width="200"></el-table-column>
<el-table-column label="等级" prop="nickName"></el-table-column>
<el-table-column label="状态" prop="nickName"></el-table-column>
<el-table-column label="关联订单号" prop="nickName"></el-table-column>
<el-table-column label="总收益(元)" prop="nickName"></el-table-column>
<el-table-column label="创建时间" prop="nickName"></el-table-column>
</el-table>
</div>
<div class="row mt14">
<el-pagination v-model:current-page="tableData.page" v-model:page-size="tableData.size"
:page-sizes="[10, 20, 50, 100, 500]" background layout="total, sizes, prev, pager, next, jumper"
:total="tableData.total" @size-change="handleSizeChange" @current-change="handleCurrentChange" />
</div>
<recharge_dialog ref="rechargeDialogRef" />
<recharge_record_dialog ref="rechargeRecordDialogRef" />
</div>
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue'
import { distributionFlow } from '@/api/coupon'
import recharge_dialog from './recharge_dialog.vue'
import recharge_record_dialog from './recharge_record_dialog.vue'
const rechargeDialogRef = ref(null)
const rechargeRecordDialogRef = ref(null)
const queryForm = ref({
type: '',
user: '',
startTime: '',
endTime: ''
})
function searchHandle() {
tableData.page = 1;
getTableData()
}
function resetHandle() {
queryForm.value.user = ''
queryForm.value.startTime = ''
queryForm.value.startTime = ''
times.value = []
searchHandle()
}
const tableData = reactive({
loading: false,
page: 1,
size: 10,
total: 0,
list: []
})
//
const times = ref([])
function selectTimeChange(e) {
queryForm.value.startTime = dayjs(e[0]).format('YYYY-MM-DD 00:00:00')
queryForm.value.endTime = dayjs(e[1]).format('YYYY-MM-DD 23:59:59')
}
//
function handleSizeChange(e) {
tableData.size = e;
getTableData();
}
//
function handleCurrentChange(e) {
tableData.page = e;
getTableData();
}
async function getTableData() {
try {
const res = await distributionFlow({
page: tableData.page,
size: tableData.size,
type: '',
key: ''
})
} catch (error) {
console.log(error);
}
}
onMounted(() => {
getTableData()
})
</script>
<style scoped lang="scss">
.between {
display: flex;
justify-content: space-between;
gap: 100px;
}
.center {
display: flex;
}
.row {
&.mt14 {
margin-top: 14px;
}
}
.info_wrap {
display: flex;
gap: 48px;
.item {
display: flex;
align-items: center;
border: 1px solid #D9D9D9;
border-radius: 8px;
padding: 0 10px;
.icon {
width: 44px;
height: 44px;
.img {
width: 100%;
height: 100%;
}
}
.info {
flex: 1;
padding: 10px;
}
}
}
</style>

View File

@ -0,0 +1,117 @@
<!-- 分销员列表 -->
<template>
<div>
<el-form inline>
<el-form-item>
<el-select placeholder="全部用户" style="width: 200px;"></el-select>
</el-form-item>
<el-form-item>
<el-date-picker style="width: 300px" v-model="times" type="daterange" range-separator=""
start-placeholder="开始日期" end-placeholder="结束日期" value-format="YYYY-MM-DD"
@change="selectTimeChange"></el-date-picker>
</el-form-item>
<el-form-item>
<el-input placeholder="请输入用户ID/昵称" style="width: 200px;"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="searchHandle">搜索</el-button>
<el-button @click="resetHandle">重置</el-button>
<el-button type="primary" plain>添加分销员</el-button>
</el-form-item>
</el-form>
<div class="row">
<el-table :data="tableData.list" stripe border v-loading="tableData.loading">
<el-table-column label="ID" prop="id" width="80"></el-table-column>
<el-table-column label="用户" prop="nickName" width="200"></el-table-column>
<el-table-column label="总收益(元)" prop="nickName" width="100"></el-table-column>
<el-table-column label="待入账金额(元)" prop="nickName" width="100"></el-table-column>
<el-table-column label="已入账金额(元)" prop="nickName" width="100"></el-table-column>
<el-table-column label="开通方式" prop="nickName" width="100"></el-table-column>
<el-table-column label="开通时间" prop="nickName" width="100"></el-table-column>
<el-table-column label="操作" width="300" fixed="right">
<template #default="scope">
<el-button link type="primary">更改分销组</el-button>
<el-button link type="primary">取消分销员</el-button>
<el-button link type="primary">分销详情</el-button>
</template>
</el-table-column>
</el-table>
</div>
<div class="row mt14">
<el-pagination v-model:current-page="tableData.page" v-model:page-size="tableData.size"
:page-sizes="[10, 20, 50, 100, 500]" background layout="total, sizes, prev, pager, next, jumper"
:total="tableData.total" @size-change="handleSizeChange" @current-change="handleCurrentChange" />
</div>
</div>
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue'
const queryForm = ref({
user: '',
startTime: '',
endTime: ''
})
function searchHandle() {
tableData.page = 1;
getTableData()
}
function resetHandle() {
queryForm.value.user = ''
queryForm.value.startTime = ''
queryForm.value.startTime = ''
times.value = []
searchHandle()
}
const tableData = reactive({
loading: false,
page: 1,
size: 10,
total: 0,
list: []
})
//
const times = ref([])
function selectTimeChange(e) {
queryForm.value.startTime = dayjs(e[0]).format('YYYY-MM-DD 00:00:00')
queryForm.value.endTime = dayjs(e[1]).format('YYYY-MM-DD 23:59:59')
}
//
function handleSizeChange(e) {
tableData.size = e;
getTableData();
}
//
function handleCurrentChange(e) {
tableData.page = e;
getTableData();
}
async function getTableData() {
try {
} catch (error) {
console.log(error);
}
}
onMounted(() => {
getTableData()
})
</script>
<style scoped lang="scss">
.row {
&.mt14 {
margin-top: 14px;
}
}
</style>

View File

@ -0,0 +1,123 @@
<!-- 分销命欢喜 - 充值 -->
<template>
<el-dialog title="充值" width="400px" v-model="visible">
<el-form ref="formRef" :model="form" :rules="rules" label-width="100" label-position="right">
<el-form-item label="选择金额">
<div class="num_list">
<div class="btn" v-for="(item, index) in numList" :key="item.value" @click="numHandle(index)">
<el-button :type="numActive == index ? 'primary' : 'default'">{{ item.value }}</el-button>
</div>
</div>
</el-form-item>
<el-form-item label="自定义金额" prop="amount">
<el-input v-model="form.amount" placeholder="请输入金额" @input="amountInput">
<template #append></template>
</el-input>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<div class="btn">
<el-button type="primary" @click="submitHandle" :loading="confirmLoading" style="width: 100%;"> </el-button>
</div>
<div class="btn">
<el-button @click="visible = false" style="width: 100%;"> </el-button>
</div>
</div>
</template>
</el-dialog>
</template>
<script setup>
import { ref } from 'vue'
import { filterNumberInput } from '@/utils'
import { distributionCashPay } from "@/api/coupon";
const visible = ref(false)
const numList = ref([
{
value: 500
},
{
value: 800
},
{
value: 1000
},
{
value: 2000
}
])
const form = ref({
amount: ''
})
const rules = ref({
amount: [
{
required: true,
message: '请输入或选择金额',
trigger: 'change'
}
]
})
const numActive = ref(-1)
function numHandle(index) {
numActive.value = index
form.value.amount = numList.value[index].value
}
function amountInput(e) {
form.value.amount = filterNumberInput(e)
numActive.value = -1
}
//
const formRef = ref(null)
const confirmLoading = ref(false)
function submitHandle() {
formRef.value.validate(async vaild => {
try {
if (vaild) {
confirmLoading.value = true
const data = { ...form.value }
data.shopId = localStorage.getItem('shopId')
await distributionCashPay(data)
}
} catch (error) {
console.log(error);
}
setTimeout(() => {
confirmLoading.value = false
}, 300);
})
}
function show() {
visible.value = true
form.value.amount = ''
numActive.value = -1
}
defineExpose({
show
})
</script>
<style scoped lang="scss">
.num_list {
display: flex;
flex-wrap: wrap;
gap: 14px;
}
.dialog-footer {
display: flex;
gap: 14px;
.btn {
flex: 1;
}
}
</style>

View File

@ -0,0 +1,133 @@
<template>
<el-dialog title="变动记录" width="1000px" v-model="visible">
<div class="row">
<el-form :model="queryForm" inline>
<el-form-item>
<el-select v-model="queryForm.type" placeholder="请选择状态" style="width: 200px;">
<el-option v-for="item in statusList" :key="item.value" :label="item.label" :value="item.value"></el-option>
</el-select>
</el-form-item>
<el-form-item>
<el-input v-model="queryForm.key" placeholder="请输入用户ID/昵称" style="width: 200px;"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="searchHandle">搜索</el-button>
<el-button @click="resetHandle">重置</el-button>
</el-form-item>
</el-form>
</div>
<div class="row">
<div class="info_wrap">
<div class="item">
<div class="icon">
<img class="img" src="@/assets/fenxiao/5.png">
</div>
<div class="info">
<div>已充值金额</div>
<div>90000</div>
</div>
</div>
<div class="item">
<div class="icon">
<img class="img" src="@/assets/fenxiao/6.png">
</div>
<div class="info">
<div>已扣减金额</div>
<div>99.99</div>
</div>
</div>
</div>
</div>
<div class="row">
<el-table :data="tableData.list"></el-table>
</div>
</el-dialog>
</template>
<script setup>
import { ref } from 'vue'
import { distributionFlow } from '@/api/coupon'
const visible = ref(true)
const statusList = ref([
{
value: 'manual_recharge',
label: '充值'
},
{
value: 'self_recharge',
label: '自助充值'
},
{
value: 'refund',
label: '退款'
},
{
value: 'manual_sub',
label: '手动扣减'
},
{
value: 'sub',
label: '统扣减'
},
])
const queryForm = ref({
type: '',
key: ''
})
function resetHandle() {
}
function searchHandle() { }
const tableData = reactive({
loading: false,
page: 1,
size: 10,
total: 0,
list: []
})
</script>
<style scoped lang="scss">
.mt14 {
margin-top: 14px;
}
.info_wrap {
display: flex;
gap: 48px;
.item {
display: flex;
align-items: center;
border: 1px solid #D9D9D9;
border-radius: 8px;
padding: 0 10px;
.icon {
width: 44px;
height: 44px;
.img {
width: 100%;
height: 100%;
}
}
.info {
flex: 1;
padding: 10px;
}
}
}
</style>

View File

@ -0,0 +1,459 @@
<template>
<div class="container">
<el-form ref="formRef" :model="form" :rules="rules" label-width="150px" label-position="right">
<el-form-item label="开通方式">
<el-radio-group v-model="form.openType">
<el-radio label="自动开通" value="auto"></el-radio>
<el-radio label="手动开通" value="manual"></el-radio>
<el-radio label="付费开通" value="pay"></el-radio>
</el-radio-group>
</el-form-item>
<div v-if="form.openType == 'pay'">
<el-form-item label="付费金额" prop="payAmount">
<el-input v-model="form.payAmount" style="width: 300px;" :maxlength="8" placeholder="请输入"
@input="e => form.payAmount = filterNumberInput(e)"></el-input>
</el-form-item>
</div>
<div v-if="form.openType == 'auto'">
<el-form-item label="获得佣金条件" prop="inviteCount">
<div class="column">
<el-input v-model="form.inviteCount" placeholder="请输入" :maxlength="8" style="width: 300px;"
@input="e => form.inviteCount = filterNumberInput(e, 1)"></el-input>
<div class="tips">邀请达到指定人数才可赚取佣金</div>
</div>
</el-form-item>
<el-form-item label="被邀请人消费有效">
<div class="center">
<el-switch v-model="form.inviteConsume" :active-value="1" :inactive-value="0"></el-switch>
<div class="tips">
开启后被邀请人需要成功支付一次订单才计数
</div>
</div>
</el-form-item>
</div>
<el-form-item label="分销奖励次数">
<div class="column">
<div class="tips">提示:设置限制次数后分销员只能从每个粉丝获得指定次数的奖励超过后的充值和消费订单不再奖励(包含退款订单)</div>
<el-radio-group v-model="isLimitCount" @change="isLimitCountChange">
<el-radio label="限制" :value="0"></el-radio>
<el-radio label="不限制" :value="1"></el-radio>
</el-radio-group>
</div>
</el-form-item>
<el-form-item label="每人奖励" v-if="isLimitCount === 0" prop="rewardCount">
<el-input v-model="form.rewardCount" placeholder="请输入" style="width: 300px;"
@input="e => form.rewardCount = filterNumberInput(e, 1)">
<template #append></template>
</el-input>
</el-form-item>
<el-form-item label="结算时长" prop="settlementDay">
<div class="column">
<div class="center">
<el-input placeholder="请输入" v-model="form.settlementDay" style="width: 300px;"
@input="e => form.settlementDay = filterNumberInput(e, 0)">
<template #append></template>
</el-input>
<div class="tips">结算时长订单完成后{{ form.settlementDay ? form.settlementDay : 0 }}天后分销到账分销员账号中</div>
</div>
<div class="tips">提示需要分销员自行提现可直接提现到微信零钱</div>
</div>
</el-form-item>
<div class="title_row">
分销员等级
</div>
<el-form-item label="条件升级" style="margin-top: 14px;" prop="upgradeType">
<el-radio-group v-model="form.upgradeType" @change="upgradeTypeChange">
<el-radio label="不自动升级" value="not_upgrade"></el-radio>
<el-radio label="邀请有效人数" value="invite"></el-radio>
<el-radio label="消费金额(不含退款)" value="cost"></el-radio>
</el-radio-group>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="form.levelConfigList.push({ ...levelConfigListObj })">添加等级</el-button>
</el-form-item>
<div class="err_wrap">
<el-form-item label-width="0" prop="levelConfigList">
<div class="level_wrap">
<div class="level_wrap_row" v-for="(item, index) in form.levelConfigList" :key="index">
<div class="title">
{{ index + 1 }}{{ item.name || '请输入名称' }}
<div class="del">
<el-icon @click="form.levelConfigList.splice(index, 1)">
<Delete />
</el-icon>
</div>
</div>
<div class="form_wrap">
<div class="form_wrap_row">
<div class="center">
<div class="label">
<span class="required">*</span>
名称
</div>
<div class="ipt">
<el-input v-model="item.name" placeholder="请输入内容" :maxlength="10"
style="width: 200px;"></el-input>
</div>
</div>
</div>
<div class="form_wrap_title" v-if="form.upgradeType != 'not_upgrade'">升级条件</div>
<div class="form_wrap_row">
<div class="column" v-if="form.upgradeType == 'invite'">
<div class="center">
<div class="label">
<span class="required">*</span>有效人数达
</div>
<div class="ipt">
<el-input v-model="item.inviteCount" placeholder="请输入" style="width: 200px;"
@input="e => item.inviteCount = filterNumberInput(e, 0)">
<template #append></template>
</el-input>
</div>
</div>
<div class="tips">有效人数被邀请人在店铺消费过即有一笔订单完成</div>
</div>
<div class="column" v-if="form.upgradeType == 'cost'">
<div class="center">
<div class="label">
<span class="required">*</span>消费金额达
</div>
<div class="ipt">
<el-input v-model="item.costAmount" placeholder="请输入" style="width: 200px;"
@input="e => item.costAmount = filterNumberInput(e)">
<template #append></template>
</el-input>
</div>
</div>
<div class="tips">消费金额分销员和被邀请人在店铺订单消费总金额不包含退款</div>
</div>
</div>
<div class="form_wrap_row">
<div class="column">
<div class="center">
<div class="label">
<span class="required">*</span>订单一级分成
</div>
<div class="ipt">
<el-input v-model="item.levelOneCommission" placeholder="请输入" :maxlength="2"
style="width: 200px;" @input="e => item.levelOneCommission = filterNumberInput(e, 1)">
<template #append>%</template>
</el-input>
</div>
</div>
</div>
<div class="column">
<div class="center">
<div class="label">订单二级分成</div>
<div class="ipt">
<el-input v-model="item.levelTwoCommission" placeholder="请输入" :maxlength="2"
style="width: 200px;" @input="e => item.levelTwoCommission = filterNumberInput(e, 1)">
<template #append>%</template>
</el-input>
</div>
</div>
<div class="tips">二级分成不填或为0时则不进行二级分成</div>
</div>
</div>
</div>
</div>
</div>
</el-form-item>
</div>
<div class="title_row mt14">未开通页面营销</div>
<el-form-item :label-width="0" style="margin-top: 14px;">
<WangEditor v-model="form.notActivatedPage" />
</el-form-item>
</el-form>
<div class="footer_wrap">
<div class="btn">
<el-button size="large" style="width: 100%;" type="primary" :loading="formLoading"
@click="submintHandle">保存</el-button>
</div>
<div class="btn">
<el-button size="large" style="width: 100%;" @click="router.back()">取消</el-button>
</div>
</div>
</div>
</template>
<script setup>
import { ref, onMounted, nextTick } from 'vue'
import { distributionGet, distributionPut } from '@/api/coupon'
import { filterNumberInput } from '@/utils'
import { useRouter } from 'vue-router'
const router = useRouter()
const isLimitCount = ref(0)
function isLimitCountChange(e) {
console.log(e);
if (e === 1) {
form.value.rewardCount = -1
} else {
form.value.rewardCount = ''
}
}
const formRef = ref(null)
const formLoading = ref(false)
const levelConfigListObj = ref({
name: '', //
inviteCount: '', //
costAmount: '', //
levelOneCommission: '', //
levelTwoCommission: '', //
})
const form = ref({
id: '',
openType: 'pay', // pay auto manual
inviteCount: '', //
inviteConsume: 0, // 0 1
payAmount: '', //
rewardCount: '', //
settlementDay: '', // n
upgradeType: 'invite', // not_upgrade invite cost
notActivatedPage: '', //
levelConfigList: [{ ...levelConfigListObj.value }]
})
const rules = ref({
payAmount: [
{
required: true,
message: '请输入付费金额',
trigger: 'blur'
}
],
inviteCount: [
{
required: true,
message: '请输入获得佣金条件',
trigger: 'blur'
}
],
rewardCount: [
{
required: true,
message: '请输入每人奖励',
trigger: 'blur'
}
],
settlementDay: [
{
required: true,
message: '请输入结算时长',
trigger: 'blur'
}
],
upgradeType: [
{
trigger: 'change',
validator: (rule, value, callback) => {
if ((form.value.upgradeType == 'invite' || form.value.upgradeType == 'cost') && form.value.levelConfigList.length == 0) {
callback(new Error('请至少添加一个等级'))
return
}
callback()
}
}
],
levelConfigList: [
{
trigger: 'change',
validator: (rule, value, callback) => {
let tips = '';
form.value.levelConfigList.forEach((item, index) => {
if (item.name === '') {
tips = `请输入${index + 1}级的名称`
callback(new Error(tips))
return
}
if (item.inviteCount === '' && form.value.upgradeType == 'invite') {
tips = `请输入${index + 1}级的有效人数`
callback(new Error(tips))
return
}
if (item.costAmount === '' && form.value.upgradeType == 'cost') {
tips = `请输入${index + 1}级的消费金额`
callback(new Error(tips))
return
}
if (item.levelOneCommission === '') {
tips = `请输入${index + 1}级的订单一级分成`
callback(new Error(tips))
return
}
})
callback()
}
}
]
})
//
function upgradeTypeChange(e) {
form.value.levelConfigList = [{ ...levelConfigListObj.value }]
// if (e == 'not_upgrade') {
// form.value.levelConfigList = []
// } else {
// }
}
//
function submintHandle() {
console.log(form.value);
formRef.value.validate(async valid => {
try {
if (valid) {
await distributionPut(form.value)
ElNotification({
title: '注意',
message: '保存成功',
type: 'success'
})
}
} catch (error) {
console.log(error);
}
})
}
//
async function distributionGetAjax() {
try {
const res = await distributionGet()
nextTick(() => {
form.value = { ...res }
})
} catch (error) {
console.log(error);
}
}
onMounted(async () => {
await distributionGetAjax()
// nextTick(() => {
// formRef.value.resetFields()
// })
})
</script>
<style scoped lang="scss">
.mt14 {
margin-top: 14px;
}
.container {
padding-bottom: 100px;
}
.tips {
white-space: nowrap;
font-size: 12px;
color: #999;
}
.column {
display: flex;
flex-direction: column;
}
.center {
display: flex;
gap: 12px;
align-items: flex-start;
}
.title_row {
padding: 14px 20px;
background-color: #F8F8F8;
font-size: 16px;
color: #333;
}
.level_wrap {
.level_wrap_row {
display: flex;
margin-bottom: 24px;
.title {
font-size: 14px;
color: #333;
width: 150px;
display: flex;
justify-content: flex-end;
padding-right: 10px;
display: flex;
// align-items: center;
gap: 10px;
.del {
position: relative;
top: 2px;
}
}
.form_wrap {
flex: 1;
.form_wrap_title {
font-size: 14px;
color: #333;
font-weight: bold;
}
.form_wrap_row {
display: grid;
grid-template-columns: repeat(2, 1fr);
grid-template-rows: repeat(2, auto);
grid-column-gap: 34px;
grid-row-gap: 14px;
.label {
width: 120px;
font-size: 16px;
color: #666;
}
.ipt {
flex: 1;
}
}
}
}
}
.footer_wrap {
--w: 600px;
width: var(--w);
padding: 10px 12px;
position: fixed;
left: 50%;
margin-left: calc(var(--w) / 2 * -1);
bottom: 44px;
z-index: 999;
background-color: #fff;
border-radius: 10px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
display: flex;
justify-content: center;
gap: 14px;
.btn {
flex: 1;
}
}
.err_wrap {
::v-deep .el-form-item__error {
/* 假设输入框左侧有一定间距,调整到与输入框左侧对齐 */
margin-left: 150px;
}
}
.required {
color: var(--el-color-danger);
font-size: 12px;
margin-right: 4px;
}
</style>

View File

@ -0,0 +1,120 @@
<template>
<div class="gyq_container">
<div class="gyq_content">
<HeaderCard name="分销" intro="用户成为业务员,可促进消费" icon="xffx"
:showSwitch="shopInfo.isHeadShop == 1 || shopInfo.shopType == 'only'" v-model:isOpen="form.isEnable">
</HeaderCard>
<div class="row mt14">
<tabHeader v-model="tabActiveIndex" :list="tabList" @change="tabChange" />
</div>
<div class="row mt14">
<!-- 基础设置 -->
<setting v-if="tabActiveIndex == 0" />
<!-- 分销员列表 -->
<distributor v-if="tabActiveIndex == 1" />
<!-- 开通记录 -->
<activation_record v-if="tabActiveIndex == 2" />
<!-- 分销明细 -->
<distribution_details v-if="tabActiveIndex == 3" />
</div>
</div>
</div>
</template>
<script setup>
import HeaderCard from "../components/headerCard.vue";
import tabHeader from "../components/tabHeader.vue";
import setting from "./components/setting.vue";
import distributor from "./components/distributor.vue";
import activation_record from "./components/activation_record.vue";
import distribution_details from "./components/distribution_details.vue";
import { ref, onMounted, watch } from 'vue'
import { distributionGet, distributionPut } from '@/api/coupon'
const shopInfo = ref(JSON.parse(localStorage.getItem('userInfo')))
const tabActiveIndex = ref(0)
const tabList = ref([
{
label: '基础设置',
value: 1
},
{
label: '分销员列表',
value: 2
},
{
label: '开通记录',
value: 3
},
{
label: '分销明细',
value: 4
},
])
//
function tabChange(e) {
tabActive.value = e.item.value
}
const form = ref({
isEnable: 1
})
watch(
() => form.value.isEnable,
(newVal) => {
if (!loading.value) {
distributionPutAjax();
}
}
);
//
async function distributionPutAjax() {
try {
await distributionPut(form.value)
} catch (error) {
console.log(error);
}
}
//
const loading = ref(true)
async function distributionGetAjax() {
try {
const res = await distributionGet()
form.value.isEnable = res.isEnable
} catch (error) {
console.log(error);
}
setTimeout(() => {
loading.value = false
}, 300);
}
onMounted(() => {
distributionGetAjax()
})
</script>
<style scoped lang="scss">
.gyq_container {
padding: 14px;
.gyq_content {
padding: 14px;
background-color: #fff;
border-radius: 8px;
}
}
.row {
&.mt14 {
margin-top: 14px;
}
}
</style>

View File

@ -64,7 +64,7 @@
</div>
</div>
<div class="gyq_content" v-else>
门店未参券兑换码活动如需开启参与请联系主店
门店未参与霸王餐活动如需开启参与请联系主店
</div>
</div>
</template>
@ -138,16 +138,20 @@ async function freeDingGetAjax() {
form.value = { ...res }
form.value.enable = res.enable ? 1 : 0
if (res.useType == 'all') {
if (shopInfo.value.shopType == 'only') {
isUse.value = true
} else {
let currentShopId = shopInfo.value.shopId;
res.shopIdList.some((item) => {
if (item == currentShopId) {
isUse.value = true;
return true;
}
});
if (res.useType == 'all') {
isUse.value = true
} else {
let currentShopId = shopInfo.value.shopId;
res.shopIdList.some((item) => {
if (item == currentShopId) {
isUse.value = true;
return true;
}
});
}
}
} catch (err) {
console.log(err);

View File

@ -52,7 +52,7 @@ const menus = ref([
pathName: "points",
intro: "设置充值消费的N倍当前订单立即免单",
},
{ name: "充值活动", icon: "czhd", pathName: "", intro: "允许客户充值并使用余额支付" },
// { name: "", icon: "czhd", pathName: "", intro: "使" },
{ name: "弹窗广告", icon: "tcgg", pathName: "", intro: "设置弹窗广告" },
{ name: "超级会员", icon: "cjhy", pathName: "superVip", intro: "用户会员管理设置" },
{ name: "新客立减", icon: "xklj", pathName: "newUserDiscount", intro: "首单下单减免金额" },
@ -62,7 +62,7 @@ const menus = ref([
pathName: "wisdom_recharge",
intro: "允许客户充值并使用余额支付",
},
{ name: "分销", icon: "zhcz", pathName: "", intro: "允许客户充值并使用余额支付" },
{ name: "分销", icon: "zhcz", pathName: "distribution_page", intro: "用户成为业务员,可促进消费" },
{
name: "消费返现",
icon: "xffx",

View File

@ -93,16 +93,16 @@
</el-form-item>
<div class="title">发送内容</div>
<el-form-item label="商家名称">
<el-input placeholder="请输入文字" v-model="form.shopName" disabled :maxlength="20" show-word-limit
<el-input placeholder="请输入文字" v-model="form.shopName" disabled :maxlength="20"
style="width: 300px;"></el-input>
</el-form-item>
<el-form-item label="商家地址" prop="address">
<el-input placeholder="请输入文字" type="textarea" :rows="4" v-model="form.address" :maxlength="10" show-word-limit
<el-input placeholder="请输入文字" type="textarea" :rows="4" v-model="form.address" :maxlength="10"
style="width: 300px;"></el-input>
</el-form-item>
<el-form-item label="活动描述" prop="activityDetail">
<el-input type="textarea" v-model="form.activityDetail" :rows="4" placeholder="请输入活动描述" :maxlength="20"
show-word-limit style="width: 300px;"></el-input>
style="width: 300px;"></el-input>
</el-form-item>
<el-form-item label="活动时间" prop="activityTime">
<el-date-picker v-model="form.activityTime" type="datetime" placeholder="请选择时间" format="YYYY-MM-DD HH:mm:ss"

View File

@ -2,8 +2,7 @@
<el-dialog :title="form.id ? '编辑点单智能' : '添加点单智能'" width="600px" v-model="visible" @closed="onClosed">
<el-form ref="formRef" :model="form" :rules="rules" label-width="120px" label-position="right">
<el-form-item label="模版名称" prop="title">
<el-input v-model="form.title" placeholder="请输入模版名称" :maxlength="20" show-word-limit
style="width: 300px;"></el-input>
<el-input v-model="form.title" placeholder="请输入模版名称" :maxlength="20" style="width: 300px;"></el-input>
</el-form-item>
<el-form-item label="选择商品" prop="foods">
<!-- <el-cascader v-model="goodsTypeCascaderValue" :options="goodsList" :props="cascaderProps"
@ -15,8 +14,7 @@
</el-select>
</el-form-item>
<el-form-item label="自定义引导语" prop="guideDetail">
<el-input v-model="form.guideDetail" placeholder="请输入自定义引导语" :maxlength="12" show-word-limit
style="width: 300px;"></el-input>
<el-input v-model="form.guideDetail" placeholder="请输入自定义引导语" :maxlength="12" style="width: 300px;"></el-input>
</el-form-item>
<el-form-item label="可用周期" prop="useDays">
<el-checkbox-group v-model="form.useDays">

View File

@ -2,8 +2,7 @@
<el-dialog title="添加兑换码" width="500px" v-model="visible" @closed="onClosed">
<el-form ref="formRef" :model="form" :rules="rules" label-width="80px" label-position="right">
<el-form-item label="名称" prop="name">
<el-input v-model="form.name" placeholder="请输入" :maxlength="50" show-word-limit
style="width: 350px;"></el-input>
<el-input v-model="form.name" placeholder="请输入" :maxlength="50" style="width: 350px;"></el-input>
</el-form-item>
<el-form-item label="有效期" prop="timeScope">
<div style="width: 350px;">
@ -13,18 +12,18 @@
</div>
</el-form-item>
<el-form-item label="金额" prop="amount">
<el-input v-model="form.amount" placeholder="请输入" :maxlength="8" show-word-limit style="width: 350px;"
<el-input v-model="form.amount" placeholder="请输入" :maxlength="8" style="width: 350px;"
@input="e => form.amount = filterNumberInput(e)" :disabled="!!form.id"></el-input>
</el-form-item>
<el-form-item label="发行数量" prop="total">
<div class="column">
<el-input v-model="form.total" placeholder="请输入" :maxlength="4" show-word-limit style="width: 350px;"
<el-input v-model="form.total" placeholder="请输入" :maxlength="4" style="width: 350px;"
@input="e => form.total = filterNumberInput(e, true)" :disabled="!!form.id"></el-input>
<div class="tips" style="color: #999;">单次发行上限1000个如需更多请分多次创建</div>
</div>
</el-form-item>
<el-form-item label="库存" prop="stock" v-if="form.id">
<el-input v-model="form.stock" placeholder="请输入" :maxlength="4" show-word-limit style="width: 350px;"
<el-input v-model="form.stock" placeholder="请输入" :maxlength="4" style="width: 350px;"
@input="e => form.stock = filterNumberInput(e, true)"></el-input>
</el-form-item>
</el-form>

View File

@ -52,7 +52,7 @@
</div>
</div>
<div class="gyq_content" v-else>
门店未参兑换码活动如需开启参与请联系主店
门店未参与充值兑换码活动如需开启参与请联系主店
</div>
<useShops ref="useShopsRef" />
<add ref="addRef" @success="getTableData" />
@ -150,16 +150,20 @@ async function rechargeRedemptionEnableStatusAjax() {
const res = await rechargeRedemptionEnableStatus();
queryForm.value.isEnable = res.isEnable;
if (res.useType == 'all') {
if (shopInfo.value.shopType == 'only') {
isUse.value = true
} else {
let currentShopId = shopInfo.value.shopId;
res.shopIdList.some((item) => {
if (item == currentShopId) {
isUse.value = true;
return true;
}
});
if (res.useType == 'all') {
isUse.value = true
} else {
let currentShopId = shopInfo.value.shopId;
res.shopIdList.some((item) => {
if (item == currentShopId) {
isUse.value = true;
return true;
}
});
}
}
} catch (error) {
console.log(error);