mirror of
https://gitee.com/beijing_hongye_huicheng/lilishop-ui.git
synced 2026-08-06 19:07:25 +08:00
增加二级分销相关设置以及页面
This commit is contained in:
@@ -356,33 +356,19 @@ export function distribution () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 申请成为分销商
|
* 提交招募申请
|
||||||
* @param idNumber 身份证号
|
|
||||||
* @param name 名字
|
|
||||||
*/
|
*/
|
||||||
export function applyDistribution (params) {
|
export function submitRecruitApplication (data) {
|
||||||
return request({
|
return request({
|
||||||
url: `/buyer/distribution/distribution`,
|
url: `/buyer/distribution/distribution/recruit/apply`,
|
||||||
method: Method.POST,
|
method: Method.POST,
|
||||||
needToken: true,
|
needToken: true,
|
||||||
params
|
data
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取分销商订单列表
|
* 获取分销员商品列表
|
||||||
*/
|
|
||||||
export function getDistOrderList (params) {
|
|
||||||
return request({
|
|
||||||
url: `/buyer/distribution/order`,
|
|
||||||
method: Method.GET,
|
|
||||||
needToken: true,
|
|
||||||
params
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取分销商商品列表
|
|
||||||
*/
|
*/
|
||||||
export function getDistGoodsList (params) {
|
export function getDistGoodsList (params) {
|
||||||
return request({
|
return request({
|
||||||
@@ -408,7 +394,7 @@ export function selectDistGoods (params) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 分销商提现历史
|
* 分销员提现历史
|
||||||
*/
|
*/
|
||||||
export function distCashHistory (params) {
|
export function distCashHistory (params) {
|
||||||
return request({
|
return request({
|
||||||
@@ -420,7 +406,7 @@ export function distCashHistory (params) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 分销商提现
|
* 分销员提现
|
||||||
*/
|
*/
|
||||||
export function distCash (params) {
|
export function distCash (params) {
|
||||||
return request({
|
return request({
|
||||||
|
|||||||
@@ -4,24 +4,11 @@
|
|||||||
<!-- 分销申请 -->
|
<!-- 分销申请 -->
|
||||||
|
|
||||||
<div v-if="status === 0">
|
<div v-if="status === 0">
|
||||||
<el-alert type="warning">分销商申请</el-alert>
|
<el-alert type="warning">分销员申请</el-alert>
|
||||||
<el-form ref="form" :model="applyForm" :rules="rules">
|
<el-form ref="form" :model="applyForm" :rules="rules">
|
||||||
<el-form-item label="姓名" prop="name">
|
<el-form-item>
|
||||||
<el-input v-model="applyForm.name"></el-input>
|
<el-alert type="info" show-icon :closable="false" title="分销员申请已改为招募流程,请通过招募入口加入。" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="身份证号" prop="idNumber">
|
|
||||||
<el-input v-model="applyForm.idNumber"></el-input>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="银行开户行" prop="settlementBankBranchName">
|
|
||||||
<el-input v-model="applyForm.settlementBankBranchName"></el-input>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="银行开户名" prop="settlementBankAccountName">
|
|
||||||
<el-input v-model="applyForm.settlementBankAccountName"></el-input>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="银行账号" prop="settlementBankAccountNum">
|
|
||||||
<el-input v-model="applyForm.settlementBankAccountNum"></el-input>
|
|
||||||
</el-form-item>
|
|
||||||
|
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-button type="primary" :loading="applyLoading" @click="apply"
|
<el-button type="primary" :loading="applyLoading" @click="apply"
|
||||||
>提交申请</el-button
|
>提交申请</el-button
|
||||||
@@ -266,14 +253,12 @@ import { Message } from "@/utils/message";
|
|||||||
import { Edit } from '@element-plus/icons-vue';
|
import { Edit } from '@element-plus/icons-vue';
|
||||||
import {
|
import {
|
||||||
distribution,
|
distribution,
|
||||||
applyDistribution,
|
submitRecruitApplication,
|
||||||
distCash,
|
distCash,
|
||||||
distCashHistory,
|
distCashHistory,
|
||||||
getDistGoodsList,
|
getDistGoodsList,
|
||||||
selectDistGoods,
|
selectDistGoods,
|
||||||
} from "@/api/member.js";
|
} from "@/api/member.js";
|
||||||
import { IDCard } from "@/plugins/RegExp.js";
|
|
||||||
import { checkBankno } from "@/plugins/Foundation";
|
|
||||||
import vueQr from "vue-qr";
|
import vueQr from "vue-qr";
|
||||||
import config from "@/config";
|
import config from "@/config";
|
||||||
export default {
|
export default {
|
||||||
@@ -285,37 +270,7 @@ export default {
|
|||||||
status: 0, // 申请状态,0为未申请 1 申请中 2 申请完成 3 功能暂未开启
|
status: 0, // 申请状态,0为未申请 1 申请中 2 申请完成 3 功能暂未开启
|
||||||
applyForm: {}, // 申请表单
|
applyForm: {}, // 申请表单
|
||||||
rules: {
|
rules: {
|
||||||
// 验证规则
|
// 验证规则(V2 招募流程,旧实名认证字段已移除)
|
||||||
name: [{ required: true, message: "请输入真实姓名" }],
|
|
||||||
idNumber: [
|
|
||||||
{ required: true, message: "请输入身份证号" },
|
|
||||||
{ pattern: IDCard, message: "请输入正确的身份证号" },
|
|
||||||
],
|
|
||||||
settlementBankBranchName: [
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
message: "请输入银行开户行",
|
|
||||||
// 可以单个或者同时写两个触发验证方式
|
|
||||||
trigger: "blur",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
settlementBankAccountName: [
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
message: "请输入银行开户名",
|
|
||||||
// 可以单个或者同时写两个触发验证方式
|
|
||||||
trigger: "blur",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
//银行账号
|
|
||||||
settlementBankAccountNum: [
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
message: "银行账号不正确",
|
|
||||||
// 可以单个或者同时写两个触发验证方式
|
|
||||||
trigger: "blur",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
tabName: "goodsChecked", // 当前所在tab
|
tabName: "goodsChecked", // 当前所在tab
|
||||||
result: {}, // 审核结果
|
result: {}, // 审核结果
|
||||||
@@ -356,11 +311,11 @@ export default {
|
|||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
apply() {
|
apply() {
|
||||||
// 申请成为分销商
|
// 申请成为分销员
|
||||||
this.$refs.form.validate((valid) => {
|
this.$refs.form.validate((valid) => {
|
||||||
if (valid) {
|
if (valid) {
|
||||||
this.applyLoading = true;
|
this.applyLoading = true;
|
||||||
applyDistribution(this.applyForm).then((res) => {
|
submitRecruitApplication({ agreementAccepted: true }).then((res) => {
|
||||||
this.applyLoading = false;
|
this.applyLoading = false;
|
||||||
if (res.success) {
|
if (res.success) {
|
||||||
Message.success("申请已提交,请等待管理员审核");
|
Message.success("申请已提交,请等待管理员审核");
|
||||||
@@ -496,7 +451,7 @@ export default {
|
|||||||
},
|
},
|
||||||
//申诉
|
//申诉
|
||||||
repaying(){
|
repaying(){
|
||||||
applyDistribution().then((res) => {
|
submitRecruitApplication({ agreementAccepted: true }).then((res) => {
|
||||||
this.applyLoading = false;
|
this.applyLoading = false;
|
||||||
if (res.success) {
|
if (res.success) {
|
||||||
Message.success("申诉已提交,请等待管理员审核");
|
Message.success("申诉已提交,请等待管理员审核");
|
||||||
@@ -505,7 +460,7 @@ export default {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
distribution() {
|
distribution() {
|
||||||
// 获取分销商信息
|
// 获取分销员信息
|
||||||
distribution()
|
distribution()
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
if (res?.result) {
|
if (res?.result) {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ var BASE = {
|
|||||||
* @description api请求基础路径
|
* @description api请求基础路径
|
||||||
*/
|
*/
|
||||||
API_DEV: {
|
API_DEV: {
|
||||||
manager: "https://admin-api.pickmall.cn",
|
manager: "http://127.0.0.1:8887",
|
||||||
},
|
},
|
||||||
API_PROD: {
|
API_PROD: {
|
||||||
manager: "https://admin-api.pickmall.cn"
|
manager: "https://admin-api.pickmall.cn"
|
||||||
|
|||||||
@@ -1,11 +1,45 @@
|
|||||||
|
|
||||||
// 统一请求路径前缀在libs/axios.js中修改
|
// 统一请求路径前缀在libs/axios.js中修改
|
||||||
import {getRequest, postRequest, putRequest, deleteRequest} from '@/libs/axios';
|
import {getRequest, postRequest, postRequestWithNoForm, putRequest, putRequestWithNoForm, deleteRequest, importRequest} from '@/libs/axios';
|
||||||
|
|
||||||
//查询分销员
|
//查询分销员
|
||||||
export const getDistributionListData = (params) => {
|
export const getDistributionListData = (params) => {
|
||||||
return getRequest('/distribution/distribution/getByPage', params)
|
return getRequest('/distribution/distribution/getByPage', params)
|
||||||
}
|
}
|
||||||
|
// 分销员聚合分页(V2)
|
||||||
|
export const getDistributionMemberPage = (params) => {
|
||||||
|
return getRequest('/distribution/distribution/memberPage', params)
|
||||||
|
}
|
||||||
|
export const getDistributionMemberDetail = (id) => {
|
||||||
|
return getRequest(`/distribution/distribution/memberDetail/${id}`)
|
||||||
|
}
|
||||||
|
export const updateDistributionMember = (id, params) => {
|
||||||
|
return putRequestWithNoForm(`/distribution/distribution/member/${id}`, params)
|
||||||
|
}
|
||||||
|
export const createDistributionMember = (params) => {
|
||||||
|
return postRequestWithNoForm('/distribution/distribution/member', params)
|
||||||
|
}
|
||||||
|
export const batchUpdateDistributionGrade = (params) => {
|
||||||
|
return putRequestWithNoForm('/distribution/distribution/member/batchGrade', params)
|
||||||
|
}
|
||||||
|
export const exportDistributionMembers = (params) => {
|
||||||
|
return getRequest('/distribution/distribution/memberExport', params, 'blob')
|
||||||
|
}
|
||||||
|
export const downloadDistributionImportTemplate = () => {
|
||||||
|
return getRequest('/distribution/memberImport/template', {}, 'blob')
|
||||||
|
}
|
||||||
|
export const importDistributionMembers = (formData) => {
|
||||||
|
return importRequest('/distribution/memberImport/import', formData)
|
||||||
|
}
|
||||||
|
export const getDistributionImportBatches = (params) => {
|
||||||
|
return getRequest('/distribution/memberImport/batch', params)
|
||||||
|
}
|
||||||
|
export const getDistributionImportItems = (batchId, params) => {
|
||||||
|
return getRequest(`/distribution/memberImport/batch/${batchId}/items`, params)
|
||||||
|
}
|
||||||
|
export const downloadDistributionImportFailItems = (batchId) => {
|
||||||
|
return getRequest(`/distribution/memberImport/batch/${batchId}/failExport`, {}, 'blob')
|
||||||
|
}
|
||||||
//审核分销员
|
//审核分销员
|
||||||
export const auditDistribution = (id, params) => {
|
export const auditDistribution = (id, params) => {
|
||||||
return putRequest(`/distribution/distribution/audit/${id}`, params)
|
return putRequest(`/distribution/distribution/audit/${id}`, params)
|
||||||
@@ -34,6 +68,50 @@ export const getDistributionSetting = (id, params) => {
|
|||||||
return getRequest(`/distribution/setting/get/${id}`, params)
|
return getRequest(`/distribution/setting/get/${id}`, params)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// V2客户关系设置
|
||||||
|
export const getDistributionCustomerSetting = () => getRequest('/distribution/setting/v2/customer')
|
||||||
|
export const putDistributionCustomerSetting = (params) => putRequestWithNoForm('/distribution/setting/v2/customer', params)
|
||||||
|
// V2结算方案设置
|
||||||
|
export const getDistributionSettlementSetting = () => getRequest('/distribution/setting/v2/settlement')
|
||||||
|
export const putDistributionSettlementSetting = (params) => putRequestWithNoForm('/distribution/setting/v2/settlement', params)
|
||||||
|
// 分销商品设置
|
||||||
|
export const getDistributionFeaturedGoodsSetting = () => getRequest('/distribution/setting/v2/featured-goods')
|
||||||
|
export const putDistributionFeaturedGoodsSetting = (params) => putRequestWithNoForm('/distribution/setting/v2/featured-goods', params)
|
||||||
|
// 平台海报设置
|
||||||
|
export const getDistributionPromotionPosterSetting = () => getRequest('/distribution/setting/v2/promotion-poster')
|
||||||
|
export const putDistributionPromotionPosterSetting = (params) => putRequestWithNoForm('/distribution/setting/v2/promotion-poster', params)
|
||||||
|
// 邀请卡设置
|
||||||
|
export const getDistributionInviteCardSetting = () => getRequest('/distribution/setting/v2/invite-card')
|
||||||
|
export const putDistributionInviteCardSetting = (params) => putRequestWithNoForm('/distribution/setting/v2/invite-card', params)
|
||||||
|
// 分销等级
|
||||||
|
export const getDistributionGrades = () => getRequest('/distribution/grade')
|
||||||
|
export const saveAllDistributionGrades = (params) => putRequestWithNoForm('/distribution/grade/batch', params)
|
||||||
|
export const deleteDistributionGrade = (id) => deleteRequest(`/distribution/grade/${id}`)
|
||||||
|
export const getDistributionGradeHistoryPage = (params) => getRequest('/distribution/gradeHistory', params)
|
||||||
|
|
||||||
|
// 客户关系
|
||||||
|
export const getCustomerRelations = (params) => getRequest('/distribution/customerRelation', params)
|
||||||
|
export const getCustomerRelationDetail = (id) => getRequest(`/distribution/customerRelation/${id}`)
|
||||||
|
export const getCustomerRelationLogs = (id) => getRequest(`/distribution/customerRelation/${id}/logs`)
|
||||||
|
export const rebindCustomerRelation = (params) => putRequest('/distribution/customerRelation/rebind', params)
|
||||||
|
export const unbindCustomerRelation = (params) => putRequest('/distribution/customerRelation/unbind', params)
|
||||||
|
|
||||||
|
// 分销招募设置
|
||||||
|
export const getCurrentRecruitPlan = () => getRequest('/distribution/recruit/plan/current')
|
||||||
|
export const getPublishedRecruitPlan = () => getRequest('/distribution/recruit/plan/published')
|
||||||
|
export const saveRecruitPlan = (params) => postRequestWithNoForm('/distribution/recruit/plan', params)
|
||||||
|
export const getDistributionRecruitSetting = () => getRequest('/distribution/setting/v2/recruit')
|
||||||
|
export const putDistributionRecruitSetting = (params) => putRequestWithNoForm('/distribution/setting/v2/recruit', params)
|
||||||
|
export const publishRecruitPlan = (planId) => putRequest(`/distribution/recruit/plan/publish/${planId}`)
|
||||||
|
export const auditRecruitApplication = (applicationId, params) => putRequest(`/distribution/recruit/application/audit/${applicationId}`, params)
|
||||||
|
|
||||||
|
// 统计V2
|
||||||
|
export const getDistributionOverviewV2 = (params) => getRequest('/statistics/distribution/v2/overview', params)
|
||||||
|
export const getDistributionBehaviorV2 = (params) => getRequest('/statistics/distribution/v2/behavior', params)
|
||||||
|
export const getDistributionGoodsV2 = (params) => getRequest('/statistics/distribution/v2/goods', params)
|
||||||
|
|
||||||
|
// 钱包流水
|
||||||
|
export const getDistributionWalletLogs = (params) => getRequest('/distribution/walletLog', params)
|
||||||
|
|
||||||
//获取分销订单列表
|
//获取分销订单列表
|
||||||
export const getDistributionOrder = (params) => {
|
export const getDistributionOrder = (params) => {
|
||||||
@@ -48,6 +126,10 @@ export const getDistributionGoods = (params) => {
|
|||||||
export const delDistributionGoods = (ids) => {
|
export const delDistributionGoods = (ids) => {
|
||||||
return deleteRequest(`/distribution/goods/delByIds/${ids}`)
|
return deleteRequest(`/distribution/goods/delByIds/${ids}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const auditDistributionGoods = (params) => {
|
||||||
|
return putRequestWithNoForm('/distribution/goods/audit', params)
|
||||||
|
}
|
||||||
//获取分销佣金信息
|
//获取分销佣金信息
|
||||||
export const getDistributionCash = (params) => {
|
export const getDistributionCash = (params) => {
|
||||||
return getRequest('/distribution/cash/getByPage', params)
|
return getRequest('/distribution/cash/getByPage', params)
|
||||||
@@ -56,6 +138,3 @@ export const getDistributionCash = (params) => {
|
|||||||
export const auditDistributionCash = (id,params) => {
|
export const auditDistributionCash = (id,params) => {
|
||||||
return postRequest(`/distribution/cash/audit/${id}`, params)
|
return postRequest(`/distribution/cash/audit/${id}`, params)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
188
manager/src/components/distribution/distribution-selector.vue
Normal file
188
manager/src/components/distribution/distribution-selector.vue
Normal file
@@ -0,0 +1,188 @@
|
|||||||
|
<template>
|
||||||
|
<el-dialog
|
||||||
|
:model-value="modelValue"
|
||||||
|
title="选择分销员"
|
||||||
|
width="900px"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
destroy-on-close
|
||||||
|
append-to-body
|
||||||
|
@close="handleClose"
|
||||||
|
>
|
||||||
|
<el-form inline label-width="70px" class="search-form" @keyup.enter="handleSearch">
|
||||||
|
<el-form-item label="手机号">
|
||||||
|
<el-input v-model="searchForm.mobile" placeholder="请输入手机号" clearable style="width: 180px" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="昵称">
|
||||||
|
<el-input v-model="searchForm.nickname" placeholder="请输入昵称" clearable style="width: 180px" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" @click="handleSearch">搜索</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<el-table
|
||||||
|
v-loading="loading"
|
||||||
|
border
|
||||||
|
:data="data"
|
||||||
|
row-key="id"
|
||||||
|
highlight-current-row
|
||||||
|
max-height="420"
|
||||||
|
@row-click="handleRowClick"
|
||||||
|
@current-change="handleCurrentChange"
|
||||||
|
>
|
||||||
|
<el-table-column width="55" align="center">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-radio v-model="selectedId" :label="row.id" @click.stop @change="handleRadioChange(row)">
|
||||||
|
<span />
|
||||||
|
</el-radio>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="mobile" label="手机号" min-width="120" />
|
||||||
|
<el-table-column prop="nickname" label="昵称" min-width="120" show-overflow-tooltip />
|
||||||
|
<el-table-column prop="gradeName" label="等级" min-width="100" />
|
||||||
|
<el-table-column prop="joinTime" label="加入时间" min-width="160" />
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<div class="pagination-wrap">
|
||||||
|
<el-pagination
|
||||||
|
v-model:current-page="searchForm.pageNumber"
|
||||||
|
v-model:page-size="searchForm.pageSize"
|
||||||
|
:page-sizes="[10, 20, 50]"
|
||||||
|
:total="total"
|
||||||
|
layout="total, sizes, prev, pager, next"
|
||||||
|
size="small"
|
||||||
|
@current-change="getDataList"
|
||||||
|
@size-change="changePageSize"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<template #footer>
|
||||||
|
<el-button @click="handleClose">取消</el-button>
|
||||||
|
<el-button type="primary" :disabled="!selectedRow" @click="handleConfirm">确定</el-button>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { getDistributionMemberPage } from "@/api/distribution";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "distributionSelector",
|
||||||
|
props: {
|
||||||
|
modelValue: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
excludeId: {
|
||||||
|
type: String,
|
||||||
|
default: "",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
emits: ["update:modelValue", "confirm"],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
loading: false,
|
||||||
|
searchForm: {
|
||||||
|
pageNumber: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
mobile: "",
|
||||||
|
nickname: "",
|
||||||
|
},
|
||||||
|
data: [],
|
||||||
|
total: 0,
|
||||||
|
selectedId: "",
|
||||||
|
selectedRow: null,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
modelValue(val) {
|
||||||
|
if (val) {
|
||||||
|
this.resetState();
|
||||||
|
this.getDataList();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
resetState() {
|
||||||
|
this.searchForm = {
|
||||||
|
pageNumber: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
mobile: "",
|
||||||
|
nickname: "",
|
||||||
|
};
|
||||||
|
this.selectedId = "";
|
||||||
|
this.selectedRow = null;
|
||||||
|
},
|
||||||
|
buildParams() {
|
||||||
|
const params = {
|
||||||
|
pageNumber: this.searchForm.pageNumber,
|
||||||
|
pageSize: this.searchForm.pageSize,
|
||||||
|
distributionStatus: "PASS",
|
||||||
|
};
|
||||||
|
if (this.searchForm.mobile) params.mobile = this.searchForm.mobile;
|
||||||
|
if (this.searchForm.nickname) params.nickname = this.searchForm.nickname;
|
||||||
|
return params;
|
||||||
|
},
|
||||||
|
getDataList() {
|
||||||
|
this.loading = true;
|
||||||
|
getDistributionMemberPage(this.buildParams())
|
||||||
|
.then((res) => {
|
||||||
|
this.loading = false;
|
||||||
|
if (!res.success) return;
|
||||||
|
const result = res.result || {};
|
||||||
|
let records = result.records || [];
|
||||||
|
const beforeCount = records.length;
|
||||||
|
if (this.excludeId) {
|
||||||
|
records = records.filter((item) => item.id !== this.excludeId);
|
||||||
|
}
|
||||||
|
this.data = records;
|
||||||
|
const excludedCount = beforeCount - records.length;
|
||||||
|
this.total = Math.max(0, (result.total || 0) - excludedCount);
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
this.loading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
handleSearch() {
|
||||||
|
this.searchForm.pageNumber = 1;
|
||||||
|
this.getDataList();
|
||||||
|
},
|
||||||
|
changePageSize() {
|
||||||
|
this.searchForm.pageNumber = 1;
|
||||||
|
this.getDataList();
|
||||||
|
},
|
||||||
|
handleRowClick(row) {
|
||||||
|
if (!row) return;
|
||||||
|
this.selectedRow = row;
|
||||||
|
this.selectedId = row.id;
|
||||||
|
},
|
||||||
|
handleCurrentChange(row) {
|
||||||
|
if (!row) return;
|
||||||
|
this.selectedRow = row;
|
||||||
|
this.selectedId = row.id;
|
||||||
|
},
|
||||||
|
handleRadioChange(row) {
|
||||||
|
this.selectedRow = row;
|
||||||
|
},
|
||||||
|
handleClose() {
|
||||||
|
this.$emit("update:modelValue", false);
|
||||||
|
},
|
||||||
|
handleConfirm() {
|
||||||
|
if (!this.selectedRow) {
|
||||||
|
this.$Message.warning("请选择分销员");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.$emit("confirm", this.selectedRow);
|
||||||
|
this.handleClose();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.pagination-wrap {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
margin-top: 12px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -7,7 +7,6 @@ let util = {};
|
|||||||
/** 已弃用、前端未实现或已移除的菜单 frontRoute,注册路由时跳过 */
|
/** 已弃用、前端未实现或已移除的菜单 frontRoute,注册路由时跳过 */
|
||||||
const DEPRECATED_FRONT_ROUTES = new Set([
|
const DEPRECATED_FRONT_ROUTES = new Set([
|
||||||
"goods/goods-manage/spec",
|
"goods/goods-manage/spec",
|
||||||
"distribution/distrbutionGrade",
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
function isDeprecatedMenu(menu) {
|
function isDeprecatedMenu(menu) {
|
||||||
|
|||||||
@@ -455,6 +455,20 @@ export const otherRouter = {
|
|||||||
meta: { title: "商品销售汇总报表" },
|
meta: { title: "商品销售汇总报表" },
|
||||||
component: () => import("@/views/statistics/goods-sales-summary.vue")
|
component: () => import("@/views/statistics/goods-sales-summary.vue")
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: "distributionImportRecord",
|
||||||
|
title: "分销员导入记录",
|
||||||
|
name: "distributionImportRecord",
|
||||||
|
meta: { title: "分销员导入记录" },
|
||||||
|
component: () => import("@/views/distribution/distributionImportRecord.vue")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "distributionGradeHistory",
|
||||||
|
title: "等级变更记录",
|
||||||
|
name: "distributionGradeHistory",
|
||||||
|
meta: { title: "等级变更记录" },
|
||||||
|
component: () => import("@/views/distribution/distributionGradeHistory.vue")
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: "procurement/purchase-order/detail",
|
path: "procurement/purchase-order/detail",
|
||||||
title: "采购单详情",
|
title: "采购单详情",
|
||||||
|
|||||||
@@ -47,6 +47,20 @@
|
|||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 全局表格表头 */
|
||||||
|
.el-table {
|
||||||
|
.el-table__header th.el-table__cell {
|
||||||
|
background: var(--el-color-primary-light-9, #ecf5ff);
|
||||||
|
color: #303133;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-table__header th.el-table__cell .cell {
|
||||||
|
color: #303133;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* 列表操作列统一样式 */
|
/* 列表操作列统一样式 */
|
||||||
.link-text {
|
.link-text {
|
||||||
color: #409eff;
|
color: #409eff;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// 分销商状态列表
|
// 分销员状态列表
|
||||||
export const distributionStatusList= [
|
export const distributionStatusList= [
|
||||||
{
|
{
|
||||||
value:'APPLY',
|
value:'APPLY',
|
||||||
@@ -32,6 +32,43 @@ export const cashStatusList = [
|
|||||||
label:'通过'
|
label:'通过'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
// 分销客户关系有效状态筛选
|
||||||
|
export const relationEffectiveList = [
|
||||||
|
{ value: true, label: '有效' },
|
||||||
|
{ value: false, label: '失效' },
|
||||||
|
]
|
||||||
|
export const hasDeadlineList = [
|
||||||
|
{ value: true, label: '是' },
|
||||||
|
{ value: false, label: '否' },
|
||||||
|
]
|
||||||
|
// 分销客户关系状态列表
|
||||||
|
export const relationStatusList = [
|
||||||
|
{ value: 'PENDING', label: '待支付转永久' },
|
||||||
|
{ value: 'TEMPORARY', label: '短期有效' },
|
||||||
|
{ value: 'PERMANENT', label: '永久有效' },
|
||||||
|
{ value: 'EXPIRED', label: '已到期' },
|
||||||
|
{ value: 'UNBOUND', label: '已解绑' },
|
||||||
|
{ value: 'SELF_BIND', label: '自绑定' },
|
||||||
|
]
|
||||||
|
export const customerRelationChangeTypeList = [
|
||||||
|
{ value: 'CREATE', label: '绑定' },
|
||||||
|
{ value: 'EXPIRE', label: '过期' },
|
||||||
|
{ value: 'ADMIN_REBIND', label: '换绑' },
|
||||||
|
{ value: 'ADMIN_UNBIND', label: '解绑' },
|
||||||
|
]
|
||||||
|
export const bindModeList = [
|
||||||
|
{ value: 'PERMANENT', label: '永久锁客' },
|
||||||
|
{ value: 'SHORT_TERM', label: '短期锁客' },
|
||||||
|
]
|
||||||
|
export const permanentBindModeList = [
|
||||||
|
{ value: 'SCAN_IMMEDIATE', label: '扫码立即永久' },
|
||||||
|
{ value: 'PAID_WITHIN_DAYS', label: '规定天数内支付转永久' },
|
||||||
|
]
|
||||||
|
export const bindSourceList = [
|
||||||
|
{ value: 'SCAN', label: '扫码绑定' },
|
||||||
|
{ value: 'ORDER', label: '订单触发' },
|
||||||
|
{ value: 'ADMIN', label: '后台操作' },
|
||||||
|
]
|
||||||
// 分销订单状态列表
|
// 分销订单状态列表
|
||||||
export const orderStatusList = [
|
export const orderStatusList = [
|
||||||
{
|
{
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,156 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="search">
|
|
||||||
<el-card>
|
|
||||||
<el-form
|
|
||||||
ref="searchForm"
|
|
||||||
:model="searchForm"
|
|
||||||
inline
|
|
||||||
label-width="70px"
|
|
||||||
class="search-form"
|
|
||||||
@keyup.enter="handleSearch"
|
|
||||||
>
|
|
||||||
<el-form-item label="会员名称" prop="memberName">
|
|
||||||
<el-input
|
|
||||||
v-model="searchForm.memberName"
|
|
||||||
placeholder="请输入会员名称"
|
|
||||||
clearable
|
|
||||||
style="width: 240px"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item>
|
|
||||||
<el-button type="primary" class="search-btn" @click="handleSearch">搜索</el-button>
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
|
||||||
</el-card>
|
|
||||||
|
|
||||||
<el-card>
|
|
||||||
<el-table
|
|
||||||
ref="table"
|
|
||||||
v-loading="loading"
|
|
||||||
border
|
|
||||||
:data="data"
|
|
||||||
class="mt_10"
|
|
||||||
style="width: 100%"
|
|
||||||
>
|
|
||||||
<el-table-column prop="memberName" label="会员名称" min-width="100" show-overflow-tooltip />
|
|
||||||
<el-table-column prop="name" label="姓名" min-width="100" />
|
|
||||||
<el-table-column prop="idNumber" label="身份证号" min-width="120" />
|
|
||||||
<el-table-column prop="settlementBankAccountName" label="结算银行开户行名称" min-width="120" />
|
|
||||||
<el-table-column prop="settlementBankAccountNum" label="结算银行开户账号" min-width="120" />
|
|
||||||
<el-table-column prop="settlementBankBranchName" label="结算银行开户支行名称" min-width="120" />
|
|
||||||
<el-table-column prop="createTime" label="提交时间" min-width="100" />
|
|
||||||
<el-table-column label="操作" width="200" align="center" fixed="right">
|
|
||||||
<template #default="{ row }">
|
|
||||||
<div v-if="row" class="ops">
|
|
||||||
<a class="link-text" @click="audit(row, 'PASS')">通过</a>
|
|
||||||
<span class="op-split">|</span>
|
|
||||||
<a class="link-text" @click="audit(row, 'REFUSE')">拒绝</a>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
|
|
||||||
<div class="mt_10" style="display: flex; justify-content: flex-end">
|
|
||||||
<el-pagination
|
|
||||||
v-model:current-page="searchForm.pageNumber"
|
|
||||||
v-model:page-size="searchForm.pageSize"
|
|
||||||
:page-sizes="[20, 50, 100]"
|
|
||||||
:total="total"
|
|
||||||
layout="total, sizes, prev, pager, next, jumper"
|
|
||||||
size="small"
|
|
||||||
@current-change="changePage"
|
|
||||||
@size-change="changePageSize"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</el-card>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import { auditDistribution, getDistributionListData } from "@/api/distribution";
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: "distributionApply",
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
loading: true,
|
|
||||||
searchForm: {
|
|
||||||
pageNumber: 1,
|
|
||||||
pageSize: 20,
|
|
||||||
sort: "createTime",
|
|
||||||
order: "desc",
|
|
||||||
startDate: "",
|
|
||||||
endDate: "",
|
|
||||||
},
|
|
||||||
data: [],
|
|
||||||
total: 0,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
init() {
|
|
||||||
this.getDataList();
|
|
||||||
},
|
|
||||||
changePage() {
|
|
||||||
this.getDataList();
|
|
||||||
},
|
|
||||||
changePageSize() {
|
|
||||||
this.searchForm.pageNumber = 1;
|
|
||||||
this.getDataList();
|
|
||||||
},
|
|
||||||
handleSearch() {
|
|
||||||
this.searchForm.pageNumber = 1;
|
|
||||||
this.searchForm.pageSize = 20;
|
|
||||||
this.getDataList();
|
|
||||||
},
|
|
||||||
getDataList() {
|
|
||||||
this.loading = true;
|
|
||||||
this.searchForm.distributionStatus = "APPLY";
|
|
||||||
getDistributionListData(this.searchForm).then((res) => {
|
|
||||||
this.loading = false;
|
|
||||||
if (res.success) {
|
|
||||||
this.data = res.result.records;
|
|
||||||
this.total = res.result.total;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
audit(v, status) {
|
|
||||||
let test = "拒绝";
|
|
||||||
if (status == "PASS") {
|
|
||||||
test = "通过";
|
|
||||||
}
|
|
||||||
let params = {
|
|
||||||
status: status,
|
|
||||||
};
|
|
||||||
this.$Modal.confirm({
|
|
||||||
title: "确认" + test,
|
|
||||||
content: "您确认要" + test + " " + v.memberName + " ?",
|
|
||||||
loading: true,
|
|
||||||
onOk: () => {
|
|
||||||
auditDistribution(v.id, params).then((res) => {
|
|
||||||
this.$Modal.remove();
|
|
||||||
if (res.success) {
|
|
||||||
this.$Message.success("操作成功");
|
|
||||||
this.getDataList();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this.init();
|
|
||||||
},
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.ops a {
|
|
||||||
color: #2d8cf0;
|
|
||||||
cursor: pointer;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
.ops span {
|
|
||||||
display: inline-block;
|
|
||||||
margin: 0 8px;
|
|
||||||
color: #dcdee2;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
648
manager/src/views/distribution/distributionCustomerRelation.vue
Normal file
648
manager/src/views/distribution/distributionCustomerRelation.vue
Normal file
@@ -0,0 +1,648 @@
|
|||||||
|
<template>
|
||||||
|
<div class="search">
|
||||||
|
<el-card>
|
||||||
|
<el-form
|
||||||
|
ref="searchForm"
|
||||||
|
:model="searchForm"
|
||||||
|
inline
|
||||||
|
label-width="110px"
|
||||||
|
class="search-form"
|
||||||
|
@keyup.enter="handleSearch"
|
||||||
|
>
|
||||||
|
<el-form-item label="客户手机号" prop="memberMobile">
|
||||||
|
<el-input v-model="searchForm.memberMobile" placeholder="请输入客户手机号" clearable style="width: 200px" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="分销员手机号" prop="distributionMobile">
|
||||||
|
<el-input v-model="searchForm.distributionMobile" placeholder="请输入分销员手机号" clearable style="width: 200px" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="绑定时间">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="bindTimeRange"
|
||||||
|
type="daterange"
|
||||||
|
value-format="YYYY-MM-DD"
|
||||||
|
start-placeholder="开始日期"
|
||||||
|
end-placeholder="结束日期"
|
||||||
|
placeholder="选择时间"
|
||||||
|
style="width: 240px"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="当前关系">
|
||||||
|
<el-select v-model="searchForm.relationEffective" clearable placeholder="全部" style="width: 200px">
|
||||||
|
<el-option
|
||||||
|
v-for="item in relationEffectiveList"
|
||||||
|
:key="item.value"
|
||||||
|
:value="item.value"
|
||||||
|
:label="item.label"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="是否有截止时间">
|
||||||
|
<el-select v-model="searchForm.hasDeadline" clearable placeholder="全部" style="width: 200px">
|
||||||
|
<el-option
|
||||||
|
v-for="item in hasDeadlineList"
|
||||||
|
:key="item.value"
|
||||||
|
:value="item.value"
|
||||||
|
:label="item.label"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" class="search-btn" @click="handleSearch">搜索</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
<el-card>
|
||||||
|
<el-table v-loading="loading" border :data="data" class="mt_10" style="width: 100%">
|
||||||
|
<el-table-column prop="memberId" label="客户ID" min-width="200" class-name="id-column" show-overflow-tooltip />
|
||||||
|
<el-table-column prop="memberNickname" label="客户昵称" min-width="120" show-overflow-tooltip />
|
||||||
|
<el-table-column prop="memberMobile" label="客户手机号" min-width="120" />
|
||||||
|
<el-table-column prop="distributionNickname" label="分销员昵称" min-width="120" show-overflow-tooltip />
|
||||||
|
<el-table-column prop="distributionMobile" label="分销员手机号" min-width="120" />
|
||||||
|
<el-table-column label="当前关系" width="120">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<div v-if="row" class="relation-status-cell">
|
||||||
|
<span :class="isRelationEffective(row) ? 'relation-effective' : 'relation-invalid'">
|
||||||
|
{{ isRelationEffective(row) ? "有效" : "失效" }}
|
||||||
|
</span>
|
||||||
|
<el-tooltip v-if="!isRelationEffective(row)" placement="top">
|
||||||
|
<template #content>
|
||||||
|
<div>失效时间:{{ formatInvalidTime(row) }}</div>
|
||||||
|
<div>失效原因:{{ row.invalidReason || "-" }}</div>
|
||||||
|
</template>
|
||||||
|
<el-icon class="relation-tip-icon"><QuestionFilled /></el-icon>
|
||||||
|
</el-tooltip>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="bindTime" label="绑定时间" min-width="160" />
|
||||||
|
<el-table-column label="是否有截止日期" width="150" align="center">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<span v-if="row">{{ row.hasDeadline ? "是" : "否" }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="有效截止日期" min-width="160">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<span v-if="row">{{ row.hasDeadline && row.expireAt ? row.expireAt : "-" }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="操作" width="260" align="center" fixed="right">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<div v-if="row" class="ops">
|
||||||
|
<a class="link-text" @click="openDetail(row)">详情</a>
|
||||||
|
<span class="op-split">|</span>
|
||||||
|
<a class="link-text" @click="openLogs(row)">变更记录</a>
|
||||||
|
<span class="op-split">|</span>
|
||||||
|
<a class="link-text" @click="openRebind(row)">换绑</a>
|
||||||
|
<span class="op-split">|</span>
|
||||||
|
<a class="link-text" @click="openUnbind(row)">解绑</a>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<div class="mt_10" style="display: flex; justify-content: flex-end">
|
||||||
|
<el-pagination
|
||||||
|
v-model:current-page="searchForm.pageNumber"
|
||||||
|
v-model:page-size="searchForm.pageSize"
|
||||||
|
:page-sizes="[20, 50, 100]"
|
||||||
|
:total="total"
|
||||||
|
layout="total, sizes, prev, pager, next, jumper"
|
||||||
|
size="small"
|
||||||
|
@current-change="changePage"
|
||||||
|
@size-change="changePageSize"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
<el-drawer
|
||||||
|
v-model="detailVisible"
|
||||||
|
title="客户关系详情"
|
||||||
|
size="640px"
|
||||||
|
direction="rtl"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
append-to-body
|
||||||
|
destroy-on-close
|
||||||
|
>
|
||||||
|
<div v-loading="detailLoading" class="detail-body">
|
||||||
|
<template v-if="detail">
|
||||||
|
<div class="detail-meta">
|
||||||
|
<div class="detail-meta-row">
|
||||||
|
<span class="detail-meta-label">关系ID</span>
|
||||||
|
<span class="detail-meta-value">{{ detail.id || "-" }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="detail-meta-row">
|
||||||
|
<span class="detail-meta-label">客户ID</span>
|
||||||
|
<span class="detail-meta-value">{{ detail.memberId || "-" }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="detail-meta-row">
|
||||||
|
<span class="detail-meta-label">客户手机号</span>
|
||||||
|
<span class="detail-meta-value">{{ detail.memberMobile || "-" }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="detail-meta-row">
|
||||||
|
<span class="detail-meta-label">客户昵称</span>
|
||||||
|
<span class="detail-meta-value">{{ detail.memberNickname || "-" }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="detail-meta-row">
|
||||||
|
<span class="detail-meta-label">分销员ID</span>
|
||||||
|
<span class="detail-meta-value">{{ detail.distributionId || "-" }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="detail-meta-row">
|
||||||
|
<span class="detail-meta-label">分销员手机号</span>
|
||||||
|
<span class="detail-meta-value">{{ detail.distributionMobile || "-" }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="detail-meta-row">
|
||||||
|
<span class="detail-meta-label">分销员昵称</span>
|
||||||
|
<span class="detail-meta-value">{{ detail.distributionNickname || "-" }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="detail-meta-row">
|
||||||
|
<span class="detail-meta-label">绑定时间</span>
|
||||||
|
<span class="detail-meta-value">{{ detail.bindTime || "-" }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="detail-meta-row">
|
||||||
|
<span class="detail-meta-label">当前关系</span>
|
||||||
|
<span
|
||||||
|
class="detail-meta-value"
|
||||||
|
:class="isRelationEffective(detail) ? 'relation-effective' : 'relation-invalid'"
|
||||||
|
>
|
||||||
|
{{ isRelationEffective(detail) ? "有效" : "失效" }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="detail-meta-row">
|
||||||
|
<span class="detail-meta-label">失效时间</span>
|
||||||
|
<span class="detail-meta-value">{{ formatDetailInvalidTime(detail) }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="detail-meta-row">
|
||||||
|
<span class="detail-meta-label">失效原因</span>
|
||||||
|
<span class="detail-meta-value">{{ formatDetailInvalidReason(detail) }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="detail-meta-row">
|
||||||
|
<span class="detail-meta-label">是否有截止日期</span>
|
||||||
|
<span class="detail-meta-value">{{ detail.hasDeadline ? "是" : "否" }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="detail-meta-row">
|
||||||
|
<span class="detail-meta-label">有效截止日期</span>
|
||||||
|
<span class="detail-meta-value">{{ detail.hasDeadline && detail.expireAt ? detail.expireAt : "-" }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</el-drawer>
|
||||||
|
|
||||||
|
<el-drawer
|
||||||
|
v-model="logVisible"
|
||||||
|
title="变更记录"
|
||||||
|
size="1100px"
|
||||||
|
direction="rtl"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
append-to-body
|
||||||
|
destroy-on-close
|
||||||
|
class="relation-log-drawer"
|
||||||
|
>
|
||||||
|
<div v-loading="logLoading" class="log-body">
|
||||||
|
<el-table
|
||||||
|
v-if="logList.length || logLoading"
|
||||||
|
border
|
||||||
|
:data="logList"
|
||||||
|
style="width: 100%"
|
||||||
|
empty-text="暂无变更记录"
|
||||||
|
>
|
||||||
|
<el-table-column label="客户名称" min-width="120" show-overflow-tooltip>
|
||||||
|
<template #default="{ row }">
|
||||||
|
{{ row.memberNickname || logTarget?.memberNickname || "-" }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="客户手机号" min-width="120">
|
||||||
|
<template #default="{ row }">
|
||||||
|
{{ row.memberMobile || logTarget?.memberMobile || "-" }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="变更前分销员" min-width="140">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<div class="log-distributor-cell">
|
||||||
|
{{ formatDistributorCell(row.beforeDistributionNickname, row.beforeDistributionMobile, row.beforeDistributionId) }}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="变更后分销员" min-width="140">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<div class="log-distributor-cell">
|
||||||
|
{{ formatDistributorCell(row.afterDistributionNickname, row.afterDistributionMobile, row.afterDistributionId) }}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="createTime" label="变更时间" min-width="160" />
|
||||||
|
<el-table-column prop="changeTypeLabel" label="变更类型" width="90" align="center" />
|
||||||
|
<el-table-column label="原因" min-width="160">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<div class="log-reason-cell">{{ row.reason || "-" }}</div>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
<el-empty v-else-if="!logLoading" description="暂无变更记录" />
|
||||||
|
</div>
|
||||||
|
</el-drawer>
|
||||||
|
|
||||||
|
<el-dialog
|
||||||
|
v-model="rebindVisible"
|
||||||
|
title="换绑客户关系"
|
||||||
|
width="520px"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
destroy-on-close
|
||||||
|
>
|
||||||
|
<el-form ref="rebindForm" :model="rebindForm" :rules="rebindRules" label-width="110px">
|
||||||
|
<el-form-item label="客户">
|
||||||
|
<span>{{ rebindForm.memberNickname || rebindForm.memberMobile || rebindForm.memberId }}</span>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="新分销员" prop="distributionId">
|
||||||
|
<div class="distributor-picker">
|
||||||
|
<el-input
|
||||||
|
:model-value="selectedDistributorLabel"
|
||||||
|
placeholder="请选择分销员"
|
||||||
|
readonly
|
||||||
|
/>
|
||||||
|
<el-button type="primary" plain @click="distributorSelectorVisible = true">选择分销员</el-button>
|
||||||
|
<el-button v-if="rebindForm.distributionId" link type="primary" @click="clearSelectedDistributor">清除</el-button>
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="换绑原因" prop="reason">
|
||||||
|
<el-input v-model="rebindForm.reason" type="textarea" :rows="3" placeholder="请输入换绑原因" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<el-button @click="rebindVisible = false">取消</el-button>
|
||||||
|
<el-button type="primary" :loading="rebindLoading" @click="submitRebind">确定</el-button>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
|
<distribution-selector
|
||||||
|
v-model="distributorSelectorVisible"
|
||||||
|
:exclude-id="rebindForm.currentDistributionId"
|
||||||
|
@confirm="onDistributorSelected"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<el-dialog
|
||||||
|
v-model="unbindVisible"
|
||||||
|
title="解绑客户关系"
|
||||||
|
width="520px"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
destroy-on-close
|
||||||
|
>
|
||||||
|
<el-form ref="unbindForm" :model="unbindForm" label-width="110px">
|
||||||
|
<el-form-item label="客户">
|
||||||
|
<span>{{ unbindForm.memberNickname || unbindForm.memberMobile || unbindForm.memberId }}</span>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="解绑原因">
|
||||||
|
<el-input v-model="unbindForm.reason" type="textarea" :rows="3" placeholder="请输入解绑原因" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<el-button @click="unbindVisible = false">取消</el-button>
|
||||||
|
<el-button type="primary" :loading="unbindLoading" @click="submitUnbind">确定</el-button>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import {
|
||||||
|
getCustomerRelationDetail,
|
||||||
|
getCustomerRelationLogs,
|
||||||
|
getCustomerRelations,
|
||||||
|
rebindCustomerRelation,
|
||||||
|
unbindCustomerRelation,
|
||||||
|
} from "@/api/distribution";
|
||||||
|
import { relationEffectiveList, hasDeadlineList } from "./dataJson.js";
|
||||||
|
import DistributionSelector from "@/components/distribution/distribution-selector.vue";
|
||||||
|
import { QuestionFilled } from "@element-plus/icons-vue";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "distributionCustomerRelation",
|
||||||
|
components: { QuestionFilled, DistributionSelector },
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
relationEffectiveList,
|
||||||
|
hasDeadlineList,
|
||||||
|
bindTimeRange: [],
|
||||||
|
loading: true,
|
||||||
|
searchForm: {
|
||||||
|
pageNumber: 1,
|
||||||
|
pageSize: 20,
|
||||||
|
memberMobile: "",
|
||||||
|
distributionMobile: "",
|
||||||
|
distributionId: "",
|
||||||
|
relationEffective: "",
|
||||||
|
hasDeadline: "",
|
||||||
|
},
|
||||||
|
data: [],
|
||||||
|
total: 0,
|
||||||
|
detailVisible: false,
|
||||||
|
detailLoading: false,
|
||||||
|
detail: null,
|
||||||
|
logVisible: false,
|
||||||
|
logLoading: false,
|
||||||
|
logList: [],
|
||||||
|
logTarget: null,
|
||||||
|
rebindVisible: false,
|
||||||
|
distributorSelectorVisible: false,
|
||||||
|
unbindVisible: false,
|
||||||
|
rebindLoading: false,
|
||||||
|
unbindLoading: false,
|
||||||
|
rebindForm: {
|
||||||
|
memberId: "",
|
||||||
|
memberNickname: "",
|
||||||
|
memberMobile: "",
|
||||||
|
currentDistributionId: "",
|
||||||
|
distributionId: "",
|
||||||
|
distributionNickname: "",
|
||||||
|
distributionMobile: "",
|
||||||
|
reason: "",
|
||||||
|
},
|
||||||
|
unbindForm: {
|
||||||
|
memberId: "",
|
||||||
|
memberNickname: "",
|
||||||
|
memberMobile: "",
|
||||||
|
reason: "",
|
||||||
|
},
|
||||||
|
rebindRules: {
|
||||||
|
distributionId: [{
|
||||||
|
required: true,
|
||||||
|
message: "请选择分销员",
|
||||||
|
trigger: "change",
|
||||||
|
}],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
selectedDistributorLabel() {
|
||||||
|
if (!this.rebindForm.distributionId) return "";
|
||||||
|
const name = this.rebindForm.distributionNickname || "-";
|
||||||
|
const mobile = this.rebindForm.distributionMobile || "-";
|
||||||
|
return `${name}(${mobile})`;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.applyRouteQuery();
|
||||||
|
this.getDataList();
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
$route() {
|
||||||
|
this.applyRouteQuery();
|
||||||
|
this.getDataList();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
applyRouteQuery() {
|
||||||
|
const query = this.$route.query || {};
|
||||||
|
this.searchForm.distributionId = query.distributionId ? String(query.distributionId) : "";
|
||||||
|
this.searchForm.distributionMobile = query.distributionMobile ? String(query.distributionMobile) : "";
|
||||||
|
},
|
||||||
|
formatInvalidTime(row) {
|
||||||
|
return row?.invalidTime || "-";
|
||||||
|
},
|
||||||
|
formatDetailInvalidTime(detail) {
|
||||||
|
if (!detail || this.isRelationEffective(detail)) return "-";
|
||||||
|
return detail.invalidTime || "-";
|
||||||
|
},
|
||||||
|
formatDetailInvalidReason(detail) {
|
||||||
|
if (!detail || this.isRelationEffective(detail)) return "-";
|
||||||
|
return detail.invalidReason || "客户关系失效";
|
||||||
|
},
|
||||||
|
isRelationEffective(row) {
|
||||||
|
if (!row) return false;
|
||||||
|
if (row.relationEffective === true) return true;
|
||||||
|
if (row.relationEffective === false) return false;
|
||||||
|
return !["EXPIRED", "UNBOUND"].includes(row.relationStatus);
|
||||||
|
},
|
||||||
|
changePage() {
|
||||||
|
this.getDataList();
|
||||||
|
},
|
||||||
|
changePageSize() {
|
||||||
|
this.searchForm.pageNumber = 1;
|
||||||
|
this.getDataList();
|
||||||
|
},
|
||||||
|
handleSearch() {
|
||||||
|
this.searchForm.pageNumber = 1;
|
||||||
|
this.getDataList();
|
||||||
|
},
|
||||||
|
getDataList() {
|
||||||
|
this.loading = true;
|
||||||
|
const params = { ...this.searchForm };
|
||||||
|
if (!params.memberMobile) delete params.memberMobile;
|
||||||
|
if (!params.distributionMobile) delete params.distributionMobile;
|
||||||
|
if (!params.distributionId) delete params.distributionId;
|
||||||
|
if (params.relationEffective === "" || params.relationEffective === null || params.relationEffective === undefined) {
|
||||||
|
delete params.relationEffective;
|
||||||
|
} else {
|
||||||
|
params.relationEffective = params.relationEffective === true || params.relationEffective === "true";
|
||||||
|
}
|
||||||
|
if (params.hasDeadline === "" || params.hasDeadline === null || params.hasDeadline === undefined) {
|
||||||
|
delete params.hasDeadline;
|
||||||
|
} else {
|
||||||
|
params.hasDeadline = params.hasDeadline === true || params.hasDeadline === "true";
|
||||||
|
}
|
||||||
|
if (this.bindTimeRange && this.bindTimeRange[0] && this.bindTimeRange[1]) {
|
||||||
|
const startTime = new Date(this.bindTimeRange[0]).getTime();
|
||||||
|
const endTime = new Date(this.bindTimeRange[1]).getTime();
|
||||||
|
params.bindTimeStart = this.$filters.unixToDate(startTime / 1000);
|
||||||
|
params.bindTimeEnd = this.$filters.unixToDate(endTime / 1000);
|
||||||
|
}
|
||||||
|
getCustomerRelations(params).then((res) => {
|
||||||
|
this.loading = false;
|
||||||
|
if (res.success) {
|
||||||
|
this.data = res.result.records;
|
||||||
|
this.total = res.result.total;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
openDetail(row) {
|
||||||
|
this.detailVisible = true;
|
||||||
|
this.detailLoading = true;
|
||||||
|
this.detail = null;
|
||||||
|
getCustomerRelationDetail(row.id).then((res) => {
|
||||||
|
this.detailLoading = false;
|
||||||
|
if (res.success) {
|
||||||
|
this.detail = res.result;
|
||||||
|
}
|
||||||
|
}).catch(() => {
|
||||||
|
this.detailLoading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
openLogs(row) {
|
||||||
|
this.logVisible = true;
|
||||||
|
this.logTarget = row;
|
||||||
|
this.logLoading = true;
|
||||||
|
this.logList = [];
|
||||||
|
getCustomerRelationLogs(row.id).then((res) => {
|
||||||
|
this.logLoading = false;
|
||||||
|
if (res.success) {
|
||||||
|
this.logList = res.result || [];
|
||||||
|
}
|
||||||
|
}).catch(() => {
|
||||||
|
this.logLoading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
formatDistributorCell(nickname, mobile, id) {
|
||||||
|
if ((!nickname || nickname === "-") && !id) return "-";
|
||||||
|
const name = nickname && nickname !== "-" ? nickname : id;
|
||||||
|
if (mobile) return `${name}\n${mobile}`;
|
||||||
|
return name;
|
||||||
|
},
|
||||||
|
openRebind(row) {
|
||||||
|
this.rebindForm = {
|
||||||
|
memberId: row.memberId,
|
||||||
|
memberNickname: row.memberNickname,
|
||||||
|
memberMobile: row.memberMobile,
|
||||||
|
currentDistributionId: row.distributionId || "",
|
||||||
|
distributionId: "",
|
||||||
|
distributionNickname: "",
|
||||||
|
distributionMobile: "",
|
||||||
|
reason: "",
|
||||||
|
};
|
||||||
|
this.rebindVisible = true;
|
||||||
|
},
|
||||||
|
onDistributorSelected(row) {
|
||||||
|
this.rebindForm.distributionId = row.id;
|
||||||
|
this.rebindForm.distributionNickname = row.nickname;
|
||||||
|
this.rebindForm.distributionMobile = row.mobile;
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.$refs.rebindForm?.validateField("distributionId");
|
||||||
|
});
|
||||||
|
},
|
||||||
|
clearSelectedDistributor() {
|
||||||
|
this.rebindForm.distributionId = "";
|
||||||
|
this.rebindForm.distributionNickname = "";
|
||||||
|
this.rebindForm.distributionMobile = "";
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.$refs.rebindForm?.validateField("distributionId");
|
||||||
|
});
|
||||||
|
},
|
||||||
|
openUnbind(row) {
|
||||||
|
this.unbindForm = {
|
||||||
|
memberId: row.memberId,
|
||||||
|
memberNickname: row.memberNickname,
|
||||||
|
memberMobile: row.memberMobile,
|
||||||
|
reason: "",
|
||||||
|
};
|
||||||
|
this.unbindVisible = true;
|
||||||
|
},
|
||||||
|
submitRebind() {
|
||||||
|
this.$refs.rebindForm.validate((valid) => {
|
||||||
|
if (!valid) return;
|
||||||
|
if (this.rebindForm.distributionId === this.rebindForm.currentDistributionId) {
|
||||||
|
this.$Message.warning("新分销员不能与当前分销员相同");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.rebindLoading = true;
|
||||||
|
rebindCustomerRelation({
|
||||||
|
memberId: this.rebindForm.memberId,
|
||||||
|
distributionId: this.rebindForm.distributionId,
|
||||||
|
reason: this.rebindForm.reason,
|
||||||
|
}).then((res) => {
|
||||||
|
this.rebindLoading = false;
|
||||||
|
if (res.success) {
|
||||||
|
this.$Message.success("操作成功");
|
||||||
|
this.rebindVisible = false;
|
||||||
|
this.getDataList();
|
||||||
|
}
|
||||||
|
}).catch(() => {
|
||||||
|
this.rebindLoading = false;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
submitUnbind() {
|
||||||
|
this.unbindLoading = true;
|
||||||
|
unbindCustomerRelation({
|
||||||
|
memberId: this.unbindForm.memberId,
|
||||||
|
reason: this.unbindForm.reason,
|
||||||
|
}).then((res) => {
|
||||||
|
this.unbindLoading = false;
|
||||||
|
if (res.success) {
|
||||||
|
this.$Message.success("操作成功");
|
||||||
|
this.unbindVisible = false;
|
||||||
|
this.getDataList();
|
||||||
|
}
|
||||||
|
}).catch(() => {
|
||||||
|
this.unbindLoading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.ops a {
|
||||||
|
color: #2d8cf0;
|
||||||
|
cursor: pointer;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ops .op-split {
|
||||||
|
display: inline-block;
|
||||||
|
margin: 0 6px;
|
||||||
|
color: #dcdee2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.distributor-picker {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.distributor-picker .el-input {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.relation-status-cell {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.relation-effective {
|
||||||
|
color: #19be6b;
|
||||||
|
}
|
||||||
|
|
||||||
|
.relation-invalid {
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.relation-tip-icon {
|
||||||
|
color: #909399;
|
||||||
|
font-size: 14px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-body {
|
||||||
|
padding: 0 8px 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-meta-row {
|
||||||
|
display: flex;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
line-height: 22px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-meta-label {
|
||||||
|
flex: 0 0 120px;
|
||||||
|
color: #909399;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-meta-value {
|
||||||
|
flex: 1;
|
||||||
|
color: #303133;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
|
||||||
|
.log-body {
|
||||||
|
padding: 0 8px 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.log-distributor-cell,
|
||||||
|
.log-reason-cell {
|
||||||
|
white-space: pre-wrap;
|
||||||
|
word-break: break-word;
|
||||||
|
line-height: 22px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -17,15 +17,58 @@
|
|||||||
style="width: 240px"
|
style="width: 240px"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<el-form-item label="店铺名称" prop="storeName">
|
||||||
|
<el-input
|
||||||
|
v-model="searchForm.storeName"
|
||||||
|
placeholder="请输入店铺名称"
|
||||||
|
clearable
|
||||||
|
style="width: 240px"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="平台类目" prop="categoryId">
|
||||||
|
<el-cascader
|
||||||
|
v-model="searchForm.categoryId"
|
||||||
|
:options="categoryList"
|
||||||
|
:props="categoryProps"
|
||||||
|
clearable
|
||||||
|
filterable
|
||||||
|
placeholder="请选择平台类目"
|
||||||
|
style="width: 240px"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="商品品牌" prop="brandId">
|
||||||
|
<el-select
|
||||||
|
v-model="searchForm.brandId"
|
||||||
|
clearable
|
||||||
|
filterable
|
||||||
|
placeholder="请选择商品品牌"
|
||||||
|
style="width: 240px"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="item in brandList"
|
||||||
|
:key="item.id"
|
||||||
|
:label="item.name"
|
||||||
|
:value="item.id"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-button type="primary" class="search-btn" @click="handleSearch">搜索</el-button>
|
<el-button type="primary" class="search-btn" @click="handleSearch">搜索</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</el-card>
|
</el-card>
|
||||||
|
|
||||||
<el-card>
|
<el-card class="list-card">
|
||||||
|
<el-tabs v-model="currentAuthStatus" class="auth-tabs" @tab-change="onAuthStatusChange">
|
||||||
|
<el-tab-pane label="全部" name="ALL" />
|
||||||
|
<el-tab-pane label="待审核" name="TOBEAUDITED" />
|
||||||
|
<el-tab-pane label="已审核" name="PASS" />
|
||||||
|
<el-tab-pane label="审核未通过" name="REFUSE" />
|
||||||
|
</el-tabs>
|
||||||
|
|
||||||
<div class="operation" style="margin: 10px 0">
|
<div class="operation" style="margin: 10px 0">
|
||||||
<el-button type="primary" @click="delAll">批量下架</el-button>
|
<el-button type="primary" @click="openBatchAudit" :disabled="!hasPendingAuditSelection">批量审核</el-button>
|
||||||
|
<el-button type="primary" style="margin-left: 10px" @click="delAll">批量下架</el-button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<el-table
|
<el-table
|
||||||
@@ -33,46 +76,41 @@
|
|||||||
v-loading="loading"
|
v-loading="loading"
|
||||||
border
|
border
|
||||||
:data="data"
|
:data="data"
|
||||||
class="mt_10"
|
class="mt_10 distribution-goods-table"
|
||||||
style="width: 100%"
|
style="width: 100%"
|
||||||
@selection-change="changeSelect"
|
@selection-change="changeSelect"
|
||||||
>
|
>
|
||||||
<el-table-column type="selection" width="55" align="center" fixed="left" />
|
<el-table-column type="selection" width="55" align="center" fixed="left" />
|
||||||
<el-table-column label="商品图片" width="120" align="center" fixed="left">
|
<el-table-column label="商品图片" width="80" align="center" fixed="left">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<img
|
<img
|
||||||
v-if="row"
|
v-if="row?.thumbnail"
|
||||||
:src="row.thumbnail || ''"
|
:src="row.thumbnail"
|
||||||
alt="商品图"
|
alt="商品图"
|
||||||
style="cursor: pointer; width: 80px; height: 60px; margin: 10px 0; object-fit: contain"
|
class="goods-thumbnail"
|
||||||
/>
|
/>
|
||||||
|
<span v-else class="goods-thumbnail-empty">无图</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="商品名称" min-width="220" show-overflow-tooltip>
|
<el-table-column label="商品名称" min-width="200" show-overflow-tooltip fixed="left">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<template v-if="row">
|
<span v-if="row">{{ row.goodsName }}</span>
|
||||||
<div class="div-zoom">
|
</template>
|
||||||
<a class="link-text" @click="linkTo(row.goodsId, row.skuId)">{{ row.goodsName }}</a>
|
</el-table-column>
|
||||||
</div>
|
<el-table-column prop="goodsId" label="商品编号" min-width="200" show-overflow-tooltip />
|
||||||
<el-popover trigger="hover" title="扫码在手机中查看" placement="top" width="180">
|
<el-table-column label="商品类型" min-width="110">
|
||||||
<template #reference>
|
<template #default="{ row }">
|
||||||
<img
|
<span v-if="row">{{ goodsTypeText(row.goodsType) }}</span>
|
||||||
src="../../assets/qrcode.svg"
|
</template>
|
||||||
class="hover-pointer"
|
</el-table-column>
|
||||||
width="20"
|
<el-table-column label="平台类目" min-width="120" show-overflow-tooltip>
|
||||||
height="20"
|
<template #default="{ row }">
|
||||||
alt="qrcode"
|
<span v-if="row">{{ row.platformCategoryName || "-" }}</span>
|
||||||
/>
|
</template>
|
||||||
</template>
|
</el-table-column>
|
||||||
<vue-qr
|
<el-table-column label="品牌" min-width="120" show-overflow-tooltip>
|
||||||
:text="wapLinkTo(row.goodsId, row.skuId)"
|
<template #default="{ row }">
|
||||||
:margin="0"
|
<span v-if="row">{{ row.brandName || "-" }}</span>
|
||||||
color-dark="#000"
|
|
||||||
color-light="#fff"
|
|
||||||
:size="150"
|
|
||||||
/>
|
|
||||||
</el-popover>
|
|
||||||
</template>
|
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="商品价格" min-width="110">
|
<el-table-column label="商品价格" min-width="110">
|
||||||
@@ -80,17 +118,26 @@
|
|||||||
<span v-if="row" :style="{ color: $mainColor }">{{ $filters.unitPrice(row.price, "¥") }}</span>
|
<span v-if="row" :style="{ color: $mainColor }">{{ $filters.unitPrice(row.price, "¥") }}</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="quantity" label="库存" min-width="80" />
|
|
||||||
<el-table-column prop="createTime" label="添加时间" min-width="160" />
|
|
||||||
<el-table-column prop="storeName" label="店铺名称" min-width="120" show-overflow-tooltip />
|
<el-table-column prop="storeName" label="店铺名称" min-width="120" show-overflow-tooltip />
|
||||||
<el-table-column label="佣金金额" min-width="110">
|
<el-table-column label="佣金金额" min-width="110">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<span v-if="row" :style="{ color: $mainColor }">{{ $filters.unitPrice(row.commission, "¥") }}</span>
|
<span v-if="row" :style="{ color: $mainColor }">{{ $filters.unitPrice(row.commission, "¥") }}</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="操作" min-width="100" align="center" fixed="right">
|
<el-table-column label="审核状态" min-width="110" align="center">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<a v-if="row" class="link-text" @click="remove(row)">下架</a>
|
<el-tag v-if="row" :type="authStatusTagType(row.authStatus)" size="small">
|
||||||
|
{{ authStatusText(row.authStatus) }}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="操作" min-width="120" align="center" fixed="right">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<template v-if="row">
|
||||||
|
<a v-if="row.authStatus === 'TOBEAUDITED'" class="link-text" @click.prevent="openAudit(row)">审核</a>
|
||||||
|
<span v-if="row.authStatus === 'TOBEAUDITED'" class="op-split">|</span>
|
||||||
|
<a class="link-text" @click.prevent="remove(row)">下架</a>
|
||||||
|
</template>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
@@ -108,34 +155,162 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</el-card>
|
</el-card>
|
||||||
|
|
||||||
|
<el-dialog
|
||||||
|
v-model="auditModalVisible"
|
||||||
|
:title="auditTargetIds.length > 1 ? '批量审核分销商品' : '审核分销商品'"
|
||||||
|
width="500px"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
destroy-on-close
|
||||||
|
>
|
||||||
|
<el-form :model="auditForm" label-width="90px">
|
||||||
|
<el-form-item label="审核结果">
|
||||||
|
<el-radio-group v-model="auditForm.authStatus">
|
||||||
|
<el-radio value="PASS">通过</el-radio>
|
||||||
|
<el-radio value="REFUSE">驳回</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item v-if="auditForm.authStatus === 'REFUSE'" label="审核备注">
|
||||||
|
<el-input
|
||||||
|
v-model="auditForm.authRemark"
|
||||||
|
type="textarea"
|
||||||
|
:rows="3"
|
||||||
|
maxlength="255"
|
||||||
|
show-word-limit
|
||||||
|
placeholder="请输入驳回原因"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<el-button @click="auditModalVisible = false">取消</el-button>
|
||||||
|
<el-button type="primary" :loading="auditLoading" @click="submitAudit">确定</el-button>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { delDistributionGoods, getDistributionGoods } from "@/api/distribution";
|
import { auditDistributionGoods, delDistributionGoods, getDistributionGoods } from "@/api/distribution";
|
||||||
import vueQr from "vue-qr";
|
import { getBrandListData, getCategoryTree } from "@/api/goods";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "distributionGoods",
|
name: "distributionGoods",
|
||||||
components: { vueQr },
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
loading: true,
|
loading: true,
|
||||||
|
currentAuthStatus: "ALL",
|
||||||
|
auditModalVisible: false,
|
||||||
|
auditLoading: false,
|
||||||
|
auditTargetIds: [],
|
||||||
|
auditForm: {
|
||||||
|
authStatus: "PASS",
|
||||||
|
authRemark: "",
|
||||||
|
},
|
||||||
searchForm: {
|
searchForm: {
|
||||||
pageNumber: 1,
|
pageNumber: 1,
|
||||||
pageSize: 20,
|
pageSize: 20,
|
||||||
sort: "createTime",
|
sort: "createTime",
|
||||||
order: "desc",
|
order: "desc",
|
||||||
|
goodsName: "",
|
||||||
|
storeName: "",
|
||||||
|
categoryId: "",
|
||||||
|
brandId: "",
|
||||||
|
authStatus: "",
|
||||||
},
|
},
|
||||||
|
categoryList: [],
|
||||||
|
categoryProps: {
|
||||||
|
value: "id",
|
||||||
|
label: "name",
|
||||||
|
children: "children",
|
||||||
|
checkStrictly: true,
|
||||||
|
emitPath: false,
|
||||||
|
},
|
||||||
|
brandList: [],
|
||||||
selectList: [],
|
selectList: [],
|
||||||
selectCount: 0,
|
selectCount: 0,
|
||||||
data: [],
|
data: [],
|
||||||
total: 0,
|
total: 0,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
hasPendingAuditSelection() {
|
||||||
|
return this.selectList.some((item) => item.authStatus === "TOBEAUDITED");
|
||||||
|
},
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
init() {
|
authStatusText(value) {
|
||||||
|
if (value === "TOBEAUDITED") return "待审核";
|
||||||
|
if (value === "REFUSE") return "审核未通过";
|
||||||
|
return "已审核";
|
||||||
|
},
|
||||||
|
authStatusTagType(value) {
|
||||||
|
if (value === "TOBEAUDITED") return "warning";
|
||||||
|
if (value === "REFUSE") return "danger";
|
||||||
|
return "success";
|
||||||
|
},
|
||||||
|
onAuthStatusChange(name) {
|
||||||
|
this.searchForm.authStatus = name === "ALL" ? "" : name;
|
||||||
|
this.searchForm.pageNumber = 1;
|
||||||
this.getDataList();
|
this.getDataList();
|
||||||
|
this.clearSelectAll();
|
||||||
|
},
|
||||||
|
openAudit(row) {
|
||||||
|
this.auditTargetIds = [row.id];
|
||||||
|
this.auditForm = { authStatus: "PASS", authRemark: "" };
|
||||||
|
this.auditModalVisible = true;
|
||||||
|
},
|
||||||
|
openBatchAudit() {
|
||||||
|
const pendingRows = this.selectList.filter((item) => item.authStatus === "TOBEAUDITED");
|
||||||
|
if (!pendingRows.length) {
|
||||||
|
this.$Message.warning("请选择待审核的分销商品");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.auditTargetIds = pendingRows.map((item) => item.id);
|
||||||
|
this.auditForm = { authStatus: "PASS", authRemark: "" };
|
||||||
|
this.auditModalVisible = true;
|
||||||
|
},
|
||||||
|
submitAudit() {
|
||||||
|
if (this.auditForm.authStatus === "REFUSE" && !this.auditForm.authRemark.trim()) {
|
||||||
|
this.$Message.warning("请填写驳回原因");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.auditLoading = true;
|
||||||
|
auditDistributionGoods({
|
||||||
|
ids: this.auditTargetIds,
|
||||||
|
authStatus: this.auditForm.authStatus,
|
||||||
|
authRemark: this.auditForm.authRemark.trim(),
|
||||||
|
})
|
||||||
|
.then((res) => {
|
||||||
|
if (res.success) {
|
||||||
|
this.$Message.success("审核成功");
|
||||||
|
this.auditModalVisible = false;
|
||||||
|
this.clearSelectAll();
|
||||||
|
this.getDataList();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.auditLoading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
goodsTypeText(value) {
|
||||||
|
if (value === "PHYSICAL_GOODS") return "实物商品";
|
||||||
|
if (value === "VIRTUAL_GOODS") return "虚拟商品";
|
||||||
|
if (value === "E_COUPON") return "电子卡券";
|
||||||
|
return "-";
|
||||||
|
},
|
||||||
|
init() {
|
||||||
|
this.loadFilterOptions();
|
||||||
|
this.getDataList();
|
||||||
|
},
|
||||||
|
loadFilterOptions() {
|
||||||
|
getCategoryTree().then((res) => {
|
||||||
|
if (res.success) {
|
||||||
|
this.categoryList = res.result || [];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
getBrandListData().then((res) => {
|
||||||
|
this.brandList = Array.isArray(res) ? res : res.result || [];
|
||||||
|
});
|
||||||
},
|
},
|
||||||
changePage(v) {
|
changePage(v) {
|
||||||
this.searchForm.pageNumber = v;
|
this.searchForm.pageNumber = v;
|
||||||
@@ -222,17 +397,39 @@ export default {
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
.div-zoom {
|
|
||||||
overflow: hidden;
|
.goods-thumbnail {
|
||||||
text-overflow: ellipsis;
|
width: 50px;
|
||||||
white-space: nowrap;
|
height: 50px;
|
||||||
margin-bottom: 4px;
|
object-fit: cover;
|
||||||
}
|
|
||||||
.hover-pointer {
|
|
||||||
cursor: pointer;
|
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.goods-thumbnail-empty {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 50px;
|
||||||
|
height: 50px;
|
||||||
|
background: #f5f7fa;
|
||||||
|
color: #909399;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
.mt_10 {
|
.mt_10 {
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.auth-tabs {
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-card {
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.op-split {
|
||||||
|
margin: 0 6px;
|
||||||
|
color: #dcdfe6;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
450
manager/src/views/distribution/distributionGrade.vue
Normal file
450
manager/src/views/distribution/distributionGrade.vue
Normal file
@@ -0,0 +1,450 @@
|
|||||||
|
<template>
|
||||||
|
<div class="search">
|
||||||
|
<el-card>
|
||||||
|
<div class="operation padding-row">
|
||||||
|
<el-button type="primary" :disabled="data.length >= 5" @click="addRow">添加分销等级</el-button>
|
||||||
|
<span v-if="data.length >= 5" class="limit-tip">最多添加5个等级</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-table v-loading="loading" border :data="data" class="mt_10 grade-table" style="width: 100%">
|
||||||
|
<el-table-column label="等级值" width="100" align="center">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<span>{{ row.sortOrder }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="等级名" min-width="140">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-input v-model="row.gradeName" maxlength="50" placeholder="请输入等级名" />
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="升级规则" min-width="300">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<div v-if="isBaseGrade(row)" class="no-condition-tip">初始等级,无需设置</div>
|
||||||
|
<div v-else class="grade-edit-lines">
|
||||||
|
<div class="grade-edit-line">
|
||||||
|
<el-checkbox v-model="row.enableSalesCondition" />
|
||||||
|
<span class="line-label">销售额达到</span>
|
||||||
|
<el-input-number
|
||||||
|
v-model="row.salesThreshold"
|
||||||
|
:min="0"
|
||||||
|
:precision="2"
|
||||||
|
:disabled="!row.enableSalesCondition"
|
||||||
|
controls-position="right"
|
||||||
|
class="inline-number"
|
||||||
|
/>
|
||||||
|
<span class="line-unit">元</span>
|
||||||
|
</div>
|
||||||
|
<div class="grade-edit-line">
|
||||||
|
<el-checkbox v-model="row.enableCommissionCondition" />
|
||||||
|
<span class="line-label">收益额</span>
|
||||||
|
<el-input-number
|
||||||
|
v-model="row.commissionThreshold"
|
||||||
|
:min="0"
|
||||||
|
:precision="2"
|
||||||
|
:disabled="!row.enableCommissionCondition"
|
||||||
|
controls-position="right"
|
||||||
|
class="inline-number"
|
||||||
|
/>
|
||||||
|
<span class="line-unit">元</span>
|
||||||
|
</div>
|
||||||
|
<div class="grade-edit-line">
|
||||||
|
<el-checkbox v-model="row.enableInviteCondition" />
|
||||||
|
<span class="line-label">邀请人数</span>
|
||||||
|
<el-input-number
|
||||||
|
v-model="row.inviteThreshold"
|
||||||
|
:min="0"
|
||||||
|
:precision="0"
|
||||||
|
:disabled="!row.enableInviteCondition"
|
||||||
|
controls-position="right"
|
||||||
|
class="inline-number"
|
||||||
|
/>
|
||||||
|
<span class="line-unit">人</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="佣金返利" min-width="220">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<div class="grade-edit-lines">
|
||||||
|
<div class="grade-edit-line">
|
||||||
|
<span class="line-label">佣金比例</span>
|
||||||
|
<el-input-number
|
||||||
|
v-model="row.goodsCommissionRate"
|
||||||
|
:min="0"
|
||||||
|
:max="100"
|
||||||
|
:precision="2"
|
||||||
|
controls-position="right"
|
||||||
|
class="inline-number"
|
||||||
|
/>
|
||||||
|
<span class="line-unit">%</span>
|
||||||
|
</div>
|
||||||
|
<div class="grade-edit-line">
|
||||||
|
<span class="line-label">邀请奖励</span>
|
||||||
|
<el-input-number
|
||||||
|
v-model="row.inviteCommissionRate"
|
||||||
|
:min="0"
|
||||||
|
:max="100"
|
||||||
|
:precision="2"
|
||||||
|
controls-position="right"
|
||||||
|
class="inline-number"
|
||||||
|
/>
|
||||||
|
<span class="line-unit">%</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="操作" width="80" align="center" fixed="right">
|
||||||
|
<template #default="{ row, $index }">
|
||||||
|
<span v-if="isBaseGrade(row)" class="disabled-text">-</span>
|
||||||
|
<a v-else class="link-text" @click.prevent="remove(row, $index)">删除</a>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<div class="grade-footer">
|
||||||
|
<el-button type="primary" :loading="saveLoading" @click="saveAll">保存</el-button>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import {
|
||||||
|
getDistributionGrades,
|
||||||
|
saveAllDistributionGrades,
|
||||||
|
deleteDistributionGrade,
|
||||||
|
} from "@/api/distribution";
|
||||||
|
|
||||||
|
const MAX_GRADE_COUNT = 5;
|
||||||
|
const GRADE_VALUE_OPTIONS = [1, 2, 3, 4, 5];
|
||||||
|
const BASE_GRADE_VALUE = 1;
|
||||||
|
const BASE_GRADE_NAME = "普通分销员";
|
||||||
|
|
||||||
|
const buildDefaultRow = (sortOrder = null) => {
|
||||||
|
const isBaseGrade = sortOrder === BASE_GRADE_VALUE;
|
||||||
|
return {
|
||||||
|
_key: `grade_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`,
|
||||||
|
id: "",
|
||||||
|
gradeName: isBaseGrade ? BASE_GRADE_NAME : "",
|
||||||
|
sortOrder,
|
||||||
|
salesThreshold: 0,
|
||||||
|
commissionThreshold: 0,
|
||||||
|
inviteThreshold: 0,
|
||||||
|
enableSalesCondition: false,
|
||||||
|
enableCommissionCondition: false,
|
||||||
|
enableInviteCondition: false,
|
||||||
|
goodsCommissionRate: 0,
|
||||||
|
inviteCommissionRate: 0,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "distributionGrade",
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
loading: true,
|
||||||
|
saveLoading: false,
|
||||||
|
data: [],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.getDataList();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
normalizeRow(row, index) {
|
||||||
|
const normalized = {
|
||||||
|
...buildDefaultRow(row.sortOrder),
|
||||||
|
...row,
|
||||||
|
enableSalesCondition: row.enableSalesCondition !== false,
|
||||||
|
enableCommissionCondition: row.enableCommissionCondition !== false,
|
||||||
|
enableInviteCondition: row.enableInviteCondition !== false,
|
||||||
|
_key: row._key || row.id || `grade_${index}_${Date.now()}`,
|
||||||
|
};
|
||||||
|
if (this.isBaseGrade(normalized)) {
|
||||||
|
normalized.enableSalesCondition = false;
|
||||||
|
normalized.enableCommissionCondition = false;
|
||||||
|
normalized.enableInviteCondition = false;
|
||||||
|
if (!normalized.gradeName || !normalized.gradeName.trim()) {
|
||||||
|
normalized.gradeName = BASE_GRADE_NAME;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return normalized;
|
||||||
|
},
|
||||||
|
isBaseGrade(row) {
|
||||||
|
return row && row.sortOrder === BASE_GRADE_VALUE;
|
||||||
|
},
|
||||||
|
getSortedRows() {
|
||||||
|
return this.data.slice().sort((a, b) => (a.sortOrder || 0) - (b.sortOrder || 0));
|
||||||
|
},
|
||||||
|
getGradeLabel(row) {
|
||||||
|
return row.gradeName && row.gradeName.trim() ? row.gradeName.trim() : `等级值${row.sortOrder}`;
|
||||||
|
},
|
||||||
|
getNextGradeValue() {
|
||||||
|
const used = new Set(this.data.map((item) => item.sortOrder));
|
||||||
|
return GRADE_VALUE_OPTIONS.find((value) => value !== BASE_GRADE_VALUE && !used.has(value)) || null;
|
||||||
|
},
|
||||||
|
getDataList() {
|
||||||
|
this.loading = true;
|
||||||
|
getDistributionGrades().then((res) => {
|
||||||
|
this.loading = false;
|
||||||
|
if (res.success) {
|
||||||
|
this.data = (res.result || [])
|
||||||
|
.slice()
|
||||||
|
.sort((a, b) => (a.sortOrder || 0) - (b.sortOrder || 0))
|
||||||
|
.map((item, index) => this.normalizeRow(item, index));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
addRow() {
|
||||||
|
if (this.data.length >= MAX_GRADE_COUNT) {
|
||||||
|
this.$Message.warning("最多添加5个分销等级");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const nextGradeValue = this.getNextGradeValue();
|
||||||
|
if (!nextGradeValue) {
|
||||||
|
this.$Message.warning("等级值1-5已全部使用");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.data.push(buildDefaultRow(nextGradeValue));
|
||||||
|
},
|
||||||
|
validateGradeProgression() {
|
||||||
|
const rows = this.getSortedRows();
|
||||||
|
for (let i = 1; i < rows.length; i++) {
|
||||||
|
const lower = rows[i - 1];
|
||||||
|
const higher = rows[i];
|
||||||
|
const lowerLabel = this.getGradeLabel(lower);
|
||||||
|
const higherLabel = this.getGradeLabel(higher);
|
||||||
|
if (lower.enableSalesCondition) {
|
||||||
|
if (!higher.enableSalesCondition) {
|
||||||
|
this.$Message.warning(`${higherLabel}必须启用${lowerLabel}已启用的销售额升级条件`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (Number(higher.salesThreshold || 0) < Number(lower.salesThreshold || 0)) {
|
||||||
|
this.$Message.warning(`${higherLabel}销售额阈值不能小于${lowerLabel}`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (lower.enableCommissionCondition) {
|
||||||
|
if (!higher.enableCommissionCondition) {
|
||||||
|
this.$Message.warning(`${higherLabel}必须启用${lowerLabel}已启用的收益额升级条件`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (Number(higher.commissionThreshold || 0) < Number(lower.commissionThreshold || 0)) {
|
||||||
|
this.$Message.warning(`${higherLabel}收益额阈值不能小于${lowerLabel}`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (lower.enableInviteCondition) {
|
||||||
|
if (!higher.enableInviteCondition) {
|
||||||
|
this.$Message.warning(`${higherLabel}必须启用${lowerLabel}已启用的邀请人数升级条件`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (Number(higher.inviteThreshold || 0) < Number(lower.inviteThreshold || 0)) {
|
||||||
|
this.$Message.warning(`${higherLabel}邀请人数阈值不能小于${lowerLabel}`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
validateRows() {
|
||||||
|
if (!this.data.length) {
|
||||||
|
this.$Message.warning("请至少添加一个分销等级");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const usedValues = [];
|
||||||
|
for (let i = 0; i < this.data.length; i++) {
|
||||||
|
const row = this.data[i];
|
||||||
|
if (!row.sortOrder) {
|
||||||
|
this.$Message.warning(`请填写第${i + 1}行等级值`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (usedValues.includes(row.sortOrder)) {
|
||||||
|
this.$Message.warning("等级值不能重复");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
usedValues.push(row.sortOrder);
|
||||||
|
if (!row.gradeName || !row.gradeName.trim()) {
|
||||||
|
this.$Message.warning(`请填写第${i + 1}行等级名`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (this.isBaseGrade(row)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!row.enableSalesCondition && !row.enableCommissionCondition && !row.enableInviteCondition) {
|
||||||
|
this.$Message.warning(`第${i + 1}行请至少启用一项升级规则`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const rowLabel = this.getGradeLabel(row);
|
||||||
|
if (row.enableSalesCondition && Number(row.salesThreshold || 0) <= 0) {
|
||||||
|
this.$Message.warning(`${rowLabel}销售额必须大于0`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (row.enableCommissionCondition && Number(row.commissionThreshold || 0) <= 0) {
|
||||||
|
this.$Message.warning(`${rowLabel}收益额必须大于0`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (row.enableInviteCondition && Number(row.inviteThreshold || 0) <= 0) {
|
||||||
|
this.$Message.warning(`${rowLabel}邀请人数必须大于0`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this.validateGradeProgression() && this.validateCrossRates();
|
||||||
|
},
|
||||||
|
validateCrossRates() {
|
||||||
|
let maxGoods = 0;
|
||||||
|
let maxInvite = 0;
|
||||||
|
for (const row of this.data) {
|
||||||
|
maxGoods = Math.max(maxGoods, Number(row.goodsCommissionRate || 0));
|
||||||
|
maxInvite = Math.max(maxInvite, Number(row.inviteCommissionRate || 0));
|
||||||
|
}
|
||||||
|
if (maxGoods + maxInvite > 100) {
|
||||||
|
this.$Message.warning(
|
||||||
|
`所有等级中最高佣金比例(${maxGoods}%)与最高邀请奖励(${maxInvite}%)之和不能超过100%`
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (const row of this.data) {
|
||||||
|
const goods = Number(row.goodsCommissionRate || 0);
|
||||||
|
const invite = Number(row.inviteCommissionRate || 0);
|
||||||
|
if (goods + invite > 100) {
|
||||||
|
this.$Message.warning(`${this.getGradeLabel(row)}的佣金比例与邀请奖励之和不能超过100%`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
buildPayload(row) {
|
||||||
|
const isBaseGrade = this.isBaseGrade(row);
|
||||||
|
return {
|
||||||
|
id: row.id || undefined,
|
||||||
|
gradeName: row.gradeName.trim(),
|
||||||
|
sortOrder: row.sortOrder,
|
||||||
|
salesThreshold: row.salesThreshold,
|
||||||
|
commissionThreshold: row.commissionThreshold,
|
||||||
|
inviteThreshold: row.inviteThreshold,
|
||||||
|
enableSalesCondition: isBaseGrade ? false : row.enableSalesCondition,
|
||||||
|
enableCommissionCondition: isBaseGrade ? false : row.enableCommissionCondition,
|
||||||
|
enableInviteCondition: isBaseGrade ? false : row.enableInviteCondition,
|
||||||
|
goodsCommissionRate: row.goodsCommissionRate,
|
||||||
|
inviteCommissionRate: row.inviteCommissionRate,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
async saveAll() {
|
||||||
|
if (!this.validateRows()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.saveLoading = true;
|
||||||
|
try {
|
||||||
|
const payload = this.getSortedRows().map((row) => this.buildPayload(row));
|
||||||
|
const res = await saveAllDistributionGrades(payload);
|
||||||
|
if (res.success) {
|
||||||
|
this.$Message.success("保存成功");
|
||||||
|
this.getDataList();
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// request layer handles error message
|
||||||
|
} finally {
|
||||||
|
this.saveLoading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
remove(row, index) {
|
||||||
|
if (this.isBaseGrade(row)) {
|
||||||
|
this.$Message.warning("默认分销等级不能删除");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const name = row.gradeName || `等级值${row.sortOrder || ""}`;
|
||||||
|
if (!row.id) {
|
||||||
|
this.$Modal.confirm({
|
||||||
|
title: "确认删除",
|
||||||
|
content: `您确认要删除 ${name} 吗?`,
|
||||||
|
onOk: () => {
|
||||||
|
this.data.splice(index, 1);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.$Modal.confirm({
|
||||||
|
title: "确认删除",
|
||||||
|
content: `您确认要删除 ${name} 吗?`,
|
||||||
|
loading: true,
|
||||||
|
onOk: () => {
|
||||||
|
deleteDistributionGrade(row.id).then((res) => {
|
||||||
|
this.$Modal.remove();
|
||||||
|
if (res.success) {
|
||||||
|
this.$Message.success("删除成功");
|
||||||
|
this.getDataList();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.grade-footer {
|
||||||
|
margin-top: 16px;
|
||||||
|
padding-top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.limit-tip {
|
||||||
|
margin-left: 12px;
|
||||||
|
color: #909399;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grade-table :deep(.el-table__cell) {
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grade-table :deep(.el-table__cell .cell) {
|
||||||
|
white-space: normal;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grade-edit-lines {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grade-edit-line {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.line-label {
|
||||||
|
min-width: 70px;
|
||||||
|
color: #606266;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.line-unit {
|
||||||
|
color: #606266;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inline-number {
|
||||||
|
width: 120px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-condition-tip {
|
||||||
|
color: #909399;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.disabled-text {
|
||||||
|
color: #c0c4cc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.link-text {
|
||||||
|
color: #2d8cf0;
|
||||||
|
cursor: pointer;
|
||||||
|
user-select: none;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
220
manager/src/views/distribution/distributionGradeHistory.vue
Normal file
220
manager/src/views/distribution/distributionGradeHistory.vue
Normal file
@@ -0,0 +1,220 @@
|
|||||||
|
<template>
|
||||||
|
<div class="search">
|
||||||
|
<el-card>
|
||||||
|
<el-form
|
||||||
|
ref="searchForm"
|
||||||
|
:model="searchForm"
|
||||||
|
inline
|
||||||
|
label-width="110px"
|
||||||
|
class="search-form"
|
||||||
|
@keyup.enter="handleSearch"
|
||||||
|
>
|
||||||
|
<el-form-item label="分销员昵称" prop="memberName">
|
||||||
|
<el-input
|
||||||
|
v-model="searchForm.memberName"
|
||||||
|
placeholder="请输入分销员昵称"
|
||||||
|
clearable
|
||||||
|
style="width: 200px"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="分销员手机号" prop="distributionMobile">
|
||||||
|
<el-input
|
||||||
|
v-model="searchForm.distributionMobile"
|
||||||
|
placeholder="请输入分销员手机号"
|
||||||
|
clearable
|
||||||
|
style="width: 200px"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="变更后等级" prop="gradeId">
|
||||||
|
<el-select v-model="searchForm.gradeId" clearable placeholder="全部" style="width: 200px">
|
||||||
|
<el-option
|
||||||
|
v-for="item in gradeOptions"
|
||||||
|
:key="item.value"
|
||||||
|
:value="item.value"
|
||||||
|
:label="item.label"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="升级时间">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="upgradeTimeRange"
|
||||||
|
type="daterange"
|
||||||
|
value-format="YYYY-MM-DD"
|
||||||
|
start-placeholder="开始日期"
|
||||||
|
end-placeholder="结束日期"
|
||||||
|
placeholder="选择时间"
|
||||||
|
style="width: 240px"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" class="search-btn" @click="handleSearch">搜索</el-button>
|
||||||
|
<el-button @click="handleReset">重置</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
<el-card>
|
||||||
|
<el-table v-loading="loading" border :data="data" class="mt_10" style="width: 100%">
|
||||||
|
<el-table-column prop="distributionId" label="分销员ID" min-width="200" class-name="id-column" show-overflow-tooltip />
|
||||||
|
<el-table-column prop="memberName" label="分销员昵称" min-width="120" show-overflow-tooltip />
|
||||||
|
<el-table-column prop="memberMobile" label="分销员手机号" min-width="130" />
|
||||||
|
<el-table-column label="变更前等级" min-width="140" show-overflow-tooltip>
|
||||||
|
<template #default="{ row }">
|
||||||
|
<span v-if="row">{{ formatGrade(row.previousGradeName, row.previousGradeId) }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="变更后等级" min-width="140" show-overflow-tooltip>
|
||||||
|
<template #default="{ row }">
|
||||||
|
<span v-if="row">{{ formatGrade(row.gradeName, row.gradeId) }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="商品佣金比例" width="130" align="right">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<span v-if="row">{{ formatRate(row.goodsCommissionRate) }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="邀请佣金比例" width="130" align="right">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<span v-if="row">{{ formatRate(row.inviteCommissionRate) }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="upgradeTime" label="升级时间" min-width="170" />
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<div class="mt_10" style="display: flex; justify-content: flex-end">
|
||||||
|
<el-pagination
|
||||||
|
v-model:current-page="searchForm.pageNumber"
|
||||||
|
v-model:page-size="searchForm.pageSize"
|
||||||
|
:page-sizes="[20, 50, 100]"
|
||||||
|
:total="total"
|
||||||
|
layout="total, sizes, prev, pager, next, jumper"
|
||||||
|
size="small"
|
||||||
|
@current-change="changePage"
|
||||||
|
@size-change="changePageSize"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { getDistributionGradeHistoryPage, getDistributionGrades } from "@/api/distribution";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "distributionGradeHistory",
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
loading: false,
|
||||||
|
searchForm: {
|
||||||
|
pageNumber: 1,
|
||||||
|
pageSize: 20,
|
||||||
|
memberName: "",
|
||||||
|
distributionMobile: "",
|
||||||
|
gradeId: "",
|
||||||
|
upgradeTimeStart: "",
|
||||||
|
upgradeTimeEnd: "",
|
||||||
|
},
|
||||||
|
upgradeTimeRange: [],
|
||||||
|
gradeOptions: [],
|
||||||
|
data: [],
|
||||||
|
total: 0,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.loadGradeOptions();
|
||||||
|
this.getDataList();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
loadGradeOptions() {
|
||||||
|
getDistributionGrades()
|
||||||
|
.then((res) => {
|
||||||
|
if (!res.success) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.gradeOptions = (res.result || [])
|
||||||
|
.slice()
|
||||||
|
.sort((a, b) => (a.sortOrder || 0) - (b.sortOrder || 0))
|
||||||
|
.map((item) => ({
|
||||||
|
value: String(item.sortOrder),
|
||||||
|
label: item.gradeName || `等级值${item.sortOrder}`,
|
||||||
|
}));
|
||||||
|
})
|
||||||
|
.catch(() => {});
|
||||||
|
},
|
||||||
|
formatGrade(name, gradeId) {
|
||||||
|
if (name) {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
if (gradeId) {
|
||||||
|
return `等级值${gradeId}`;
|
||||||
|
}
|
||||||
|
return "-";
|
||||||
|
},
|
||||||
|
formatRate(value) {
|
||||||
|
const num = Number(value);
|
||||||
|
if (!Number.isFinite(num)) {
|
||||||
|
return "-";
|
||||||
|
}
|
||||||
|
return `${num.toFixed(2).replace(/\.?0+$/, "") || "0"}%`;
|
||||||
|
},
|
||||||
|
applyUpgradeTimeRange() {
|
||||||
|
if (this.upgradeTimeRange && this.upgradeTimeRange.length === 2) {
|
||||||
|
this.searchForm.upgradeTimeStart = `${this.upgradeTimeRange[0]} 00:00:00`;
|
||||||
|
this.searchForm.upgradeTimeEnd = `${this.upgradeTimeRange[1]} 23:59:59`;
|
||||||
|
} else {
|
||||||
|
this.searchForm.upgradeTimeStart = "";
|
||||||
|
this.searchForm.upgradeTimeEnd = "";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handleSearch() {
|
||||||
|
this.searchForm.pageNumber = 1;
|
||||||
|
this.getDataList();
|
||||||
|
},
|
||||||
|
handleReset() {
|
||||||
|
this.searchForm = {
|
||||||
|
pageNumber: 1,
|
||||||
|
pageSize: 20,
|
||||||
|
memberName: "",
|
||||||
|
distributionMobile: "",
|
||||||
|
gradeId: "",
|
||||||
|
upgradeTimeStart: "",
|
||||||
|
upgradeTimeEnd: "",
|
||||||
|
};
|
||||||
|
this.upgradeTimeRange = [];
|
||||||
|
this.getDataList();
|
||||||
|
},
|
||||||
|
changePage() {
|
||||||
|
this.getDataList();
|
||||||
|
},
|
||||||
|
changePageSize() {
|
||||||
|
this.searchForm.pageNumber = 1;
|
||||||
|
this.getDataList();
|
||||||
|
},
|
||||||
|
getDataList() {
|
||||||
|
this.applyUpgradeTimeRange();
|
||||||
|
this.loading = true;
|
||||||
|
getDistributionGradeHistoryPage(this.searchForm)
|
||||||
|
.then((res) => {
|
||||||
|
this.loading = false;
|
||||||
|
if (res.success) {
|
||||||
|
this.data = res.result.records || [];
|
||||||
|
this.total = res.result.total || 0;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
this.loading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.search-form {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
136
manager/src/views/distribution/distributionImportRecord.vue
Normal file
136
manager/src/views/distribution/distributionImportRecord.vue
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
<template>
|
||||||
|
<div class="search">
|
||||||
|
<el-card>
|
||||||
|
<el-table v-loading="loading" border :data="data" class="mt_10" style="width: 100%">
|
||||||
|
<el-table-column prop="id" label="ID" min-width="200" class-name="id-column" show-overflow-tooltip />
|
||||||
|
<el-table-column prop="totalCount" label="导入总数" width="110" />
|
||||||
|
<el-table-column prop="successCount" label="成功数" width="100" />
|
||||||
|
<el-table-column prop="failCount" label="失败数" width="100" />
|
||||||
|
<el-table-column label="导入状态" width="120">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<span v-if="row">{{ statusLabel(row.status) }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="operatorName" label="操作人" min-width="120" show-overflow-tooltip />
|
||||||
|
<el-table-column prop="createTime" label="导入时间" min-width="170" />
|
||||||
|
<el-table-column label="操作" width="140" align="center" fixed="right">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<template v-if="row">
|
||||||
|
<a
|
||||||
|
v-if="row.failCount > 0"
|
||||||
|
class="link-text"
|
||||||
|
@click="downloadFailList(row)"
|
||||||
|
>下载失败列表</a>
|
||||||
|
<span v-else>-</span>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<div class="mt_10" style="display: flex; justify-content: flex-end">
|
||||||
|
<el-pagination
|
||||||
|
v-model:current-page="searchForm.pageNumber"
|
||||||
|
v-model:page-size="searchForm.pageSize"
|
||||||
|
:page-sizes="[20, 50, 100]"
|
||||||
|
:total="total"
|
||||||
|
layout="total, sizes, prev, pager, next, jumper"
|
||||||
|
size="small"
|
||||||
|
@current-change="changePage"
|
||||||
|
@size-change="changePageSize"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import {
|
||||||
|
downloadDistributionImportFailItems,
|
||||||
|
getDistributionImportBatches,
|
||||||
|
} from "@/api/distribution";
|
||||||
|
import { downloadBlob } from "@/utils/downloadBlob";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "distributionImportRecord",
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
loading: false,
|
||||||
|
searchForm: {
|
||||||
|
pageNumber: 1,
|
||||||
|
pageSize: 20,
|
||||||
|
},
|
||||||
|
data: [],
|
||||||
|
total: 0,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.getDataList();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
statusLabel(status) {
|
||||||
|
const map = {
|
||||||
|
PROCESSING: "处理中",
|
||||||
|
DONE: "已完成",
|
||||||
|
FAILED: "失败",
|
||||||
|
};
|
||||||
|
return map[status] || status || "-";
|
||||||
|
},
|
||||||
|
changePage() {
|
||||||
|
this.getDataList();
|
||||||
|
},
|
||||||
|
changePageSize() {
|
||||||
|
this.searchForm.pageNumber = 1;
|
||||||
|
this.getDataList();
|
||||||
|
},
|
||||||
|
getDataList() {
|
||||||
|
this.loading = true;
|
||||||
|
getDistributionImportBatches(this.searchForm)
|
||||||
|
.then((res) => {
|
||||||
|
this.loading = false;
|
||||||
|
if (res.success) {
|
||||||
|
this.data = res.result.records || [];
|
||||||
|
this.total = res.result.total || 0;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
this.loading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
downloadFailList(row) {
|
||||||
|
if (!row || !row.failCount) return;
|
||||||
|
downloadDistributionImportFailItems(row.id)
|
||||||
|
.then((blob) => {
|
||||||
|
if (blob && blob.type && blob.type.includes("application/json")) {
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.onload = () => {
|
||||||
|
try {
|
||||||
|
const result = JSON.parse(reader.result);
|
||||||
|
this.$Message.error(result.message || "下载失败");
|
||||||
|
} catch (e) {
|
||||||
|
this.$Message.error("下载失败");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
reader.readAsText(blob);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
downloadBlob(blob, `分销员导入失败列表_${row.id}.xlsx`);
|
||||||
|
this.$Message.success("下载成功");
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
this.$Message.error("下载失败");
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.link-text {
|
||||||
|
color: #2d8cf0;
|
||||||
|
cursor: pointer;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
.mt_10 {
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div class="search">
|
||||||
<el-card>
|
<el-card>
|
||||||
<el-form
|
<el-form
|
||||||
ref="searchForm"
|
ref="searchForm"
|
||||||
@@ -17,10 +17,10 @@
|
|||||||
style="width: 240px"
|
style="width: 240px"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="分销商" prop="distributionName">
|
<el-form-item label="分销员" prop="distributionName">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="searchForm.distributionName"
|
v-model="searchForm.distributionName"
|
||||||
placeholder="请输入分销商名称"
|
placeholder="请输入分销员名称"
|
||||||
clearable
|
clearable
|
||||||
style="width: 240px"
|
style="width: 240px"
|
||||||
/>
|
/>
|
||||||
@@ -60,7 +60,13 @@
|
|||||||
</el-form>
|
</el-form>
|
||||||
</el-card>
|
</el-card>
|
||||||
|
|
||||||
<el-card>
|
<el-card class="list-card">
|
||||||
|
<el-tabs v-model="currentStatus" class="status-tabs" @tab-change="onStatusTabChange">
|
||||||
|
<el-tab-pane label="全部" name="ALL" />
|
||||||
|
<el-tab-pane label="未完成" name="NO_COMPLETED" />
|
||||||
|
<el-tab-pane label="完成" name="COMPLETE" />
|
||||||
|
<el-tab-pane label="退款" name="REFUND" />
|
||||||
|
</el-tabs>
|
||||||
<el-table
|
<el-table
|
||||||
ref="table"
|
ref="table"
|
||||||
v-loading="loading"
|
v-loading="loading"
|
||||||
@@ -76,38 +82,42 @@
|
|||||||
fixed="left"
|
fixed="left"
|
||||||
show-overflow-tooltip
|
show-overflow-tooltip
|
||||||
/>
|
/>
|
||||||
|
<el-table-column label="客户信息" min-width="180">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<div v-if="row" class="member-cell">
|
||||||
|
<div class="member-name">{{ row.memberName || "-" }}</div>
|
||||||
|
<div class="member-id">客户ID:{{ row.memberId || "-" }}</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column label="商品信息" min-width="200">
|
<el-table-column label="商品信息" min-width="200">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<div v-if="row" class="goods-msg">
|
<div v-if="row" class="goods-msg">
|
||||||
<img :src="row.image" width="60" height="60" alt="" />
|
<img v-if="row.image" :src="row.image" class="goods-thumb" alt="" />
|
||||||
<div>
|
<span v-else class="goods-thumb-empty">无图</span>
|
||||||
<div class="div-zoom">
|
<div class="goods-info">
|
||||||
<a class="link-text" @click="linkTo(row.goodsId, row.skuId)">{{ row.goodsName }}</a>
|
<div class="goods-name">{{ row.goodsName }}</div>
|
||||||
</div>
|
<div class="goods-num">数量:x{{ row.num }}</div>
|
||||||
<div style="color: #999; font-size: 10px">数量:x{{ row.num }}</div>
|
|
||||||
<el-popover trigger="hover" title="扫码在手机中查看" placement="top" width="180">
|
|
||||||
<template #reference>
|
|
||||||
<img
|
|
||||||
src="../../assets/qrcode.svg"
|
|
||||||
class="hover-pointer"
|
|
||||||
width="20"
|
|
||||||
height="20"
|
|
||||||
alt="qrcode"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
<vue-qr
|
|
||||||
:text="wapLinkTo(row.goodsId, row.skuId)"
|
|
||||||
:margin="0"
|
|
||||||
color-dark="#000"
|
|
||||||
color-light="#fff"
|
|
||||||
:size="150"
|
|
||||||
/>
|
|
||||||
</el-popover>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="distributionName" label="分销商" min-width="100" show-overflow-tooltip />
|
<el-table-column label="一级分销员" min-width="180">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<div v-if="row" class="member-cell">
|
||||||
|
<div class="member-name">{{ row.distributionName || "-" }}</div>
|
||||||
|
<div class="member-id">分销员ID:{{ row.distributionId || "-" }}</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="二级分销员" min-width="180">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<div v-if="row" class="member-cell">
|
||||||
|
<div class="member-name">{{ row.inviteDistributionName || "-" }}</div>
|
||||||
|
<div class="member-id">分销员ID:{{ row.inviteDistributionId || "-" }}</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column prop="storeName" label="店铺名称" min-width="100" show-overflow-tooltip />
|
<el-table-column prop="storeName" label="店铺名称" min-width="100" show-overflow-tooltip />
|
||||||
<el-table-column label="状态" min-width="90">
|
<el-table-column label="状态" min-width="90">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
@@ -116,10 +126,18 @@
|
|||||||
</el-tag>
|
</el-tag>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="佣金金额" min-width="100">
|
<el-table-column label="一级佣金" min-width="100">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<span v-if="row" :style="{ color: $mainColor }">
|
<span v-if="row" :style="{ color: $mainColor }">
|
||||||
{{ $filters.unitPrice(row.rebate, "¥") }}</span>
|
{{ formatCommission(row.directCommission) }}
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="二级佣金" min-width="100">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<span v-if="row" :style="{ color: $mainColor }">
|
||||||
|
{{ formatCommission(row.inviteCommission) }}
|
||||||
|
</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="createTime" label="创建时间" min-width="160" fixed="right" />
|
<el-table-column prop="createTime" label="创建时间" min-width="160" fixed="right" />
|
||||||
@@ -145,23 +163,23 @@
|
|||||||
import { getDistributionOrder } from "@/api/distribution";
|
import { getDistributionOrder } from "@/api/distribution";
|
||||||
import { orderStatusList } from "./dataJson";
|
import { orderStatusList } from "./dataJson";
|
||||||
import { getShopListData } from "@/api/shops";
|
import { getShopListData } from "@/api/shops";
|
||||||
import vueQr from "vue-qr";
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "distributionOrder",
|
name: "distributionOrder",
|
||||||
components: { vueQr },
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
timeRange: [],
|
timeRange: [],
|
||||||
orderStatusList,
|
orderStatusList,
|
||||||
shopList: [],
|
shopList: [],
|
||||||
distributionId: this.$route.query.id,
|
distributionId: this.$route.query.id,
|
||||||
|
currentStatus: "ALL",
|
||||||
loading: true,
|
loading: true,
|
||||||
searchForm: {
|
searchForm: {
|
||||||
pageNumber: 1,
|
pageNumber: 1,
|
||||||
pageSize: 20,
|
pageSize: 20,
|
||||||
sort: "create_time",
|
sort: "create_time",
|
||||||
order: "desc",
|
order: "desc",
|
||||||
|
distributionOrderStatus: "",
|
||||||
},
|
},
|
||||||
data: [],
|
data: [],
|
||||||
total: 0,
|
total: 0,
|
||||||
@@ -178,6 +196,12 @@ export default {
|
|||||||
this.getDataList();
|
this.getDataList();
|
||||||
this.getShopList();
|
this.getShopList();
|
||||||
},
|
},
|
||||||
|
onStatusTabChange(name) {
|
||||||
|
this.currentStatus = name;
|
||||||
|
this.searchForm.distributionOrderStatus = name === "ALL" ? "" : name;
|
||||||
|
this.searchForm.pageNumber = 1;
|
||||||
|
this.getDataList();
|
||||||
|
},
|
||||||
changePage() {
|
changePage() {
|
||||||
this.getDataList();
|
this.getDataList();
|
||||||
},
|
},
|
||||||
@@ -191,7 +215,11 @@ export default {
|
|||||||
this.getDataList();
|
this.getDataList();
|
||||||
},
|
},
|
||||||
getDataList() {
|
getDataList() {
|
||||||
this.searchForm.distributionId = this.distributionId;
|
if (this.distributionId) {
|
||||||
|
this.searchForm.distributionId = this.distributionId;
|
||||||
|
} else {
|
||||||
|
delete this.searchForm.distributionId;
|
||||||
|
}
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
if (this.timeRange && this.timeRange[0] && this.timeRange[1]) {
|
if (this.timeRange && this.timeRange[0] && this.timeRange[1]) {
|
||||||
const startTime = new Date(this.timeRange[0]).getTime();
|
const startTime = new Date(this.timeRange[0]).getTime();
|
||||||
@@ -249,6 +277,12 @@ export default {
|
|||||||
}
|
}
|
||||||
return "warning";
|
return "warning";
|
||||||
},
|
},
|
||||||
|
formatCommission(amount) {
|
||||||
|
if (amount === null || amount === undefined || amount === "") {
|
||||||
|
return "-";
|
||||||
|
}
|
||||||
|
return this.$filters.unitPrice(amount, "¥");
|
||||||
|
},
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.init();
|
this.init();
|
||||||
@@ -256,26 +290,72 @@ export default {
|
|||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style scoped lang="scss">
|
||||||
|
.member-cell {
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
.member-name {
|
||||||
|
color: #303133;
|
||||||
|
}
|
||||||
|
.member-id {
|
||||||
|
margin-top: 2px;
|
||||||
|
color: #909399;
|
||||||
|
font-size: 12px;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
.goods-msg {
|
.goods-msg {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
> div {
|
gap: 10px;
|
||||||
margin-left: 10px;
|
padding: 2px 0;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
.goods-thumb {
|
||||||
.link-text {
|
width: 50px;
|
||||||
color: #409eff;
|
height: 50px;
|
||||||
cursor: pointer;
|
object-fit: cover;
|
||||||
text-decoration: none;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
.goods-thumb-empty {
|
||||||
.hover-pointer {
|
display: inline-flex;
|
||||||
cursor: pointer;
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 50px;
|
||||||
|
height: 50px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
background: #f5f7fa;
|
||||||
|
color: #909399;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
.goods-info {
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
.goods-name {
|
||||||
|
line-height: 1.4;
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
.goods-num {
|
||||||
|
color: #999;
|
||||||
|
font-size: 12px;
|
||||||
|
margin-top: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mt_10 {
|
.mt_10 {
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.list-card {
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-tabs {
|
||||||
|
margin-bottom: 4px;
|
||||||
|
|
||||||
|
:deep(.el-tabs__content) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-tabs__header) {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
600
manager/src/views/distribution/distributionRecruit.vue
Normal file
600
manager/src/views/distribution/distributionRecruit.vue
Normal file
@@ -0,0 +1,600 @@
|
|||||||
|
<template>
|
||||||
|
<div class="distribution-recruit" style="background-color: #fff; padding: 10px">
|
||||||
|
<el-form
|
||||||
|
ref="planFormRef"
|
||||||
|
:model="planForm"
|
||||||
|
label-width="160px"
|
||||||
|
label-position="right"
|
||||||
|
label-suffix=":"
|
||||||
|
>
|
||||||
|
<el-divider content-position="left">招募设置</el-divider>
|
||||||
|
<el-form-item label="分销员加入条件">
|
||||||
|
<div class="join-condition-wrap">
|
||||||
|
<el-radio-group v-model="planForm.joinConditionType">
|
||||||
|
<el-radio value="NONE">无条件</el-radio>
|
||||||
|
<el-radio value="CONDITIONAL">有条件</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
<div v-if="planForm.joinConditionType === 'CONDITIONAL'" class="join-condition-block">
|
||||||
|
<div class="join-condition-item">
|
||||||
|
<el-checkbox v-model="planForm.requirePurchaseGoods">需购买指定商品</el-checkbox>
|
||||||
|
<div v-if="planForm.requirePurchaseGoods" class="join-condition-detail">
|
||||||
|
<div class="form-desc">
|
||||||
|
注:请确保以下商品已上架。若配置前已购买任一指定商品的用户则已经满足条件,无需再次购买。
|
||||||
|
</div>
|
||||||
|
<div class="goods-toolbar">
|
||||||
|
<a class="link-btn" @click.prevent="openGoodsSelect">选择商品</a>
|
||||||
|
</div>
|
||||||
|
<el-table
|
||||||
|
v-if="recruitGoodsList.length"
|
||||||
|
border
|
||||||
|
:data="recruitGoodsList"
|
||||||
|
class="recruit-goods-table"
|
||||||
|
>
|
||||||
|
<el-table-column label="商品图" width="90" align="center">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<img
|
||||||
|
v-if="row.thumbnail"
|
||||||
|
:src="row.thumbnail"
|
||||||
|
alt="商品图"
|
||||||
|
class="recruit-goods-thumb"
|
||||||
|
/>
|
||||||
|
<span v-else>-</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="商品名称" min-width="200">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<div class="recruit-goods-name">{{ row.goodsName }}</div>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="sn" label="商品编码" min-width="120" show-overflow-tooltip />
|
||||||
|
<el-table-column label="SKU信息" min-width="140" show-overflow-tooltip>
|
||||||
|
<template #default="{ row }">
|
||||||
|
{{ row.simpleSpecs ? row.simpleSpecs.trim() : "-" }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="操作" width="90" align="center">
|
||||||
|
<template #default="{ $index }">
|
||||||
|
<a class="link-btn delete-btn" @click.prevent="removeRecruitGoods($index)">删除</a>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="join-condition-item">
|
||||||
|
<el-checkbox v-model="planForm.requireSelfPurchaseAmount">
|
||||||
|
自购金额满
|
||||||
|
<el-input-number
|
||||||
|
v-model="planForm.minSelfPurchaseAmount"
|
||||||
|
:min="0.01"
|
||||||
|
:precision="2"
|
||||||
|
:disabled="!planForm.requireSelfPurchaseAmount"
|
||||||
|
controls-position="right"
|
||||||
|
class="inline-number"
|
||||||
|
/>
|
||||||
|
元
|
||||||
|
</el-checkbox>
|
||||||
|
<div class="form-desc join-condition-desc">
|
||||||
|
客户支付后即计入自购金额,自购金额最低设置为0.01元。
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="join-condition-item">
|
||||||
|
<el-checkbox v-model="planForm.requireConsumeCount">
|
||||||
|
消费笔数满
|
||||||
|
<el-input-number
|
||||||
|
v-model="planForm.minConsumeCount"
|
||||||
|
:min="1"
|
||||||
|
:precision="0"
|
||||||
|
:disabled="!planForm.requireConsumeCount"
|
||||||
|
controls-position="right"
|
||||||
|
class="inline-number"
|
||||||
|
/>
|
||||||
|
笔
|
||||||
|
</el-checkbox>
|
||||||
|
<div class="form-desc join-condition-desc">
|
||||||
|
客户支付后即计入消费笔数,消费笔数最低设置为1笔。
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="分销员申请方式" required>
|
||||||
|
<div class="apply-mode-block">
|
||||||
|
<el-radio-group v-model="planForm.applyMode">
|
||||||
|
<el-radio value="MANUAL">手动申请</el-radio>
|
||||||
|
<el-radio value="AUTO">满足条件自动申请</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
<div v-if="planForm.applyMode === 'MANUAL'" class="manual-apply-extra">
|
||||||
|
<el-checkbox v-model="planForm.requireApplyInfo">需要填写申请信息</el-checkbox>
|
||||||
|
<div class="form-desc">申请人需填写对应字段,方便审核分销员信息。</div>
|
||||||
|
<template v-if="planForm.requireApplyInfo">
|
||||||
|
<div class="recruit-field-toolbar">
|
||||||
|
<a class="link-btn" @click.prevent="addRecruitField">+ 添加字段</a>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-for="(field, index) in recruitFields"
|
||||||
|
:key="field._key"
|
||||||
|
class="recruit-field-row"
|
||||||
|
>
|
||||||
|
<div class="recruit-field-row-main">
|
||||||
|
<span class="field-label">字段{{ index + 1 }}:</span>
|
||||||
|
<el-input
|
||||||
|
v-model="field.fieldName"
|
||||||
|
placeholder="如:姓名"
|
||||||
|
maxlength="64"
|
||||||
|
style="width: 200px"
|
||||||
|
/>
|
||||||
|
<el-select v-model="field.fieldType" style="width: 140px; margin-left: 8px">
|
||||||
|
<el-option
|
||||||
|
v-for="item in fieldTypeOptions"
|
||||||
|
:key="item.value"
|
||||||
|
:label="item.label"
|
||||||
|
:value="item.value"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
<a class="link-btn delete-btn" @click.prevent="removeRecruitField(index)">删除字段</a>
|
||||||
|
</div>
|
||||||
|
<div class="recruit-field-row-required">
|
||||||
|
<el-radio-group v-model="field.required">
|
||||||
|
<el-radio :value="true">必填</el-radio>
|
||||||
|
<el-radio :value="false">选填</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="审核方式">
|
||||||
|
<el-radio-group v-model="planForm.auditMode">
|
||||||
|
<el-radio value="MANUAL">人工审核</el-radio>
|
||||||
|
<el-radio value="AUTO_PASS">无需审核(满足条件自动通过)</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="展示线上招募入口">
|
||||||
|
<el-radio-group v-model="planForm.onlineEntryVisible">
|
||||||
|
<el-radio :value="true">展示</el-radio>
|
||||||
|
<el-radio :value="false">不展示</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="招募海报">
|
||||||
|
<div class="recruit-poster-block">
|
||||||
|
<div v-if="planForm.posterBackground" class="poster-preview-wrap">
|
||||||
|
<img :src="planForm.posterBackground" alt="招募海报" class="poster-thumb" />
|
||||||
|
</div>
|
||||||
|
<upload-pic-input v-model="planForm.posterBackground" :show-input="false" />
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="分销推广服务协议">
|
||||||
|
<editor v-if="editorReady" ref="agreementEditor" open-xss v-model="planForm.agreementContent" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" :loading="planSaveLoading" @click="savePlan">保存</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<sku-select ref="skuSelect" @selectedGoodsData="selectedGoodsData" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import {
|
||||||
|
getCurrentRecruitPlan,
|
||||||
|
saveRecruitPlan,
|
||||||
|
} from "@/api/distribution";
|
||||||
|
import uploadPicInput from "@/components/lili/upload-pic-input";
|
||||||
|
import tinymec from "@/components/editor/index.vue";
|
||||||
|
import skuSelect from "@/components/lili-dialog";
|
||||||
|
|
||||||
|
const MAX_RECRUIT_FIELDS = 5;
|
||||||
|
|
||||||
|
const FIELD_TYPE_OPTIONS = [
|
||||||
|
{ value: "NAME", label: "姓名格式" },
|
||||||
|
{ value: "MOBILE", label: "手机号格式" },
|
||||||
|
{ value: "IMAGE", label: "图片格式" },
|
||||||
|
{ value: "GENDER", label: "性别格式" },
|
||||||
|
];
|
||||||
|
|
||||||
|
const buildDefaultPlan = () => ({
|
||||||
|
id: "",
|
||||||
|
planVersion: null,
|
||||||
|
publishStatus: false,
|
||||||
|
joinConditionType: "NONE",
|
||||||
|
applyMode: "MANUAL",
|
||||||
|
auditMode: "MANUAL",
|
||||||
|
onlineEntryVisible: true,
|
||||||
|
requireApplyInfo: false,
|
||||||
|
agreementContent: "",
|
||||||
|
requirePurchaseGoods: false,
|
||||||
|
requireSelfPurchaseAmount: false,
|
||||||
|
requireConsumeCount: false,
|
||||||
|
minSelfPurchaseAmount: 0.01,
|
||||||
|
minConsumeCount: 1,
|
||||||
|
goodsIds: [],
|
||||||
|
posterEnabled: false,
|
||||||
|
posterTitle: "",
|
||||||
|
posterBenefitText: "",
|
||||||
|
posterRuleText: "",
|
||||||
|
posterContact: "",
|
||||||
|
posterBackground: "",
|
||||||
|
});
|
||||||
|
|
||||||
|
const buildDefaultField = () => ({
|
||||||
|
_key: `field_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`,
|
||||||
|
fieldName: "",
|
||||||
|
fieldType: "NAME",
|
||||||
|
required: true,
|
||||||
|
placeholder: "",
|
||||||
|
});
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "distributionRecruit",
|
||||||
|
components: { uploadPicInput, editor: tinymec, skuSelect },
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
planSaveLoading: false,
|
||||||
|
editorReady: true,
|
||||||
|
planForm: buildDefaultPlan(),
|
||||||
|
recruitFields: [],
|
||||||
|
recruitGoodsList: [],
|
||||||
|
fieldTypeOptions: FIELD_TYPE_OPTIONS,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.loadPlan();
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
"planForm.applyMode"(value) {
|
||||||
|
if (value !== "MANUAL") {
|
||||||
|
this.planForm.requireApplyInfo = false;
|
||||||
|
this.recruitFields = [];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"planForm.requireApplyInfo"(value) {
|
||||||
|
if (!value) {
|
||||||
|
this.recruitFields = [];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"planForm.joinConditionType"(value) {
|
||||||
|
if (value !== "CONDITIONAL") {
|
||||||
|
this.planForm.requirePurchaseGoods = false;
|
||||||
|
this.planForm.requireSelfPurchaseAmount = false;
|
||||||
|
this.planForm.requireConsumeCount = false;
|
||||||
|
this.recruitGoodsList = [];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"planForm.requirePurchaseGoods"(value) {
|
||||||
|
if (!value) {
|
||||||
|
this.recruitGoodsList = [];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"planForm.requireSelfPurchaseAmount"(value) {
|
||||||
|
if (value && (!this.planForm.minSelfPurchaseAmount || this.planForm.minSelfPurchaseAmount < 0.01)) {
|
||||||
|
this.planForm.minSelfPurchaseAmount = 0.01;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"planForm.requireConsumeCount"(value) {
|
||||||
|
if (value && (!this.planForm.minConsumeCount || this.planForm.minConsumeCount < 1)) {
|
||||||
|
this.planForm.minConsumeCount = 1;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
normalizeFields(fields) {
|
||||||
|
if (!fields || !fields.length) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
return fields.map((item, index) => ({
|
||||||
|
...buildDefaultField(),
|
||||||
|
...item,
|
||||||
|
_key: item._key || item.id || `field_${index}_${Date.now()}`,
|
||||||
|
required: item.required !== false,
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
addRecruitField() {
|
||||||
|
if (this.recruitFields.length >= MAX_RECRUIT_FIELDS) {
|
||||||
|
this.$Message.warning("最多添加5个字段");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.recruitFields.push(buildDefaultField());
|
||||||
|
},
|
||||||
|
removeRecruitField(index) {
|
||||||
|
this.recruitFields.splice(index, 1);
|
||||||
|
},
|
||||||
|
validateRecruitFields() {
|
||||||
|
if (!this.planForm.requireApplyInfo) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!this.recruitFields.length) {
|
||||||
|
this.$Message.warning("请至少添加一个申请字段");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (let i = 0; i < this.recruitFields.length; i++) {
|
||||||
|
const field = this.recruitFields[i];
|
||||||
|
if (!field.fieldName || !field.fieldName.trim()) {
|
||||||
|
this.$Message.warning(`请填写字段${i + 1}名称`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
validateJoinConditions() {
|
||||||
|
if (this.planForm.joinConditionType !== "CONDITIONAL") {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
const {
|
||||||
|
requirePurchaseGoods,
|
||||||
|
requireSelfPurchaseAmount,
|
||||||
|
requireConsumeCount,
|
||||||
|
} = this.planForm;
|
||||||
|
if (!requirePurchaseGoods && !requireSelfPurchaseAmount && !requireConsumeCount) {
|
||||||
|
this.$Message.warning("请至少选择一个加入条件");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (requirePurchaseGoods && !this.recruitGoodsList.length) {
|
||||||
|
this.$Message.warning("请选择指定商品");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (requireSelfPurchaseAmount && this.planForm.minSelfPurchaseAmount < 0.01) {
|
||||||
|
this.$Message.warning("自购金额最低设置为0.01元");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (requireConsumeCount && this.planForm.minConsumeCount < 1) {
|
||||||
|
this.$Message.warning("消费笔数最低设置为1笔");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
openGoodsSelect() {
|
||||||
|
this.$refs.skuSelect.open("goods");
|
||||||
|
const data = this.recruitGoodsList.map((item) => ({
|
||||||
|
id: item.skuId,
|
||||||
|
goodsId: item.goodsId,
|
||||||
|
goodsName: item.goodsName,
|
||||||
|
sn: item.sn,
|
||||||
|
simpleSpecs: item.simpleSpecs,
|
||||||
|
price: item.price,
|
||||||
|
quantity: item.quantity,
|
||||||
|
small: item.thumbnail,
|
||||||
|
}));
|
||||||
|
this.$refs.skuSelect.goodsData = data;
|
||||||
|
},
|
||||||
|
selectedGoodsData(items) {
|
||||||
|
this.recruitGoodsList = (items || []).map((item) => ({
|
||||||
|
skuId: item.id,
|
||||||
|
goodsId: item.goodsId,
|
||||||
|
goodsName: item.goodsName,
|
||||||
|
sn: item.sn || "",
|
||||||
|
simpleSpecs: item.simpleSpecs || "",
|
||||||
|
price: item.price,
|
||||||
|
quantity: item.quantity,
|
||||||
|
thumbnail: item.small || item.thumbnail || "",
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
removeRecruitGoods(index) {
|
||||||
|
this.recruitGoodsList.splice(index, 1);
|
||||||
|
},
|
||||||
|
buildSavePayload() {
|
||||||
|
const fields = this.planForm.requireApplyInfo
|
||||||
|
? this.recruitFields.map((field, index) => ({
|
||||||
|
id: field.id,
|
||||||
|
fieldName: field.fieldName.trim(),
|
||||||
|
fieldType: field.fieldType,
|
||||||
|
required: field.required !== false,
|
||||||
|
placeholder: field.placeholder || "",
|
||||||
|
sortOrder: index,
|
||||||
|
}))
|
||||||
|
: [];
|
||||||
|
return {
|
||||||
|
...this.planForm,
|
||||||
|
id: this.planForm.id || undefined,
|
||||||
|
publishStatus: true,
|
||||||
|
posterEnabled: !!(this.planForm.posterBackground && this.planForm.posterBackground.trim()),
|
||||||
|
recruitGoods: this.planForm.requirePurchaseGoods
|
||||||
|
? this.recruitGoodsList.map((item) => ({
|
||||||
|
goodsId: item.goodsId,
|
||||||
|
skuId: item.skuId,
|
||||||
|
goodsName: item.goodsName,
|
||||||
|
sn: item.sn,
|
||||||
|
simpleSpecs: item.simpleSpecs,
|
||||||
|
thumbnail: item.thumbnail,
|
||||||
|
price: item.price,
|
||||||
|
}))
|
||||||
|
: [],
|
||||||
|
goodsIds: this.planForm.requirePurchaseGoods
|
||||||
|
? this.recruitGoodsList.map((item) => item.goodsId)
|
||||||
|
: [],
|
||||||
|
fields,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
applyPlanResult(result) {
|
||||||
|
this.planForm = { ...buildDefaultPlan(), ...result };
|
||||||
|
this.recruitFields = this.normalizeFields(result.fields);
|
||||||
|
this.recruitGoodsList = (result.recruitGoods || []).map((item) => ({
|
||||||
|
goodsId: item.goodsId,
|
||||||
|
skuId: item.skuId,
|
||||||
|
goodsName: item.goodsName,
|
||||||
|
sn: item.sn || "",
|
||||||
|
simpleSpecs: item.simpleSpecs || "",
|
||||||
|
thumbnail: item.thumbnail,
|
||||||
|
price: item.price,
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
loadPlan() {
|
||||||
|
getCurrentRecruitPlan().then((res) => {
|
||||||
|
if (res.success && res.result) {
|
||||||
|
this.applyPlanResult(res.result);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
savePlan() {
|
||||||
|
if (!this.validateRecruitFields() || !this.validateJoinConditions()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.planSaveLoading = true;
|
||||||
|
saveRecruitPlan(this.buildSavePayload())
|
||||||
|
.then((res) => {
|
||||||
|
if (res.success) {
|
||||||
|
this.$Message.success("保存成功");
|
||||||
|
if (res.result) {
|
||||||
|
this.applyPlanResult(res.result);
|
||||||
|
} else {
|
||||||
|
this.loadPlan();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.planSaveLoading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.apply-mode-block {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.manual-apply-extra {
|
||||||
|
margin-top: 12px;
|
||||||
|
|
||||||
|
.form-desc {
|
||||||
|
margin-top: 8px;
|
||||||
|
margin-left: 24px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.recruit-field-toolbar {
|
||||||
|
margin: 12px 0 8px 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.link-btn {
|
||||||
|
color: #2d8cf0;
|
||||||
|
cursor: pointer;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.recruit-field-row {
|
||||||
|
margin: 12px 0 0 24px;
|
||||||
|
padding-bottom: 12px;
|
||||||
|
border-bottom: 1px dashed #e8eaec;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
padding-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.recruit-field-row-main {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.field-label {
|
||||||
|
min-width: 56px;
|
||||||
|
color: #606266;
|
||||||
|
}
|
||||||
|
|
||||||
|
.delete-btn {
|
||||||
|
margin-left: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.recruit-field-row-required {
|
||||||
|
margin-top: 10px;
|
||||||
|
padding-left: 56px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-desc {
|
||||||
|
margin-top: 8px;
|
||||||
|
color: #909399;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.join-condition-wrap {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.join-condition-block {
|
||||||
|
margin-top: 12px;
|
||||||
|
padding: 16px;
|
||||||
|
background: #f8f8f9;
|
||||||
|
border-radius: 4px;
|
||||||
|
width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.join-condition-item + .join-condition-item {
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.join-condition-detail {
|
||||||
|
margin-top: 8px;
|
||||||
|
margin-left: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.join-condition-desc {
|
||||||
|
margin-top: 8px;
|
||||||
|
margin-left: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.goods-toolbar {
|
||||||
|
margin: 8px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.recruit-goods-table {
|
||||||
|
margin-top: 8px;
|
||||||
|
|
||||||
|
:deep(.el-table__cell) {
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-table__cell .cell) {
|
||||||
|
white-space: normal;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.recruit-goods-name {
|
||||||
|
white-space: normal;
|
||||||
|
word-break: break-all;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.recruit-goods-thumb {
|
||||||
|
display: block;
|
||||||
|
width: 56px;
|
||||||
|
height: 56px;
|
||||||
|
margin: 4px auto;
|
||||||
|
object-fit: contain;
|
||||||
|
border: 1px solid #ebeef5;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inline-number {
|
||||||
|
width: 120px;
|
||||||
|
margin: 0 6px;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.recruit-poster-block {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 520px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.poster-preview-wrap {
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.poster-thumb {
|
||||||
|
display: block;
|
||||||
|
max-width: 240px;
|
||||||
|
max-height: 240px;
|
||||||
|
border: 1px solid #ebeef5;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -17,10 +17,10 @@
|
|||||||
style="width: 240px"
|
style="width: 240px"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="商品编号" prop="id">
|
<el-form-item label="商品ID" prop="id">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="searchForm.id"
|
v-model="searchForm.id"
|
||||||
placeholder="请输入商品编号"
|
placeholder="请输入商品ID"
|
||||||
clearable
|
clearable
|
||||||
style="width: 240px"
|
style="width: 240px"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -33,20 +33,16 @@
|
|||||||
|
|
||||||
<el-card>
|
<el-card>
|
||||||
<el-table v-loading="loading" :data="data" ref="table" class="mt_10" style="width: 100%">
|
<el-table v-loading="loading" :data="data" ref="table" class="mt_10" style="width: 100%">
|
||||||
<el-table-column label="商品名称" min-width="200" show-overflow-tooltip>
|
<el-table-column label="商品名称" min-width="220" class-name="goods-name-column">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<div v-if="row" style="margin-top: 5px; height: 80px; display: flex; align-items: flex-start">
|
<div v-if="row" class="goods-name-cell">
|
||||||
<img
|
<img
|
||||||
v-if="row.original"
|
v-if="row.original"
|
||||||
:src="row.original"
|
:src="row.original"
|
||||||
style="height: 60px; width: 60px; object-fit: cover; margin-top: 3px"
|
class="goods-name-img"
|
||||||
alt=""
|
alt=""
|
||||||
/>
|
/>
|
||||||
<div style="margin-left: 13px">
|
<a class="link-text goods-name-text">{{ row.goodsName }}</a>
|
||||||
<div class="div-zoom">
|
|
||||||
<a class="link-text">{{ row.goodsName }}</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
@@ -201,9 +197,25 @@ export default {
|
|||||||
margin: 0 8px;
|
margin: 0 8px;
|
||||||
color: #dcdee2;
|
color: #dcdee2;
|
||||||
}
|
}
|
||||||
.div-zoom {
|
.goods-name-cell {
|
||||||
overflow: hidden;
|
display: flex;
|
||||||
text-overflow: ellipsis;
|
align-items: flex-start;
|
||||||
white-space: nowrap;
|
gap: 12px;
|
||||||
|
padding: 4px 0;
|
||||||
|
}
|
||||||
|
.goods-name-img {
|
||||||
|
width: 60px;
|
||||||
|
height: 60px;
|
||||||
|
object-fit: cover;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.goods-name-text {
|
||||||
|
white-space: normal;
|
||||||
|
word-break: break-word;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
:deep(.goods-name-column .cell) {
|
||||||
|
white-space: normal;
|
||||||
|
line-height: 1.5;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div class="search">
|
||||||
<el-card>
|
<el-card>
|
||||||
<el-form
|
<el-form
|
||||||
ref="searchForm"
|
ref="searchForm"
|
||||||
@@ -9,10 +9,10 @@
|
|||||||
class="search-form"
|
class="search-form"
|
||||||
@keyup.enter="handleSearch"
|
@keyup.enter="handleSearch"
|
||||||
>
|
>
|
||||||
<el-form-item label="会员名称" prop="memberName">
|
<el-form-item label="客户名称" prop="memberName">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="searchForm.memberName"
|
v-model="searchForm.memberName"
|
||||||
placeholder="请输入会员名称"
|
placeholder="请输入客户名称"
|
||||||
clearable
|
clearable
|
||||||
style="width: 240px"
|
style="width: 240px"
|
||||||
/>
|
/>
|
||||||
@@ -34,7 +34,7 @@
|
|||||||
<el-card>
|
<el-card>
|
||||||
<el-table ref="table" v-loading="loading" border :data="data" class="mt_10" style="width: 100%">
|
<el-table ref="table" v-loading="loading" border :data="data" class="mt_10" style="width: 100%">
|
||||||
<el-table-column prop="goodsName" label="商品名称" min-width="120" show-overflow-tooltip />
|
<el-table-column prop="goodsName" label="商品名称" min-width="120" show-overflow-tooltip />
|
||||||
<el-table-column prop="memberName" label="会员名称" min-width="120" show-overflow-tooltip />
|
<el-table-column prop="memberName" label="客户名称" min-width="120" show-overflow-tooltip />
|
||||||
<el-table-column prop="content" label="评论内容" min-width="220" show-overflow-tooltip />
|
<el-table-column prop="content" label="评论内容" min-width="220" show-overflow-tooltip />
|
||||||
<el-table-column label="是否置顶" width="100" align="center">
|
<el-table-column label="是否置顶" width="100" align="center">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
@@ -297,7 +297,7 @@ export default {
|
|||||||
this.infoData = {};
|
this.infoData = {};
|
||||||
},
|
},
|
||||||
remove(v) {
|
remove(v) {
|
||||||
ElMessageBox.confirm("您确认要删除会员" + v.memberName + "的评论?", "确认删除", { type: "warning" }).then(() => {
|
ElMessageBox.confirm("您确认要删除客户" + v.memberName + "的评论?", "确认删除", { type: "warning" }).then(() => {
|
||||||
return API_Member.delMemberReview(v.id).then(() => {
|
return API_Member.delMemberReview(v.id).then(() => {
|
||||||
ElMessage.success("修改成功");
|
ElMessage.success("修改成功");
|
||||||
this.init();
|
this.init();
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
<el-badge :value="res.complain">待处理投诉审核</el-badge>
|
<el-badge :value="res.complain">待处理投诉审核</el-badge>
|
||||||
</el-dropdown-item>
|
</el-dropdown-item>
|
||||||
<el-dropdown-item v-if="res.distributionCash" command="distributionCash">
|
<el-dropdown-item v-if="res.distributionCash" command="distributionCash">
|
||||||
<el-badge :value="res.distributionCash">待处理分销商提现申请</el-badge>
|
<el-badge :value="res.distributionCash">待处理分销员提现申请</el-badge>
|
||||||
</el-dropdown-item>
|
</el-dropdown-item>
|
||||||
<el-dropdown-item v-if="res.goods" command="applyGoods">
|
<el-dropdown-item v-if="res.goods" command="applyGoods">
|
||||||
<el-badge :value="res.goods">待处理商品审核</el-badge>
|
<el-badge :value="res.goods">待处理商品审核</el-badge>
|
||||||
|
|||||||
@@ -9,10 +9,10 @@
|
|||||||
class="search-form"
|
class="search-form"
|
||||||
@keyup.enter="handleSearch"
|
@keyup.enter="handleSearch"
|
||||||
>
|
>
|
||||||
<el-form-item label="会员名称" prop="memberName">
|
<el-form-item label="客户名称" prop="memberName">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="searchForm.memberName"
|
v-model="searchForm.memberName"
|
||||||
placeholder="请输入会员名称"
|
placeholder="请输入客户名称"
|
||||||
clearable
|
clearable
|
||||||
style="width: 240px"
|
style="width: 240px"
|
||||||
/>
|
/>
|
||||||
@@ -52,7 +52,7 @@
|
|||||||
class="mt_10"
|
class="mt_10"
|
||||||
style="width: 100%"
|
style="width: 100%"
|
||||||
>
|
>
|
||||||
<el-table-column prop="memberName" label="会员名称" min-width="120" show-overflow-tooltip />
|
<el-table-column prop="memberName" label="客户名称" min-width="120" show-overflow-tooltip />
|
||||||
<el-table-column prop="rechargeSn" label="订单号" min-width="180" show-overflow-tooltip />
|
<el-table-column prop="rechargeSn" label="订单号" min-width="180" show-overflow-tooltip />
|
||||||
<el-table-column label="充值金额" width="160" sortable>
|
<el-table-column label="充值金额" width="160" sortable>
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
|
|||||||
@@ -9,10 +9,10 @@
|
|||||||
class="search-form"
|
class="search-form"
|
||||||
@keyup.enter="handleSearch"
|
@keyup.enter="handleSearch"
|
||||||
>
|
>
|
||||||
<el-form-item label="会员名称" prop="memberName">
|
<el-form-item label="客户名称" prop="memberName">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="searchForm.memberName"
|
v-model="searchForm.memberName"
|
||||||
placeholder="请输入会员名称"
|
placeholder="请输入客户名称"
|
||||||
clearable
|
clearable
|
||||||
style="width: 240px"
|
style="width: 240px"
|
||||||
/>
|
/>
|
||||||
@@ -44,7 +44,7 @@
|
|||||||
class="mt_10"
|
class="mt_10"
|
||||||
style="width: 100%"
|
style="width: 100%"
|
||||||
>
|
>
|
||||||
<el-table-column prop="memberName" label="会员名称" min-width="100" />
|
<el-table-column prop="memberName" label="客户名称" min-width="100" />
|
||||||
<el-table-column label="变动金额" width="150">
|
<el-table-column label="变动金额" width="150">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<template v-if="row">
|
<template v-if="row">
|
||||||
|
|||||||
@@ -9,10 +9,10 @@
|
|||||||
class="search-form"
|
class="search-form"
|
||||||
@keyup.enter="handleSearch"
|
@keyup.enter="handleSearch"
|
||||||
>
|
>
|
||||||
<el-form-item label="会员名称" prop="memberName">
|
<el-form-item label="客户名称" prop="memberName">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="searchForm.memberName"
|
v-model="searchForm.memberName"
|
||||||
placeholder="请输入会员名称"
|
placeholder="请输入客户名称"
|
||||||
clearable
|
clearable
|
||||||
style="width: 240px"
|
style="width: 240px"
|
||||||
/>
|
/>
|
||||||
@@ -57,7 +57,7 @@
|
|||||||
@selection-change="changeSelect"
|
@selection-change="changeSelect"
|
||||||
>
|
>
|
||||||
<el-table-column prop="sn" label="申请编号" min-width="160" show-overflow-tooltip />
|
<el-table-column prop="sn" label="申请编号" min-width="160" show-overflow-tooltip />
|
||||||
<el-table-column prop="memberName" label="用户名称" min-width="120" show-overflow-tooltip />
|
<el-table-column prop="memberName" label="客户名称" min-width="120" show-overflow-tooltip />
|
||||||
<el-table-column label="申请金额" width="120">
|
<el-table-column label="申请金额" width="120">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<priceColorScheme v-if="row" :value="row.applyMoney" :color="$mainColor" />
|
<priceColorScheme v-if="row" :value="row.applyMoney" :color="$mainColor" />
|
||||||
|
|||||||
@@ -9,11 +9,11 @@
|
|||||||
class="search-form"
|
class="search-form"
|
||||||
@keyup.enter="handleSearch"
|
@keyup.enter="handleSearch"
|
||||||
>
|
>
|
||||||
<el-form-item label="会员ID" prop="id">
|
<el-form-item label="客户ID" prop="id">
|
||||||
<el-input v-model="searchForm.id" placeholder="请输入会员ID" clearable style="width: 240px" />
|
<el-input v-model="searchForm.id" placeholder="请输入客户ID" clearable style="width: 240px" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="会员名称" prop="username">
|
<el-form-item label="客户名称" prop="username">
|
||||||
<el-input v-model="searchForm.username" placeholder="请输入会员名称" clearable style="width: 240px" />
|
<el-input v-model="searchForm.username" placeholder="请输入客户名称" clearable style="width: 240px" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="会员昵称" prop="nickName">
|
<el-form-item label="会员昵称" prop="nickName">
|
||||||
<el-input v-model="searchForm.nickName" placeholder="请输入会员昵称" clearable style="width: 240px" />
|
<el-input v-model="searchForm.nickName" placeholder="请输入会员昵称" clearable style="width: 240px" />
|
||||||
@@ -75,7 +75,7 @@
|
|||||||
align="center"
|
align="center"
|
||||||
:reserve-selection="checkboxSelect"
|
:reserve-selection="checkboxSelect"
|
||||||
/>
|
/>
|
||||||
<el-table-column prop="id" label="会员ID" min-width="120" show-overflow-tooltip />
|
<el-table-column prop="id" label="客户ID" min-width="120" show-overflow-tooltip />
|
||||||
<el-table-column label="头像" min-width="80" align="center">
|
<el-table-column label="头像" min-width="80" align="center">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<img
|
<img
|
||||||
@@ -86,7 +86,7 @@
|
|||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="username" label="会员名称" min-width="150" show-overflow-tooltip />
|
<el-table-column prop="username" label="客户名称" min-width="150" show-overflow-tooltip />
|
||||||
<el-table-column prop="nickName" label="会员昵称" min-width="120" show-overflow-tooltip />
|
<el-table-column prop="nickName" label="会员昵称" min-width="120" show-overflow-tooltip />
|
||||||
<el-table-column label="联系方式" min-width="130">
|
<el-table-column label="联系方式" min-width="130">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
@@ -182,11 +182,11 @@
|
|||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="会员名称" prop="username" style="width: 90%">
|
<el-form-item label="客户名称" prop="username" style="width: 90%">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="addMemberForm.username"
|
v-model="addMemberForm.username"
|
||||||
maxlength="15"
|
maxlength="15"
|
||||||
placeholder="请输入会员名称"
|
placeholder="请输入客户名称"
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
name="member-username"
|
name="member-username"
|
||||||
/>
|
/>
|
||||||
@@ -421,7 +421,7 @@ export default {
|
|||||||
{ required: true, message: "请输入手机号码" },
|
{ required: true, message: "请输入手机号码" },
|
||||||
{ pattern: RegExp.mobile, message: "请输入正确的手机号" },
|
{ pattern: RegExp.mobile, message: "请输入正确的手机号" },
|
||||||
],
|
],
|
||||||
username: [{ required: true, message: "请输入会员名称" }],
|
username: [{ required: true, message: "请输入客户名称" }],
|
||||||
password: [{ required: true, message: "请输入密码" }],
|
password: [{ required: true, message: "请输入密码" }],
|
||||||
},
|
},
|
||||||
ruleValidate: {},
|
ruleValidate: {},
|
||||||
|
|||||||
@@ -9,10 +9,18 @@
|
|||||||
class="search-form"
|
class="search-form"
|
||||||
@keyup.enter="handleSearch"
|
@keyup.enter="handleSearch"
|
||||||
>
|
>
|
||||||
<el-form-item label="会员名称" prop="username">
|
<el-form-item label="客户ID" prop="id">
|
||||||
|
<el-input
|
||||||
|
v-model="searchForm.id"
|
||||||
|
placeholder="请输入客户ID"
|
||||||
|
clearable
|
||||||
|
style="width: 240px"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="客户名称" prop="username">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="searchForm.username"
|
v-model="searchForm.username"
|
||||||
placeholder="请输入会员名称"
|
placeholder="请输入客户名称"
|
||||||
clearable
|
clearable
|
||||||
style="width: 240px"
|
style="width: 240px"
|
||||||
/>
|
/>
|
||||||
@@ -40,7 +48,8 @@
|
|||||||
class="mt_10"
|
class="mt_10"
|
||||||
style="width: 100%"
|
style="width: 100%"
|
||||||
>
|
>
|
||||||
<el-table-column prop="username" label="会员名称" min-width="150" align="left" show-overflow-tooltip />
|
<el-table-column prop="id" label="客户ID" min-width="120" show-overflow-tooltip />
|
||||||
|
<el-table-column prop="username" label="客户名称" min-width="150" align="left" show-overflow-tooltip />
|
||||||
<el-table-column prop="nickName" label="昵称" min-width="120" align="left" show-overflow-tooltip />
|
<el-table-column prop="nickName" label="昵称" min-width="120" align="left" show-overflow-tooltip />
|
||||||
<el-table-column label="联系方式" min-width="130">
|
<el-table-column label="联系方式" min-width="130">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
@@ -98,7 +107,7 @@
|
|||||||
>修改</el-button>
|
>修改</el-button>
|
||||||
<input type="file" style="display: none" id="file" />
|
<input type="file" style="display: none" id="file" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="会员名称" prop="name">
|
<el-form-item label="客户名称" prop="name">
|
||||||
<el-input v-model="formValidate.username" style="width: 200px" disabled />
|
<el-input v-model="formValidate.username" style="width: 200px" disabled />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="用户昵称" prop="name">
|
<el-form-item label="用户昵称" prop="name">
|
||||||
@@ -168,6 +177,7 @@ export default {
|
|||||||
pageNumber: 1,
|
pageNumber: 1,
|
||||||
pageSize: 20,
|
pageSize: 20,
|
||||||
order: "desc",
|
order: "desc",
|
||||||
|
id: "",
|
||||||
username: "",
|
username: "",
|
||||||
mobile: "",
|
mobile: "",
|
||||||
disabled: "CLOSE",
|
disabled: "CLOSE",
|
||||||
|
|||||||
@@ -7,63 +7,169 @@
|
|||||||
</el-affix>
|
</el-affix>
|
||||||
|
|
||||||
<el-card class="card">
|
<el-card class="card">
|
||||||
<h4>分销概况</h4>
|
<el-tabs v-model="activeTab" @tab-change="onTabChange">
|
||||||
<div class="metric-grid">
|
<el-tab-pane label="概览" name="overview">
|
||||||
<div class="metric-item" v-for="item in metricDefs" :key="item.key">
|
<h4>分销概况</h4>
|
||||||
<div class="metric-label">
|
<div class="metric-grid">
|
||||||
<span>{{ item.label }}</span>
|
<div class="metric-item" v-for="item in metricDefs" :key="item.key">
|
||||||
<el-tooltip v-if="item.tip" :content="item.tip" placement="top">
|
<div class="metric-label">
|
||||||
<el-icon class="metric-tip"><QuestionFilled /></el-icon>
|
<span>{{ item.label }}</span>
|
||||||
</el-tooltip>
|
<el-tooltip v-if="item.tip" :content="item.tip" placement="top">
|
||||||
|
<el-icon class="metric-tip"><QuestionFilled /></el-icon>
|
||||||
|
</el-tooltip>
|
||||||
|
</div>
|
||||||
|
<div class="metric-value">
|
||||||
|
<template v-if="item.isMoney">
|
||||||
|
{{ $filters.unitPrice(overview[item.key] || 0, "¥") }}
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
{{ formatNumber(overview[item.key]) }}
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="metric-value">
|
|
||||||
<template v-if="item.isMoney">
|
|
||||||
{{ $filters.unitPrice(overview[item.key] || 0, "¥") }}
|
|
||||||
</template>
|
|
||||||
<template v-else>
|
|
||||||
{{ formatNumber(overview[item.key]) }}
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</el-card>
|
|
||||||
|
|
||||||
<el-card class="card">
|
<h4 class="section-title">今日数据</h4>
|
||||||
<h4>分销排行</h4>
|
<div class="metric-grid">
|
||||||
<el-row :gutter="16">
|
<div class="metric-item" v-for="item in todayMetricDefs" :key="item.key">
|
||||||
<el-col :xs="24" :md="12">
|
<div class="metric-label">{{ item.label }}</div>
|
||||||
<div class="rank-title">TOP分销员</div>
|
<div class="metric-value">
|
||||||
<el-table :data="distributorRank" stripe style="width: 100%">
|
<template v-if="item.isMoney">
|
||||||
<el-table-column prop="rank" label="排名" width="70" />
|
<priceColorScheme :value="overviewV2[item.key]" :color="$mainColor" />
|
||||||
<el-table-column prop="name" label="分销员" min-width="140" show-overflow-tooltip />
|
</template>
|
||||||
<el-table-column label="佣金" min-width="120">
|
<template v-else>
|
||||||
|
{{ formatNumber(overviewV2[item.key]) }}
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h4 class="section-title">累计数据</h4>
|
||||||
|
<div class="metric-grid">
|
||||||
|
<div class="metric-item" v-for="item in totalMetricDefs" :key="item.key">
|
||||||
|
<div class="metric-label">{{ item.label }}</div>
|
||||||
|
<div class="metric-value">
|
||||||
|
<template v-if="item.isMoney">
|
||||||
|
<priceColorScheme :value="overviewV2[item.key]" :color="$mainColor" />
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
{{ formatNumber(overviewV2[item.key]) }}
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h4 class="section-title">分销排行</h4>
|
||||||
|
<el-row :gutter="16">
|
||||||
|
<el-col :xs="24" :md="12">
|
||||||
|
<div class="rank-title">TOP分销员</div>
|
||||||
|
<el-table :data="distributorRank" stripe style="width: 100%">
|
||||||
|
<el-table-column prop="rank" label="排名" width="70" />
|
||||||
|
<el-table-column prop="name" label="分销员" min-width="140" show-overflow-tooltip />
|
||||||
|
<el-table-column label="佣金" min-width="120">
|
||||||
|
<template #default="{ row }">
|
||||||
|
{{ $filters.unitPrice(row.amount || 0, "¥") }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="num" label="订单数" min-width="100" />
|
||||||
|
</el-table>
|
||||||
|
</el-col>
|
||||||
|
<el-col :xs="24" :md="12">
|
||||||
|
<div class="rank-title">TOP分销商品</div>
|
||||||
|
<el-table :data="goodsRank" stripe style="width: 100%">
|
||||||
|
<el-table-column prop="rank" label="排名" width="70" />
|
||||||
|
<el-table-column prop="name" label="商品" min-width="160" show-overflow-tooltip />
|
||||||
|
<el-table-column label="佣金" min-width="120">
|
||||||
|
<template #default="{ row }">
|
||||||
|
{{ $filters.unitPrice(row.amount || 0, "¥") }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="num" label="销量" min-width="100" />
|
||||||
|
</el-table>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</el-tab-pane>
|
||||||
|
|
||||||
|
<el-tab-pane label="行为数据" name="behavior">
|
||||||
|
<el-table v-loading="behaviorLoading" :data="behaviorData" stripe border style="width: 100%">
|
||||||
|
<el-table-column prop="nickname" label="昵称" min-width="120" show-overflow-tooltip />
|
||||||
|
<el-table-column prop="mobile" label="手机号" min-width="120" />
|
||||||
|
<el-table-column prop="gradeName" label="等级" width="100" />
|
||||||
|
<el-table-column label="新增销售额" min-width="120">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
{{ $filters.unitPrice(row.amount || 0, "¥") }}
|
<priceColorScheme v-if="row" :value="row.newSalesAmount" :color="$mainColor" />
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="num" label="订单数" min-width="100" />
|
<el-table-column prop="newCustomerCount" label="新增客户数" width="110" />
|
||||||
|
<el-table-column prop="paidCustomerCount" label="支付客户数" width="110" />
|
||||||
|
<el-table-column prop="newInviteCount" label="新增邀请数" width="110" />
|
||||||
|
<el-table-column prop="orderInviteCount" label="开单邀请数" width="110" />
|
||||||
|
<el-table-column prop="shareCount" label="商品分享数" width="110" />
|
||||||
|
<el-table-column prop="sharedGoodsCount" label="分享商品数" width="110" />
|
||||||
|
<el-table-column prop="shareVisitorCount" label="分享引流" width="100" />
|
||||||
|
<el-table-column prop="shareVisitCount" label="分享浏览量" width="110" />
|
||||||
</el-table>
|
</el-table>
|
||||||
</el-col>
|
<div class="mt_10" style="display: flex; justify-content: flex-end">
|
||||||
<el-col :xs="24" :md="12">
|
<el-pagination
|
||||||
<div class="rank-title">TOP分销商品</div>
|
v-model:current-page="behaviorPage.pageNumber"
|
||||||
<el-table :data="goodsRank" stripe style="width: 100%">
|
v-model:page-size="behaviorPage.pageSize"
|
||||||
<el-table-column prop="rank" label="排名" width="70" />
|
:page-sizes="[20, 50, 100]"
|
||||||
<el-table-column prop="name" label="商品" min-width="160" show-overflow-tooltip />
|
:total="behaviorTotal"
|
||||||
<el-table-column label="佣金" min-width="120">
|
layout="total, sizes, prev, pager, next, jumper"
|
||||||
|
size="small"
|
||||||
|
@current-change="loadBehavior"
|
||||||
|
@size-change="onBehaviorSizeChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</el-tab-pane>
|
||||||
|
|
||||||
|
<el-tab-pane label="商品数据" name="goods">
|
||||||
|
<el-table v-loading="goodsLoading" :data="goodsData" stripe border style="width: 100%">
|
||||||
|
<el-table-column prop="goodsName" label="商品名称" min-width="160" show-overflow-tooltip />
|
||||||
|
<el-table-column prop="shareDistributorCount" label="分享人数" width="100" />
|
||||||
|
<el-table-column prop="shareCount" label="分享次数" width="100" />
|
||||||
|
<el-table-column prop="visitVisitorCount" label="访问人数" width="100" />
|
||||||
|
<el-table-column label="成交金额" min-width="120">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
{{ $filters.unitPrice(row.amount || 0, "¥") }}
|
<priceColorScheme v-if="row" :value="row.dealAmount" :color="$mainColor" />
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="支出佣金" min-width="120">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<priceColorScheme v-if="row" :value="row.commissionAmount" :color="$mainColor" />
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="访问转化率" width="110">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<span v-if="row">{{ formatRate(row.visitConversionRate) }}</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="num" label="销量" min-width="100" />
|
|
||||||
</el-table>
|
</el-table>
|
||||||
</el-col>
|
<div class="mt_10" style="display: flex; justify-content: flex-end">
|
||||||
</el-row>
|
<el-pagination
|
||||||
|
v-model:current-page="goodsPage.pageNumber"
|
||||||
|
v-model:page-size="goodsPage.pageSize"
|
||||||
|
:page-sizes="[20, 50, 100]"
|
||||||
|
:total="goodsTotal"
|
||||||
|
layout="total, sizes, prev, pager, next, jumper"
|
||||||
|
size="small"
|
||||||
|
@current-change="loadGoods"
|
||||||
|
@size-change="onGoodsSizeChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</el-tab-pane>
|
||||||
|
</el-tabs>
|
||||||
</el-card>
|
</el-card>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import * as API_Statistics from "@/api/statistics";
|
import * as API_Statistics from "@/api/statistics";
|
||||||
|
import {
|
||||||
|
getDistributionOverviewV2,
|
||||||
|
getDistributionBehaviorV2,
|
||||||
|
getDistributionGoodsV2,
|
||||||
|
} from "@/api/distribution";
|
||||||
import affixTime from "@/components/affix-time";
|
import affixTime from "@/components/affix-time";
|
||||||
import { QuestionFilled } from "@element-plus/icons-vue";
|
import { QuestionFilled } from "@element-plus/icons-vue";
|
||||||
|
|
||||||
@@ -71,6 +177,7 @@ export default {
|
|||||||
components: { affixTime, QuestionFilled },
|
components: { affixTime, QuestionFilled },
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
activeTab: "overview",
|
||||||
params: {
|
params: {
|
||||||
searchType: "LAST_SEVEN",
|
searchType: "LAST_SEVEN",
|
||||||
year: "",
|
year: "",
|
||||||
@@ -78,43 +185,52 @@ export default {
|
|||||||
storeId: "",
|
storeId: "",
|
||||||
},
|
},
|
||||||
overview: {},
|
overview: {},
|
||||||
|
overviewV2: {},
|
||||||
distributorRank: [],
|
distributorRank: [],
|
||||||
goodsRank: [],
|
goodsRank: [],
|
||||||
|
behaviorData: [],
|
||||||
|
behaviorTotal: 0,
|
||||||
|
behaviorLoading: false,
|
||||||
|
behaviorPage: { pageNumber: 1, pageSize: 20 },
|
||||||
|
goodsData: [],
|
||||||
|
goodsTotal: 0,
|
||||||
|
goodsLoading: false,
|
||||||
|
goodsPage: { pageNumber: 1, pageSize: 20 },
|
||||||
metricDefs: [
|
metricDefs: [
|
||||||
{ key: "distributorNum", label: "分销员数", tip: "已通过审核的分销员数量" },
|
{ key: "distributorNum", label: "分销员数", tip: "已通过审核的分销员数量" },
|
||||||
{ key: "applyNum", label: "待审核分销员", tip: "申请中待审核的分销员数量" },
|
{ key: "applyNum", label: "待审核分销员", tip: "申请中待审核的分销员数量" },
|
||||||
{ key: "distributionOrderNum", label: "分销订单数", tip: "周期内分销订单笔数" },
|
{ key: "distributionOrderNum", label: "分销订单数", tip: "周期内分销订单笔数" },
|
||||||
{
|
{ key: "distributionOrderAmount", label: "分销订单金额", isMoney: true, tip: "周期内分销订单佣金合计" },
|
||||||
key: "distributionOrderAmount",
|
{ key: "settledCommission", label: "已结算佣金", isMoney: true, tip: "周期内已完成结算的佣金" },
|
||||||
label: "分销订单金额",
|
{ key: "pendingCommission", label: "待结算佣金", isMoney: true, tip: "周期内待结算的佣金" },
|
||||||
isMoney: true,
|
{ key: "cashAmount", label: "提现金额", isMoney: true, tip: "周期内分销员提现金额" },
|
||||||
tip: "周期内分销订单佣金合计",
|
],
|
||||||
},
|
todayMetricDefs: [
|
||||||
{
|
{ key: "todayNewDistributorCount", label: "今日新增分销员" },
|
||||||
key: "settledCommission",
|
{ key: "todayNewSubDistributorCount", label: "今日新增下级分销员" },
|
||||||
label: "已结算佣金",
|
{ key: "todaySalesAmount", label: "今日分销员销售额", isMoney: true },
|
||||||
isMoney: true,
|
{ key: "todayPaidCustomerCount", label: "今日成交客户数" },
|
||||||
tip: "周期内已完成结算的佣金",
|
{ key: "todayCommissionAmount", label: "今日支出佣金", isMoney: true },
|
||||||
},
|
],
|
||||||
{
|
totalMetricDefs: [
|
||||||
key: "pendingCommission",
|
{ key: "totalDistributorCount", label: "累计分销员数" },
|
||||||
label: "待结算佣金",
|
{ key: "totalSubDistributorCount", label: "累计下级分销员数" },
|
||||||
isMoney: true,
|
{ key: "totalSalesAmount", label: "累计分销员销售额", isMoney: true },
|
||||||
tip: "周期内待结算的佣金",
|
{ key: "totalPaidCustomerCount", label: "累计成交客户数" },
|
||||||
},
|
{ key: "totalShareVisitorCount", label: "商品分享引流" },
|
||||||
{
|
{ key: "totalCommissionAmount", label: "累计支出佣金", isMoney: true },
|
||||||
key: "cashAmount",
|
|
||||||
label: "提现金额",
|
|
||||||
isMoney: true,
|
|
||||||
tip: "周期内分销员提现金额",
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
params: {
|
params: {
|
||||||
handler() {
|
handler() {
|
||||||
this.loadData();
|
this.loadOverviewData();
|
||||||
|
if (this.activeTab === "behavior") {
|
||||||
|
this.loadBehavior();
|
||||||
|
} else if (this.activeTab === "goods") {
|
||||||
|
this.loadGoods();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
deep: true,
|
deep: true,
|
||||||
immediate: true,
|
immediate: true,
|
||||||
@@ -131,17 +247,36 @@ export default {
|
|||||||
endTime: item.endTime || "",
|
endTime: item.endTime || "",
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
onTabChange(name) {
|
||||||
|
if (name === "behavior") {
|
||||||
|
this.loadBehavior();
|
||||||
|
} else if (name === "goods") {
|
||||||
|
this.loadGoods();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
buildQueryParams() {
|
||||||
|
return {
|
||||||
|
startTime: this.params.startTime || "",
|
||||||
|
endTime: this.params.endTime || "",
|
||||||
|
};
|
||||||
|
},
|
||||||
formatNumber(val) {
|
formatNumber(val) {
|
||||||
if (val == null || val === "") return 0;
|
if (val == null || val === "") return 0;
|
||||||
const num = Number(val);
|
const num = Number(val);
|
||||||
return Number.isNaN(num) ? 0 : Math.round(num);
|
return Number.isNaN(num) ? 0 : Math.round(num);
|
||||||
},
|
},
|
||||||
async loadData() {
|
formatRate(val) {
|
||||||
|
if (val == null || val === "") return "-";
|
||||||
|
return `${(Number(val) * 100).toFixed(2)}%`;
|
||||||
|
},
|
||||||
|
async loadOverviewData() {
|
||||||
const query = { ...this.params };
|
const query = { ...this.params };
|
||||||
const [overviewRes, distributorRes, goodsRes] = await Promise.all([
|
const v2Query = this.buildQueryParams();
|
||||||
|
const [overviewRes, distributorRes, goodsRes, overviewV2Res] = await Promise.all([
|
||||||
API_Statistics.getDistributionOverview(query),
|
API_Statistics.getDistributionOverview(query),
|
||||||
API_Statistics.getDistributionRankDistributor(query),
|
API_Statistics.getDistributionRankDistributor(query),
|
||||||
API_Statistics.getDistributionRankGoods(query),
|
API_Statistics.getDistributionRankGoods(query),
|
||||||
|
getDistributionOverviewV2(v2Query),
|
||||||
]);
|
]);
|
||||||
if (overviewRes && overviewRes.success) {
|
if (overviewRes && overviewRes.success) {
|
||||||
this.overview = overviewRes.result || {};
|
this.overview = overviewRes.result || {};
|
||||||
@@ -152,6 +287,45 @@ export default {
|
|||||||
if (goodsRes && goodsRes.success) {
|
if (goodsRes && goodsRes.success) {
|
||||||
this.goodsRank = goodsRes.result || [];
|
this.goodsRank = goodsRes.result || [];
|
||||||
}
|
}
|
||||||
|
if (overviewV2Res && overviewV2Res.success) {
|
||||||
|
this.overviewV2 = overviewV2Res.result || {};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
loadBehavior() {
|
||||||
|
this.behaviorLoading = true;
|
||||||
|
getDistributionBehaviorV2({
|
||||||
|
...this.buildQueryParams(),
|
||||||
|
pageNumber: this.behaviorPage.pageNumber,
|
||||||
|
pageSize: this.behaviorPage.pageSize,
|
||||||
|
}).then((res) => {
|
||||||
|
this.behaviorLoading = false;
|
||||||
|
if (res.success) {
|
||||||
|
this.behaviorData = res.result.records || [];
|
||||||
|
this.behaviorTotal = res.result.total || 0;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onBehaviorSizeChange() {
|
||||||
|
this.behaviorPage.pageNumber = 1;
|
||||||
|
this.loadBehavior();
|
||||||
|
},
|
||||||
|
loadGoods() {
|
||||||
|
this.goodsLoading = true;
|
||||||
|
getDistributionGoodsV2({
|
||||||
|
...this.buildQueryParams(),
|
||||||
|
pageNumber: this.goodsPage.pageNumber,
|
||||||
|
pageSize: this.goodsPage.pageSize,
|
||||||
|
}).then((res) => {
|
||||||
|
this.goodsLoading = false;
|
||||||
|
if (res.success) {
|
||||||
|
this.goodsData = res.result.records || [];
|
||||||
|
this.goodsTotal = res.result.total || 0;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onGoodsSizeChange() {
|
||||||
|
this.goodsPage.pageNumber = 1;
|
||||||
|
this.loadGoods();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -167,6 +341,9 @@ export default {
|
|||||||
h4 {
|
h4 {
|
||||||
margin: 0 0 20px 0;
|
margin: 0 0 20px 0;
|
||||||
}
|
}
|
||||||
|
.section-title {
|
||||||
|
margin-top: 24px;
|
||||||
|
}
|
||||||
.metric-grid {
|
.metric-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(4, 1fr);
|
grid-template-columns: repeat(4, 1fr);
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ var BASE = {
|
|||||||
* @description api请求基础路径
|
* @description api请求基础路径
|
||||||
*/
|
*/
|
||||||
API_DEV: {
|
API_DEV: {
|
||||||
seller: "https://admin-api.pickmall.cn",
|
seller: "http://127.0.0.1:8887",
|
||||||
},
|
},
|
||||||
API_PROD: {
|
API_PROD: {
|
||||||
seller: "https://admin-api.pickmall.cn",
|
seller: "https://admin-api.pickmall.cn",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
|
|
||||||
// 统一请求路径前缀在libs/axios.js中修改
|
// 统一请求路径前缀在libs/axios.js中修改
|
||||||
import {getRequest, postRequest, putRequest, deleteRequest, importRequest, getRequestWithNoToken} from '@/libs/axios';
|
import {getRequest, postRequest, postRequestWithNoForm, putRequest, deleteRequest, importRequest, getRequestWithNoToken} from '@/libs/axios';
|
||||||
|
|
||||||
|
|
||||||
//获取分销商品列表
|
//获取分销商品列表
|
||||||
@@ -18,22 +18,32 @@ export const distributionGoodsCheck = (id,param) => {
|
|||||||
return putRequest(`/distribution/goods/checked/${id}`,param)
|
return putRequest(`/distribution/goods/checked/${id}`,param)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 批量选择分销商品
|
||||||
|
export const distributionGoodsBatchCheck = (items) => {
|
||||||
|
return postRequestWithNoForm('/distribution/goods/checked/batch', { items })
|
||||||
|
}
|
||||||
|
|
||||||
//查询分销商
|
// 修改分销商品佣金
|
||||||
|
export const updateDistributionGoodsCommission = (id, params) => {
|
||||||
|
return putRequest(`/distribution/goods/${id}/commission`, params)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//查询分销员
|
||||||
export const getDistributionListData = (params) => {
|
export const getDistributionListData = (params) => {
|
||||||
return getRequest('/distribution/getByPage', params)
|
return getRequest('/distribution/getByPage', params)
|
||||||
}
|
}
|
||||||
//审核分销商
|
//审核分销员
|
||||||
export const auditDistribution = (id, params) => {
|
export const auditDistribution = (id, params) => {
|
||||||
return putRequest(`/distribution/audit/${id}`, params)
|
return putRequest(`/distribution/audit/${id}`, params)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 清退分销商
|
// 清退分销员
|
||||||
export const retreatDistribution = (id, params) => {
|
export const retreatDistribution = (id, params) => {
|
||||||
return putRequest(`/distribution/retreat/${id}`, params)
|
return putRequest(`/distribution/retreat/${id}`, params)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 恢复分销商
|
// 恢复分销员
|
||||||
export const resumeDistribution = (id, params) => {
|
export const resumeDistribution = (id, params) => {
|
||||||
return putRequest(`/distribution/resume/${id}`, params)
|
return putRequest(`/distribution/resume/${id}`, params)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// 分销商状态列表
|
// 分销员状态列表
|
||||||
export const distributionStatusList= [
|
export const distributionStatusList= [
|
||||||
{
|
{
|
||||||
value:'APPLY',
|
value:'APPLY',
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div class="search">
|
||||||
<el-card>
|
<el-card>
|
||||||
<el-form
|
<el-form
|
||||||
ref="searchForm"
|
ref="searchForm"
|
||||||
@@ -17,13 +17,59 @@
|
|||||||
style="width: 240px"
|
style="width: 240px"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<el-form-item label="商品品牌" prop="brandId">
|
||||||
|
<el-select
|
||||||
|
v-model="searchForm.brandId"
|
||||||
|
placeholder="请选择商品品牌"
|
||||||
|
clearable
|
||||||
|
filterable
|
||||||
|
style="width: 240px"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="item in brandList"
|
||||||
|
:key="item.id"
|
||||||
|
:label="item.name"
|
||||||
|
:value="item.id"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="店铺分类" prop="storeCategoryPath">
|
||||||
|
<el-cascader
|
||||||
|
v-model="searchForm.storeCategoryPath"
|
||||||
|
:options="shopCategoryList"
|
||||||
|
:props="shopCategoryProps"
|
||||||
|
clearable
|
||||||
|
filterable
|
||||||
|
placeholder="请选择店铺分类"
|
||||||
|
style="width: 240px"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="商品类型" prop="goodsType">
|
||||||
|
<el-select
|
||||||
|
v-model="searchForm.goodsType"
|
||||||
|
placeholder="请选择商品类型"
|
||||||
|
clearable
|
||||||
|
style="width: 240px"
|
||||||
|
>
|
||||||
|
<el-option label="实物商品" value="PHYSICAL_GOODS" />
|
||||||
|
<el-option label="虚拟商品" value="VIRTUAL_GOODS" />
|
||||||
|
<el-option label="电子卡券" value="E_COUPON" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-button type="primary" class="search-btn" @click="handleSearch">搜索</el-button>
|
<el-button type="primary" class="search-btn" @click="handleSearch">搜索</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</el-card>
|
</el-card>
|
||||||
|
|
||||||
<el-card>
|
<el-card class="list-card">
|
||||||
|
<el-tabs v-model="currentAuthStatus" class="auth-tabs" @tab-change="onAuthStatusChange">
|
||||||
|
<el-tab-pane label="全部" name="ALL" />
|
||||||
|
<el-tab-pane label="待审核" name="TOBEAUDITED" />
|
||||||
|
<el-tab-pane label="已审核" name="PASS" />
|
||||||
|
<el-tab-pane label="审核未通过" name="REFUSE" />
|
||||||
|
</el-tabs>
|
||||||
|
|
||||||
<div class="operation">
|
<div class="operation">
|
||||||
<el-button type="primary" @click="add">添加</el-button>
|
<el-button type="primary" @click="add">添加</el-button>
|
||||||
</div>
|
</div>
|
||||||
@@ -36,43 +82,33 @@
|
|||||||
class="mt_10"
|
class="mt_10"
|
||||||
style="width: 100%"
|
style="width: 100%"
|
||||||
>
|
>
|
||||||
<el-table-column label="商品名称" min-width="250">
|
<el-table-column prop="skuId" label="ID" min-width="180" show-overflow-tooltip />
|
||||||
|
<el-table-column label="商品图片" width="90" align="center">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<div v-if="row" class="goods-msg">
|
<img v-if="row?.thumbnail" :src="row.thumbnail" class="goods-thumb" alt="" />
|
||||||
<img :src="row.thumbnail" width="60" height="60" alt="" />
|
<span v-else>-</span>
|
||||||
<div>
|
|
||||||
<div class="div-zoom">
|
|
||||||
<a class="link-text" @click="linkTo(row.goodsId, row.skuId)">{{ row.goodsName }}</a>
|
|
||||||
</div>
|
|
||||||
<el-popover trigger="hover" title="扫码在手机中查看" placement="top" width="180">
|
|
||||||
<template #reference>
|
|
||||||
<img
|
|
||||||
src="../../assets/qrcode.svg"
|
|
||||||
class="hover-pointer"
|
|
||||||
width="20"
|
|
||||||
height="20"
|
|
||||||
alt="qrcode"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
<vue-qr
|
|
||||||
:text="wapLinkTo(row.goodsId, row.skuId)"
|
|
||||||
:margin="0"
|
|
||||||
color-dark="#000"
|
|
||||||
color-light="#fff"
|
|
||||||
:size="150"
|
|
||||||
/>
|
|
||||||
</el-popover>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="商品价格" min-width="110">
|
<el-table-column label="商品名称" min-width="180" class-name="goods-name-column">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<span v-if="row" class="goods-name-text">{{ row.goodsName }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="规格" min-width="140" show-overflow-tooltip>
|
||||||
|
<template #default="{ row }">
|
||||||
|
{{ row?.specs || "-" }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="商品类型" min-width="110">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<span v-if="row">{{ goodsTypeText(row.goodsType) }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="商品金额" min-width="110">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<span v-if="row" :style="{ color: $mainColor }">{{ $filters.unitPrice(row.price, "¥") }}</span>
|
<span v-if="row" :style="{ color: $mainColor }">{{ $filters.unitPrice(row.price, "¥") }}</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="quantity" label="库存" min-width="80" />
|
|
||||||
<el-table-column prop="createTime" label="添加时间" min-width="160" />
|
|
||||||
<el-table-column label="佣金金额" min-width="110">
|
<el-table-column label="佣金金额" min-width="110">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<span v-if="row" :style="{ color: $mainColor }">
|
<span v-if="row" :style="{ color: $mainColor }">
|
||||||
@@ -80,9 +116,20 @@
|
|||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="操作" min-width="100" align="center" fixed="right">
|
<el-table-column prop="quantity" label="库存" min-width="80" align="center" />
|
||||||
|
<el-table-column label="审核状态" min-width="110" align="center">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<a v-if="row" class="link-text" @click="remove(row)">删除</a>
|
<span v-if="row">{{ authStatusText(row.authStatus) }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="createTime" label="添加时间" min-width="160" />
|
||||||
|
<el-table-column label="操作" min-width="120" align="center" fixed="right">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<template v-if="row">
|
||||||
|
<a class="link-text" @click="edit(row)">编辑</a>
|
||||||
|
<span class="op-split">|</span>
|
||||||
|
<a class="link-text" @click="remove(row)">删除</a>
|
||||||
|
</template>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
@@ -91,7 +138,7 @@
|
|||||||
<el-pagination
|
<el-pagination
|
||||||
v-model:current-page="searchForm.pageNumber"
|
v-model:current-page="searchForm.pageNumber"
|
||||||
v-model:page-size="searchForm.pageSize"
|
v-model:page-size="searchForm.pageSize"
|
||||||
:page-sizes="[10, 20, 50]"
|
:page-sizes="[20, 50, 100]"
|
||||||
:total="total"
|
:total="total"
|
||||||
layout="total, sizes, prev, pager, next, jumper"
|
layout="total, sizes, prev, pager, next, jumper"
|
||||||
size="small"
|
size="small"
|
||||||
@@ -106,20 +153,88 @@
|
|||||||
<el-dialog
|
<el-dialog
|
||||||
v-model="modalVisible"
|
v-model="modalVisible"
|
||||||
:title="modalTitle"
|
:title="modalTitle"
|
||||||
width="500px"
|
width="900px"
|
||||||
:close-on-click-modal="false"
|
:close-on-click-modal="false"
|
||||||
append-to-body
|
append-to-body
|
||||||
|
class="add-distribution-goods-dialog"
|
||||||
>
|
>
|
||||||
<el-form ref="form" :model="form" label-width="100px" :rules="formValidate">
|
<el-table :data="selectedGoodsList" border max-height="420">
|
||||||
<el-form-item label="分销佣金" prop="commission">
|
<el-table-column label="商品图片" width="90" align="center">
|
||||||
<el-input v-model="form.commission" clearable placeholder="请输入分销佣金" />
|
<template #default="{ row }">
|
||||||
</el-form-item>
|
<img v-if="row?.thumbnail" :src="row.thumbnail" class="goods-thumb" alt="" />
|
||||||
</el-form>
|
<span v-else>-</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="商品名称" min-width="160" class-name="goods-name-column">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<span class="goods-name-text">{{ row.goodsName }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="规格" min-width="120" show-overflow-tooltip>
|
||||||
|
<template #default="{ row }">
|
||||||
|
{{ row.specs || "-" }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="商品价格" width="110" align="right">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<span :style="{ color: $mainColor }">{{ $filters.unitPrice(row.price, "¥") }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="分销佣金" width="160" align="center">
|
||||||
|
<template #default="{ row, $index }">
|
||||||
|
<el-input
|
||||||
|
v-model="row.commission"
|
||||||
|
clearable
|
||||||
|
placeholder="请输入佣金"
|
||||||
|
:class="{ 'commission-input-error': row.commissionError }"
|
||||||
|
@blur="validateRowCommission(row, $index)"
|
||||||
|
@input="row.commissionError = false"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="操作" width="80" align="center">
|
||||||
|
<template #default="{ $index }">
|
||||||
|
<a
|
||||||
|
v-if="selectedGoodsList.length > 1"
|
||||||
|
class="link-text"
|
||||||
|
@click="removeSelectedGoods($index)"
|
||||||
|
>移除</a>
|
||||||
|
<span v-else>-</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<el-button @click="modalVisible = false">取消</el-button>
|
<el-button @click="modalVisible = false">取消</el-button>
|
||||||
<el-button type="primary" :loading="submitLoading" @click="handleSubmit">提交</el-button>
|
<el-button type="primary" :loading="submitLoading" @click="handleSubmit">提交</el-button>
|
||||||
</template>
|
</template>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
|
||||||
|
<el-dialog
|
||||||
|
v-model="editVisible"
|
||||||
|
title="编辑分销商品"
|
||||||
|
width="480px"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
append-to-body
|
||||||
|
>
|
||||||
|
<el-form ref="editFormRef" :model="editForm" label-width="100px">
|
||||||
|
<el-form-item label="商品名称">
|
||||||
|
<span>{{ editForm.goodsName }}</span>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="分销佣金" prop="commission">
|
||||||
|
<el-input
|
||||||
|
v-model="editForm.commission"
|
||||||
|
clearable
|
||||||
|
placeholder="请输入分销佣金"
|
||||||
|
:class="{ 'commission-input-error': editForm.commissionError }"
|
||||||
|
@input="editForm.commissionError = false"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<el-button @click="editVisible = false">取消</el-button>
|
||||||
|
<el-button type="primary" :loading="editLoading" @click="handleEditSubmit">保存</el-button>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -127,75 +242,166 @@
|
|||||||
import {
|
import {
|
||||||
getDistributionGoods,
|
getDistributionGoods,
|
||||||
distributionGoodsCancel,
|
distributionGoodsCancel,
|
||||||
distributionGoodsCheck,
|
distributionGoodsBatchCheck,
|
||||||
|
updateDistributionGoodsCommission,
|
||||||
} from "@/api/distribution";
|
} from "@/api/distribution";
|
||||||
|
import { getBrandListData, getShopGoodsLabelListSeller } from "@/api/goods";
|
||||||
import liliDialog from "@/views/lili-dialog";
|
import liliDialog from "@/views/lili-dialog";
|
||||||
import vueQr from "vue-qr";
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "distributionGoods",
|
name: "distributionGoods",
|
||||||
components: { liliDialog, vueQr },
|
components: { liliDialog },
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
modalVisible: false,
|
modalVisible: false,
|
||||||
modalTitle: "添加分销商品",
|
modalTitle: "添加分销商品",
|
||||||
|
editVisible: false,
|
||||||
|
editLoading: false,
|
||||||
submitLoading: false,
|
submitLoading: false,
|
||||||
loading: true,
|
loading: true,
|
||||||
|
currentAuthStatus: "ALL",
|
||||||
searchForm: {
|
searchForm: {
|
||||||
pageNumber: 1,
|
pageNumber: 1,
|
||||||
pageSize: 10,
|
pageSize: 20,
|
||||||
sort: "createTime",
|
sort: "createTime",
|
||||||
order: "desc",
|
order: "desc",
|
||||||
|
goodsName: "",
|
||||||
|
brandId: "",
|
||||||
|
storeCategoryPath: "",
|
||||||
|
goodsType: "",
|
||||||
|
authStatus: "",
|
||||||
},
|
},
|
||||||
form: {
|
brandList: [],
|
||||||
commission: 1,
|
shopCategoryList: [],
|
||||||
|
shopCategoryProps: {
|
||||||
|
value: "value",
|
||||||
|
label: "label",
|
||||||
|
children: "children",
|
||||||
|
checkStrictly: true,
|
||||||
|
emitPath: false,
|
||||||
},
|
},
|
||||||
skuId: 0,
|
selectedGoodsList: [],
|
||||||
formValidate: {
|
editForm: {
|
||||||
commission: [
|
id: "",
|
||||||
{ required: true, message: "请输入大于1小于9999的合法佣金金额", trigger: "blur" },
|
goodsName: "",
|
||||||
{
|
commission: "",
|
||||||
pattern: /^[1-9]\d{0,3}(\.\d{1,2})?$/,
|
commissionError: false,
|
||||||
message: "请输入大于1小于9999的合法佣金金额",
|
|
||||||
trigger: "blur",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
|
commissionPattern: /^[1-9]\d{0,3}(\.\d{1,2})?$/,
|
||||||
data: [],
|
data: [],
|
||||||
total: 0,
|
total: 0,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
init() {
|
init() {
|
||||||
|
this.loadFilterOptions();
|
||||||
|
this.getDataList();
|
||||||
|
},
|
||||||
|
loadFilterOptions() {
|
||||||
|
getBrandListData().then((res) => {
|
||||||
|
this.brandList = Array.isArray(res) ? res : res?.result || [];
|
||||||
|
});
|
||||||
|
getShopGoodsLabelListSeller().then((res) => {
|
||||||
|
if (res?.success) {
|
||||||
|
this.shopCategoryList = this.formatShopCategory(res.result || []);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
formatShopCategory(list) {
|
||||||
|
return (list || []).map((item) => ({
|
||||||
|
value: item.id,
|
||||||
|
label: item.labelName,
|
||||||
|
children:
|
||||||
|
item.children && item.children.length
|
||||||
|
? this.formatShopCategory(item.children)
|
||||||
|
: undefined,
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
goodsTypeText(value) {
|
||||||
|
if (value === "PHYSICAL_GOODS") return "实物商品";
|
||||||
|
if (value === "VIRTUAL_GOODS") return "虚拟商品";
|
||||||
|
if (value === "E_COUPON") return "电子卡券";
|
||||||
|
return "-";
|
||||||
|
},
|
||||||
|
authStatusText(value) {
|
||||||
|
if (value === "TOBEAUDITED") return "待审核";
|
||||||
|
if (value === "REFUSE") return "审核未通过";
|
||||||
|
return "已审核";
|
||||||
|
},
|
||||||
|
onAuthStatusChange(name) {
|
||||||
|
this.searchForm.authStatus = name === "ALL" ? "" : name;
|
||||||
|
this.searchForm.pageNumber = 1;
|
||||||
this.getDataList();
|
this.getDataList();
|
||||||
},
|
},
|
||||||
add() {
|
add() {
|
||||||
this.$refs.liliDialog.goodsData = [];
|
this.$refs.liliDialog.goodsData = [];
|
||||||
this.$refs.liliDialog.open("goods", "single");
|
this.$refs.liliDialog.open("goods");
|
||||||
},
|
},
|
||||||
selectedGoodsData(selected) {
|
selectedGoodsData(selected) {
|
||||||
if (!selected?.length) return;
|
if (!selected?.length) return;
|
||||||
|
this.selectedGoodsList = selected.map((item) => ({
|
||||||
|
skuId: item.id,
|
||||||
|
goodsId: item.goodsId,
|
||||||
|
thumbnail: item.thumbnail,
|
||||||
|
goodsName: item.goodsName,
|
||||||
|
specs: item.simpleSpecs || "-",
|
||||||
|
price: item.price,
|
||||||
|
commission: "1",
|
||||||
|
commissionError: false,
|
||||||
|
}));
|
||||||
this.modalVisible = true;
|
this.modalVisible = true;
|
||||||
this.form.commission = 1;
|
this.modalTitle = `添加分销商品(已选${this.selectedGoodsList.length}项)`;
|
||||||
this.modalTitle = "添加分销商品";
|
},
|
||||||
this.skuId = selected[0].id;
|
validateRowCommission(row) {
|
||||||
|
const valid = this.commissionPattern.test(String(row.commission || "").trim());
|
||||||
|
row.commissionError = !valid;
|
||||||
|
return valid;
|
||||||
|
},
|
||||||
|
validateAllCommission() {
|
||||||
|
let valid = true;
|
||||||
|
this.selectedGoodsList.forEach((row) => {
|
||||||
|
if (!this.validateRowCommission(row)) {
|
||||||
|
valid = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return valid;
|
||||||
|
},
|
||||||
|
removeSelectedGoods(index) {
|
||||||
|
this.selectedGoodsList.splice(index, 1);
|
||||||
|
if (!this.selectedGoodsList.length) {
|
||||||
|
this.modalVisible = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.modalTitle = `添加分销商品(已选${this.selectedGoodsList.length}项)`;
|
||||||
},
|
},
|
||||||
handleSubmit() {
|
handleSubmit() {
|
||||||
this.$refs.form.validate((valid) => {
|
if (!this.selectedGoodsList.length) return;
|
||||||
if (!valid) return;
|
if (!this.validateAllCommission()) {
|
||||||
this.submitLoading = true;
|
this.$Message.error("请为所有商品填写大于1且小于9999的合法佣金金额");
|
||||||
distributionGoodsCheck(this.skuId, this.form)
|
return;
|
||||||
.then((res) => {
|
}
|
||||||
if (res?.success || res?.message === "success") {
|
this.submitLoading = true;
|
||||||
this.$Message.success("添加成功");
|
const items = this.selectedGoodsList.map((item) => ({
|
||||||
|
skuId: item.skuId,
|
||||||
|
commission: Number(item.commission),
|
||||||
|
}));
|
||||||
|
distributionGoodsBatchCheck(items)
|
||||||
|
.then((res) => {
|
||||||
|
if (res?.success || res?.message === "success") {
|
||||||
|
const addedCount = Array.isArray(res?.result) ? res.result.length : 0;
|
||||||
|
if (addedCount > 0) {
|
||||||
|
this.$Message.success(`成功添加${addedCount}个分销商品`);
|
||||||
|
} else {
|
||||||
|
this.$Message.info("所选商品均已是分销商品,已跳过");
|
||||||
}
|
}
|
||||||
this.modalVisible = false;
|
}
|
||||||
this.getDataList();
|
this.modalVisible = false;
|
||||||
})
|
this.selectedGoodsList = [];
|
||||||
.finally(() => {
|
this.getDataList();
|
||||||
this.submitLoading = false;
|
})
|
||||||
});
|
.finally(() => {
|
||||||
});
|
this.submitLoading = false;
|
||||||
|
});
|
||||||
},
|
},
|
||||||
changePage(v) {
|
changePage(v) {
|
||||||
this.searchForm.pageNumber = v;
|
this.searchForm.pageNumber = v;
|
||||||
@@ -243,6 +449,37 @@ export default {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
edit(row) {
|
||||||
|
this.editForm = {
|
||||||
|
id: row.id,
|
||||||
|
goodsName: row.goodsName,
|
||||||
|
commission: row.commission != null ? String(row.commission) : "1",
|
||||||
|
commissionError: false,
|
||||||
|
};
|
||||||
|
this.editVisible = true;
|
||||||
|
},
|
||||||
|
handleEditSubmit() {
|
||||||
|
const valid = this.commissionPattern.test(String(this.editForm.commission || "").trim());
|
||||||
|
this.editForm.commissionError = !valid;
|
||||||
|
if (!valid) {
|
||||||
|
this.$Message.error("请输入大于1且小于9999的合法佣金金额");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.editLoading = true;
|
||||||
|
updateDistributionGoodsCommission(this.editForm.id, {
|
||||||
|
commission: Number(this.editForm.commission),
|
||||||
|
})
|
||||||
|
.then((res) => {
|
||||||
|
if (res?.success || res?.message === "success") {
|
||||||
|
this.$Message.success("修改成功");
|
||||||
|
this.editVisible = false;
|
||||||
|
this.getDataList();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.editLoading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.init();
|
this.init();
|
||||||
@@ -255,15 +492,11 @@ export default {
|
|||||||
margin: 10px 0;
|
margin: 10px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.goods-msg {
|
.goods-thumb {
|
||||||
display: flex;
|
width: 48px;
|
||||||
align-items: flex-start;
|
height: 48px;
|
||||||
gap: 13px;
|
object-fit: contain;
|
||||||
padding: 5px 0;
|
vertical-align: middle;
|
||||||
|
|
||||||
img {
|
|
||||||
object-fit: contain;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.link-text {
|
.link-text {
|
||||||
@@ -272,19 +505,46 @@ export default {
|
|||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.div-zoom {
|
.op-split {
|
||||||
overflow: hidden;
|
margin: 0 6px;
|
||||||
text-overflow: ellipsis;
|
color: #dcdfe6;
|
||||||
white-space: nowrap;
|
|
||||||
margin-bottom: 4px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.hover-pointer {
|
.goods-name-text {
|
||||||
cursor: pointer;
|
white-space: normal;
|
||||||
vertical-align: middle;
|
word-break: break-word;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.goods-name-column .cell) {
|
||||||
|
white-space: normal;
|
||||||
|
word-break: break-word;
|
||||||
|
line-height: 1.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mt_10 {
|
.mt_10 {
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.list-card {
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-tabs {
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.commission-input-error {
|
||||||
|
:deep(.el-input__wrapper) {
|
||||||
|
box-shadow: 0 0 0 1px #f56c6c inset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-distribution-goods-dialog {
|
||||||
|
:deep(.goods-name-column .cell) {
|
||||||
|
white-space: normal;
|
||||||
|
word-break: break-word;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -17,10 +17,10 @@
|
|||||||
style="width: 240px"
|
style="width: 240px"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="分销商" prop="distributionName">
|
<el-form-item label="分销员" prop="distributionName">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="searchForm.distributionName"
|
v-model="searchForm.distributionName"
|
||||||
placeholder="请输入分销商名称"
|
placeholder="请输入分销员名称"
|
||||||
clearable
|
clearable
|
||||||
style="width: 240px"
|
style="width: 240px"
|
||||||
/>
|
/>
|
||||||
@@ -89,7 +89,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="distributionName" label="分销商" min-width="100" show-overflow-tooltip />
|
<el-table-column prop="distributionName" label="分销员" min-width="100" show-overflow-tooltip />
|
||||||
<el-table-column label="状态" min-width="90">
|
<el-table-column label="状态" min-width="90">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<el-tag v-if="row" :type="filterStatusTagType(row.distributionOrderStatus)">
|
<el-tag v-if="row" :type="filterStatusTagType(row.distributionOrderStatus)">
|
||||||
@@ -100,7 +100,7 @@
|
|||||||
<el-table-column label="佣金金额" min-width="100">
|
<el-table-column label="佣金金额" min-width="100">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<span v-if="row" :style="{ color: $mainColor }">
|
<span v-if="row" :style="{ color: $mainColor }">
|
||||||
{{ $filters.unitPrice(row.rebate, "¥") }}</span>
|
{{ $filters.unitPrice(row.directCommission ?? row.rebate, "¥") }}</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="createTime" label="创建时间" min-width="160" fixed="right" />
|
<el-table-column prop="createTime" label="创建时间" min-width="160" fixed="right" />
|
||||||
|
|||||||
@@ -111,18 +111,22 @@
|
|||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="商品名称" min-width="200" show-overflow-tooltip>
|
<el-table-column label="商品名称" min-width="220" class-name="goods-name-column">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<a class="link-text" @click="linkTo(row.id, row.skuId)">{{ row.goodsName }}</a>
|
<a
|
||||||
|
v-if="row"
|
||||||
|
class="link-text goods-name-text"
|
||||||
|
@click="linkTo(row.id, row.skuId)"
|
||||||
|
>{{ row.goodsName }}</a>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="价格" width="200">
|
<el-table-column label="价格" width="120">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<span :style="{ color: $mainColor }">{{ $filters.unitPrice(row.price, '¥') }}</span>
|
<span :style="{ color: $mainColor }">{{ $filters.unitPrice(row.price, '¥') }}</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="buyCount" label="销量" width="150" />
|
<el-table-column prop="buyCount" label="销量" width="90" />
|
||||||
<el-table-column prop="quantity" label="库存" width="150" />
|
<el-table-column prop="quantity" label="库存" width="90" />
|
||||||
<el-table-column label="销售模式" width="150">
|
<el-table-column label="销售模式" width="150">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<span v-if="row">{{ salesModelText(row.salesModel) }}</span>
|
<span v-if="row">{{ salesModelText(row.salesModel) }}</span>
|
||||||
@@ -465,4 +469,19 @@ export default {
|
|||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.goods-name-text {
|
||||||
|
white-space: normal;
|
||||||
|
word-break: break-word;
|
||||||
|
line-height: 1.5;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
:deep(.goods-name-column .cell) {
|
||||||
|
white-space: normal;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
.link-text {
|
||||||
|
color: #409eff;
|
||||||
|
cursor: pointer;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,78 +1,217 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="wrapper">
|
<div class="wrapper">
|
||||||
<div class="wap-content">
|
<div class="wap-content">
|
||||||
<div class="query-wrapper">
|
<div class="goods-filter-panel">
|
||||||
<div class="query-item">
|
<el-form :model="filterForm" inline label-width="72px" class="goods-filter-form" @keyup.enter="onSearchGoods">
|
||||||
<div>搜索范围</div>
|
<el-form-item label-width="0" class="filter-row-item">
|
||||||
<el-input
|
<div class="filter-row">
|
||||||
v-model="goodsParams.goodsName"
|
<div class="filter-row-field">
|
||||||
placeholder="商品名称"
|
<span class="filter-row-label">商品名称</span>
|
||||||
clearable
|
<el-input
|
||||||
style="width: 150px"
|
v-model="filterForm.goodsName"
|
||||||
@clear="onSearchGoods"
|
placeholder="请输入商品名称"
|
||||||
@keyup.enter="onSearchGoods"
|
clearable
|
||||||
/>
|
class="filter-control"
|
||||||
</div>
|
/>
|
||||||
<div class="query-item">
|
|
||||||
<el-cascader
|
|
||||||
v-model="category"
|
|
||||||
:options="skuList"
|
|
||||||
placeholder="请选择商品分类"
|
|
||||||
popper-class="goods-dialog-cascader-popper"
|
|
||||||
style="width: 250px"
|
|
||||||
clearable
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="query-item">
|
|
||||||
<el-button type="primary" @click="onSearchGoods">搜索</el-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<div class="wap-content-list">
|
|
||||||
<div
|
|
||||||
class="wap-content-item"
|
|
||||||
:class="{ active: item.selected }"
|
|
||||||
@click="checkedGoods(item, index)"
|
|
||||||
v-for="(item, index) in goodsData"
|
|
||||||
:key="index"
|
|
||||||
>
|
|
||||||
<div>
|
|
||||||
<img :src="item.thumbnail" alt="" />
|
|
||||||
</div>
|
</div>
|
||||||
<div class="wap-content-desc">
|
<div class="filter-row-field">
|
||||||
<div class="wap-content-desc-title">{{ item.goodsName }}</div>
|
<span class="filter-row-label">商品ID</span>
|
||||||
<div class="wap-sku">
|
<el-input
|
||||||
{{ item.goodsUnit }}
|
v-model="filterForm.goodsId"
|
||||||
<el-tag
|
placeholder="请输入商品ID"
|
||||||
style="margin-left: 10px"
|
clearable
|
||||||
:type="item.salesModel === 'RETAIL' ? 'info' : 'primary'"
|
class="filter-control"
|
||||||
>
|
/>
|
||||||
{{ item.salesModel === "RETAIL" ? "零售型" : "批发型" }}
|
</div>
|
||||||
</el-tag>
|
<div class="filter-row-field">
|
||||||
|
<span class="filter-row-label">商品类型</span>
|
||||||
|
<el-select v-model="filterForm.goodsType" placeholder="请选择" clearable class="filter-control">
|
||||||
|
<el-option label="实物商品" value="PHYSICAL_GOODS" />
|
||||||
|
<el-option label="虚拟商品" value="VIRTUAL_GOODS" />
|
||||||
|
<el-option label="电子卡券" value="E_COUPON" />
|
||||||
|
</el-select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label-width="0" class="filter-row-item">
|
||||||
|
<div class="filter-row">
|
||||||
|
<div class="filter-row-field">
|
||||||
|
<span class="filter-row-label">品牌</span>
|
||||||
|
<el-select
|
||||||
|
v-model="filterForm.brandId"
|
||||||
|
placeholder="请选择品牌"
|
||||||
|
clearable
|
||||||
|
filterable
|
||||||
|
class="filter-control"
|
||||||
|
>
|
||||||
|
<el-option v-for="item in brandList" :key="item.id" :label="item.name" :value="item.id" />
|
||||||
|
</el-select>
|
||||||
|
</div>
|
||||||
|
<div class="filter-row-field">
|
||||||
|
<span class="filter-row-label">店铺分类</span>
|
||||||
|
<el-cascader
|
||||||
|
v-model="filterForm.storeCategoryPath"
|
||||||
|
:options="shopCategoryList"
|
||||||
|
:props="shopCategoryProps"
|
||||||
|
placeholder="请选择店铺分类"
|
||||||
|
clearable
|
||||||
|
filterable
|
||||||
|
class="filter-control"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="filter-row-field">
|
||||||
|
<span class="filter-row-label">平台类目</span>
|
||||||
|
<el-cascader
|
||||||
|
v-model="filterForm.categoryPath"
|
||||||
|
:options="platformCategoryList"
|
||||||
|
:props="platformCategoryProps"
|
||||||
|
placeholder="请选择平台类目"
|
||||||
|
popper-class="goods-dialog-cascader-popper"
|
||||||
|
clearable
|
||||||
|
filterable
|
||||||
|
class="filter-control"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label-width="0" class="range-form-item range-filter-row-item">
|
||||||
|
<div class="range-filter-row">
|
||||||
|
<div class="range-group">
|
||||||
|
<span class="filter-row-label">价格</span>
|
||||||
|
<div class="range-group-inputs">
|
||||||
|
<el-input v-model="filterForm.priceMin" placeholder="≥" clearable />
|
||||||
|
<span class="range-sep">~</span>
|
||||||
|
<el-input v-model="filterForm.priceMax" placeholder="≤" clearable />
|
||||||
</div>
|
</div>
|
||||||
<div class="wap-content-desc-bottom">
|
</div>
|
||||||
<div>¥{{ $filters.unitPrice(item.price) }}</div>
|
<div class="range-group">
|
||||||
|
<span class="filter-row-label">销量</span>
|
||||||
|
<div class="range-group-inputs">
|
||||||
|
<el-input v-model="filterForm.salesMin" placeholder="≥" clearable />
|
||||||
|
<span class="range-sep">~</span>
|
||||||
|
<el-input v-model="filterForm.salesMax" placeholder="≤" clearable />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="range-group">
|
||||||
|
<span class="filter-row-label">库存</span>
|
||||||
|
<div class="range-group-inputs">
|
||||||
|
<el-input v-model="filterForm.stockMin" placeholder="≥" clearable />
|
||||||
|
<span class="range-sep">~</span>
|
||||||
|
<el-input v-model="filterForm.stockMax" placeholder="≤" clearable />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="loading" v-loading="loading" class="loading-mask" />
|
</el-form-item>
|
||||||
<div v-if="empty" class="empty">暂无商品信息</div>
|
<el-form-item>
|
||||||
</div>
|
<el-button type="primary" @click="onSearchGoods">搜索</el-button>
|
||||||
<el-pagination
|
<el-button @click="resetFilter">重置</el-button>
|
||||||
v-model:current-page="goodsParams.pageNumber"
|
</el-form-item>
|
||||||
class="pageration"
|
</el-form>
|
||||||
:total="total"
|
|
||||||
:page-size="goodsParams.pageSize"
|
|
||||||
layout="total, prev, pager, next"
|
|
||||||
size="small"
|
|
||||||
@current-change="changePageSize"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div ref="tableWrap" class="goods-table-wrap">
|
||||||
|
<el-table
|
||||||
|
ref="goodsTable"
|
||||||
|
v-loading="loading"
|
||||||
|
:data="goodsData"
|
||||||
|
border
|
||||||
|
:height="tableHeight"
|
||||||
|
:highlight-current-row="type !== 'multiple'"
|
||||||
|
class="goods-sku-table"
|
||||||
|
row-key="id"
|
||||||
|
@row-click="handleRowClick"
|
||||||
|
@selection-change="handleSelectionChange"
|
||||||
|
>
|
||||||
|
<el-table-column
|
||||||
|
v-if="type === 'multiple'"
|
||||||
|
type="selection"
|
||||||
|
width="48"
|
||||||
|
align="center"
|
||||||
|
:reserve-selection="true"
|
||||||
|
/>
|
||||||
|
<el-table-column prop="id" label="ID" min-width="150" show-overflow-tooltip />
|
||||||
|
<el-table-column label="商品图片" width="90" align="center">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<img v-if="row" :src="row.thumbnail" class="goods-thumb" alt="" />
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="商品名称" min-width="160" class-name="goods-name-column">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<span v-if="row" class="goods-name-text">{{ row.goodsName }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="规格" min-width="120" show-overflow-tooltip>
|
||||||
|
<template #default="{ row }">
|
||||||
|
{{ row?.simpleSpecs || "-" }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="商品类型" width="100" show-overflow-tooltip>
|
||||||
|
<template #default="{ row }">
|
||||||
|
{{ goodsTypeText(row?.goodsType) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="店铺分类" min-width="120" show-overflow-tooltip>
|
||||||
|
<template #default="{ row }">
|
||||||
|
{{ storeCategoryText(row?.storeCategoryPath) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="平台类目" min-width="120" show-overflow-tooltip>
|
||||||
|
<template #default="{ row }">
|
||||||
|
{{ platformCategoryText(row?.categoryPath) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="品牌" min-width="100" show-overflow-tooltip>
|
||||||
|
<template #default="{ row }">
|
||||||
|
{{ brandText(row?.brandId) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="价格" width="100" align="right">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<span v-if="row" class="goods-price">{{ $filters.unitPrice(row.price, "¥") }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="销量" width="80" align="center">
|
||||||
|
<template #default="{ row }">
|
||||||
|
{{ row?.buyCount ?? 0 }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="quantity" label="库存" width="80" align="center" />
|
||||||
|
<template #empty>
|
||||||
|
<div class="empty">{{ loading ? "" : "暂无商品信息" }}</div>
|
||||||
|
</template>
|
||||||
|
</el-table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-pagination
|
||||||
|
v-model:current-page="goodsParams.pageNumber"
|
||||||
|
class="pageration"
|
||||||
|
:total="total"
|
||||||
|
:page-size="goodsParams.pageSize"
|
||||||
|
layout="total, prev, pager, next"
|
||||||
|
size="small"
|
||||||
|
@current-change="changePageSize"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import * as API_Goods from "@/api/goods";
|
import * as API_Goods from "@/api/goods";
|
||||||
|
|
||||||
|
const defaultFilterForm = () => ({
|
||||||
|
goodsName: "",
|
||||||
|
goodsId: "",
|
||||||
|
storeCategoryPath: "",
|
||||||
|
categoryPath: "",
|
||||||
|
goodsType: "",
|
||||||
|
brandId: "",
|
||||||
|
priceMin: "",
|
||||||
|
priceMax: "",
|
||||||
|
salesMin: "",
|
||||||
|
salesMax: "",
|
||||||
|
stockMin: "",
|
||||||
|
stockMax: "",
|
||||||
|
});
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
selectedWay: {
|
selectedWay: {
|
||||||
@@ -84,29 +223,39 @@ export default {
|
|||||||
return {
|
return {
|
||||||
type: "multiple",
|
type: "multiple",
|
||||||
selectedList: [],
|
selectedList: [],
|
||||||
skuList: [],
|
platformCategoryList: [],
|
||||||
|
shopCategoryList: [],
|
||||||
|
brandList: [],
|
||||||
|
platformCategoryProps: {
|
||||||
|
value: "id",
|
||||||
|
label: "name",
|
||||||
|
children: "children",
|
||||||
|
checkStrictly: true,
|
||||||
|
emitPath: false,
|
||||||
|
},
|
||||||
|
shopCategoryProps: {
|
||||||
|
value: "value",
|
||||||
|
label: "label",
|
||||||
|
children: "children",
|
||||||
|
checkStrictly: true,
|
||||||
|
emitPath: false,
|
||||||
|
},
|
||||||
|
filterForm: defaultFilterForm(),
|
||||||
total: 0,
|
total: 0,
|
||||||
goodsParams: {
|
goodsParams: {
|
||||||
pageNumber: 1,
|
pageNumber: 1,
|
||||||
pageSize: 15,
|
pageSize: 20,
|
||||||
order: "desc",
|
order: "desc",
|
||||||
goodsName: "",
|
|
||||||
sn: "",
|
|
||||||
categoryPath: "",
|
|
||||||
marketEnable: "UPPER",
|
marketEnable: "UPPER",
|
||||||
authFlag: "PASS",
|
authFlag: "PASS",
|
||||||
sort: "createTime",
|
sort: "createTime",
|
||||||
},
|
},
|
||||||
category: [],
|
|
||||||
goodsData: [],
|
goodsData: [],
|
||||||
empty: false,
|
|
||||||
loading: false,
|
loading: false,
|
||||||
|
tableHeight: 400,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
category(val) {
|
|
||||||
this.goodsParams.categoryPath = val && val.length ? val.join(",") : "";
|
|
||||||
},
|
|
||||||
selectedWay: {
|
selectedWay: {
|
||||||
handler(val) {
|
handler(val) {
|
||||||
this.selectedList = Array.isArray(val) ? val.slice() : [];
|
this.selectedList = Array.isArray(val) ? val.slice() : [];
|
||||||
@@ -114,157 +263,366 @@ export default {
|
|||||||
deep: true,
|
deep: true,
|
||||||
immediate: true,
|
immediate: true,
|
||||||
},
|
},
|
||||||
"goodsParams.categoryPath": {
|
|
||||||
handler() {
|
|
||||||
this.goodsData = [];
|
|
||||||
this.goodsParams.pageNumber = 1;
|
|
||||||
this.getQueryGoodsList();
|
|
||||||
},
|
|
||||||
deep: true,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.init();
|
this.init();
|
||||||
|
this.$nextTick(this.calcTableHeight);
|
||||||
|
window.addEventListener("resize", this.calcTableHeight);
|
||||||
|
},
|
||||||
|
beforeUnmount() {
|
||||||
|
window.removeEventListener("resize", this.calcTableHeight);
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
calcTableHeight() {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
const wrap = this.$refs.tableWrap;
|
||||||
|
if (wrap) {
|
||||||
|
this.tableHeight = Math.max(wrap.clientHeight, 320);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
onSearchGoods() {
|
onSearchGoods() {
|
||||||
this.goodsData = [];
|
|
||||||
this.goodsParams.pageNumber = 1;
|
this.goodsParams.pageNumber = 1;
|
||||||
this.getQueryGoodsList();
|
this.getQueryGoodsList();
|
||||||
},
|
},
|
||||||
|
resetFilter() {
|
||||||
|
this.filterForm = defaultFilterForm();
|
||||||
|
this.onSearchGoods();
|
||||||
|
},
|
||||||
changePageSize(v) {
|
changePageSize(v) {
|
||||||
this.goodsParams.pageNumber = v;
|
this.goodsParams.pageNumber = v;
|
||||||
this.getQueryGoodsList();
|
this.getQueryGoodsList();
|
||||||
},
|
},
|
||||||
|
buildQueryParams() {
|
||||||
|
const params = {
|
||||||
|
...this.goodsParams,
|
||||||
|
goodsName: this.filterForm.goodsName || undefined,
|
||||||
|
goodsId: this.filterForm.goodsId || undefined,
|
||||||
|
goodsType: this.filterForm.goodsType || undefined,
|
||||||
|
brandId: this.filterForm.brandId || undefined,
|
||||||
|
storeCategoryPath: this.filterForm.storeCategoryPath || undefined,
|
||||||
|
categoryPath: this.filterForm.categoryPath || undefined,
|
||||||
|
geQuantity: undefined,
|
||||||
|
leQuantity: undefined,
|
||||||
|
geBuyCount: undefined,
|
||||||
|
leBuyCount: undefined,
|
||||||
|
price: undefined,
|
||||||
|
};
|
||||||
|
|
||||||
|
const { priceMin, priceMax, salesMin, salesMax, stockMin, stockMax } = this.filterForm;
|
||||||
|
if (priceMin !== "" && priceMax !== "") {
|
||||||
|
params.price = `${priceMin}_${priceMax}`;
|
||||||
|
} else if (priceMin !== "") {
|
||||||
|
params.price = String(priceMin);
|
||||||
|
} else if (priceMax !== "") {
|
||||||
|
params.price = `0_${priceMax}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stockMin !== "" && stockMin != null) params.geQuantity = Number(stockMin);
|
||||||
|
if (stockMax !== "" && stockMax != null) params.leQuantity = Number(stockMax);
|
||||||
|
if (salesMin !== "" && salesMin != null) params.geBuyCount = Number(salesMin);
|
||||||
|
if (salesMax !== "" && salesMax != null) params.leBuyCount = Number(salesMax);
|
||||||
|
|
||||||
|
return params;
|
||||||
|
},
|
||||||
getQueryGoodsList() {
|
getQueryGoodsList() {
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
API_Goods.getGoodsSkuData(this.goodsParams)
|
API_Goods.getGoodsSkuData(this.buildQueryParams())
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
this.initGoods(res);
|
this.initGoods(res);
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
|
this.calcTableHeight();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
initGoods(res) {
|
initGoods(res) {
|
||||||
const records = res?.result?.records || [];
|
const records = res?.result?.records || [];
|
||||||
if (records.length) {
|
this.total = res?.result?.total || 0;
|
||||||
records.forEach((item) => {
|
records.forEach((item) => {
|
||||||
item.selected = false;
|
item.selected = false;
|
||||||
item.___type = "goods";
|
item.___type = "goods";
|
||||||
this.selectedList.forEach((e) => {
|
this.selectedList.forEach((e) => {
|
||||||
if (e.id && e.id === item.id) {
|
if (e.id && e.id === item.id) {
|
||||||
item.selected = true;
|
item.selected = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
this.goodsData = records;
|
||||||
|
this.syncTableSelection();
|
||||||
|
},
|
||||||
|
syncTableSelection() {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
const table = this.$refs.goodsTable;
|
||||||
|
if (!table) return;
|
||||||
|
if (this.type === "multiple") {
|
||||||
|
table.clearSelection();
|
||||||
|
this.goodsData.forEach((row) => {
|
||||||
|
if (row.selected) {
|
||||||
|
table.toggleRowSelection(row, true);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
return;
|
||||||
this.total = res.result.total || 0;
|
}
|
||||||
this.goodsData = records;
|
const selected = this.goodsData.find((item) => item.selected);
|
||||||
this.empty = false;
|
table.setCurrentRow(selected || null);
|
||||||
} else {
|
});
|
||||||
this.goodsData = [];
|
|
||||||
this.empty = true;
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
emitSelected(list) {
|
emitSelected(list) {
|
||||||
this.selectedList = list;
|
this.selectedList = list;
|
||||||
this.$emit("selected", this.selectedList);
|
this.$emit("selected", this.selectedList);
|
||||||
},
|
},
|
||||||
|
handleRowClick(row) {
|
||||||
|
if (this.type === "multiple") return;
|
||||||
|
this.goodsData.forEach((item) => {
|
||||||
|
item.selected = false;
|
||||||
|
});
|
||||||
|
row.selected = true;
|
||||||
|
this.emitSelected([row]);
|
||||||
|
this.$refs.goodsTable?.setCurrentRow(row);
|
||||||
|
},
|
||||||
|
handleSelectionChange(selection) {
|
||||||
|
if (this.type !== "multiple") return;
|
||||||
|
const selectedIds = new Set(selection.map((item) => item.id));
|
||||||
|
this.goodsData.forEach((item) => {
|
||||||
|
item.selected = selectedIds.has(item.id);
|
||||||
|
});
|
||||||
|
const currentPageIds = new Set(this.goodsData.map((item) => item.id));
|
||||||
|
const otherPages = this.selectedList.filter((item) => !currentPageIds.has(item.id));
|
||||||
|
this.emitSelected([...otherPages, ...selection]);
|
||||||
|
},
|
||||||
|
formatShopCategory(list) {
|
||||||
|
return (list || []).map((item) => ({
|
||||||
|
value: item.id,
|
||||||
|
label: item.labelName,
|
||||||
|
children:
|
||||||
|
item.children && item.children.length ? this.formatShopCategory(item.children) : undefined,
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
goodsTypeText(value) {
|
||||||
|
if (value === "PHYSICAL_GOODS") return "实物商品";
|
||||||
|
if (value === "VIRTUAL_GOODS") return "虚拟商品";
|
||||||
|
if (value === "E_COUPON") return "电子卡券";
|
||||||
|
return "-";
|
||||||
|
},
|
||||||
|
brandText(brandId) {
|
||||||
|
if (!brandId) return "-";
|
||||||
|
const brand = this.brandList.find((item) => String(item.id) === String(brandId));
|
||||||
|
return brand?.name || "-";
|
||||||
|
},
|
||||||
|
findTreeNodeLabel(tree, targetId, valueKey, labelKey) {
|
||||||
|
for (const node of tree || []) {
|
||||||
|
if (String(node[valueKey]) === String(targetId)) {
|
||||||
|
return node[labelKey];
|
||||||
|
}
|
||||||
|
const childLabel = this.findTreeNodeLabel(node.children, targetId, valueKey, labelKey);
|
||||||
|
if (childLabel) return childLabel;
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
},
|
||||||
|
categoryPathText(path, tree, valueKey, labelKey) {
|
||||||
|
if (!path) return "-";
|
||||||
|
const ids = String(path).split(",").filter(Boolean);
|
||||||
|
if (!ids.length) return "-";
|
||||||
|
const labels = ids
|
||||||
|
.map((id) => this.findTreeNodeLabel(tree, id, valueKey, labelKey))
|
||||||
|
.filter(Boolean);
|
||||||
|
return labels.length ? labels.join(" / ") : "-";
|
||||||
|
},
|
||||||
|
storeCategoryText(path) {
|
||||||
|
return this.categoryPathText(path, this.shopCategoryList, "value", "label");
|
||||||
|
},
|
||||||
|
platformCategoryText(path) {
|
||||||
|
if (!path) return "-";
|
||||||
|
const ids = String(path).split(",").filter(Boolean);
|
||||||
|
if (!ids.length) return "-";
|
||||||
|
const targetId = ids.length >= 3 ? ids[2] : ids[ids.length - 1];
|
||||||
|
const label = this.findTreeNodeLabel(this.platformCategoryList, targetId, "id", "name");
|
||||||
|
return label || "-";
|
||||||
|
},
|
||||||
init() {
|
init() {
|
||||||
API_Goods.getGoodsSkuData(this.goodsParams).then((res) => {
|
this.getQueryGoodsList();
|
||||||
this.initGoods(res);
|
API_Goods.getCategoryTree().then((res) => {
|
||||||
});
|
if (res?.success) {
|
||||||
API_Goods.getGoodsCategoryAll(0).then((res) => {
|
this.platformCategoryList = res.result || [];
|
||||||
if (res.result) {
|
|
||||||
this.deepGroup(res.result);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
API_Goods.getShopGoodsLabelListSeller().then((res) => {
|
||||||
deepGroup(val) {
|
if (res?.success) {
|
||||||
val.forEach((item) => {
|
this.shopCategoryList = this.formatShopCategory(res.result || []);
|
||||||
let childWay = [];
|
|
||||||
if (item.children) {
|
|
||||||
item.children.forEach((child) => {
|
|
||||||
if (child.children) {
|
|
||||||
child.children.forEach((grandson, index, arr) => {
|
|
||||||
arr[index] = {
|
|
||||||
value: grandson.id,
|
|
||||||
label: grandson.name,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
childWay.push({
|
|
||||||
value: child.id,
|
|
||||||
label: child.name,
|
|
||||||
children: child.children,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
this.skuList.push({
|
|
||||||
value: item.id,
|
|
||||||
label: item.name,
|
|
||||||
children: childWay,
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
},
|
API_Goods.getBrandListData().then((res) => {
|
||||||
checkedGoods(val) {
|
this.brandList = Array.isArray(res) ? res : res?.result || [];
|
||||||
if (this.type !== "multiple") {
|
});
|
||||||
this.goodsData.forEach((item) => {
|
|
||||||
item.selected = false;
|
|
||||||
});
|
|
||||||
val.selected = true;
|
|
||||||
this.emitSelected([val]);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!val.selected) {
|
|
||||||
val.selected = true;
|
|
||||||
this.emitSelected([...this.selectedList, val]);
|
|
||||||
} else {
|
|
||||||
val.selected = false;
|
|
||||||
this.emitSelected(this.selectedList.filter((item) => item.id !== val.id));
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@import "./style.scss";
|
@import "./style.scss";
|
||||||
|
|
||||||
|
.wrapper {
|
||||||
|
height: 100%;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
.wap-content {
|
.wap-content {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
|
padding: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.goods-table-wrap {
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.goods-filter-panel {
|
||||||
|
background: #f5f7fa;
|
||||||
|
padding: 12px 16px 4px;
|
||||||
|
border-radius: 4px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.goods-filter-form {
|
||||||
|
margin-bottom: 0;
|
||||||
|
|
||||||
|
:deep(.el-form-item) {
|
||||||
|
margin-right: 8px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.range-form-item {
|
||||||
|
:deep(.el-form-item__content) {
|
||||||
|
margin-left: 0 !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.range-filter-row-item {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
:deep(.el-form-item__content) {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-row-item {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
:deep(.el-form-item__content) {
|
||||||
|
width: 100%;
|
||||||
|
margin-left: 0 !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-row {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
align-items: center;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-row-field {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
flex: 0 0 244px;
|
||||||
|
min-width: 244px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-control {
|
||||||
|
width: 160px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-row-label {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-end;
|
||||||
|
flex: 0 0 72px;
|
||||||
|
padding-right: 12px;
|
||||||
|
color: #606266;
|
||||||
|
font-size: 14px;
|
||||||
|
white-space: nowrap;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.range-filter-row {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
align-items: center;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.range-group {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
flex: 0 0 244px;
|
||||||
|
min-width: 244px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.range-group-inputs {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
:deep(.el-input) {
|
||||||
|
width: 72px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.range-sep {
|
||||||
|
margin: 0 4px;
|
||||||
|
color: #909399;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.goods-sku-table {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.goods-name-text {
|
||||||
|
white-space: normal;
|
||||||
|
word-break: break-word;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.goods-name-column .cell) {
|
||||||
|
white-space: normal;
|
||||||
|
word-break: break-word;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.goods-thumb {
|
||||||
|
width: 48px;
|
||||||
|
height: 48px;
|
||||||
|
object-fit: contain;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.goods-price {
|
||||||
|
color: $theme_color;
|
||||||
|
}
|
||||||
|
|
||||||
.empty {
|
.empty {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
padding: 8px 0;
|
padding: 24px 0;
|
||||||
width: 100%;
|
color: #909399;
|
||||||
}
|
|
||||||
.wap-content {
|
|
||||||
flex: 1;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
.wap-content-list {
|
|
||||||
display: flex;
|
|
||||||
position: relative;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
height: 340px;
|
|
||||||
}
|
|
||||||
.wap-content-item {
|
|
||||||
width: 210px;
|
|
||||||
margin: 10px 7px;
|
|
||||||
padding: 6px 0;
|
|
||||||
}
|
|
||||||
.active {
|
|
||||||
background: url("../../assets/selected.png") no-repeat;
|
|
||||||
background-position: right;
|
|
||||||
background-size: 10%;
|
|
||||||
}
|
|
||||||
.loading-mask {
|
|
||||||
position: absolute;
|
|
||||||
inset: 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.pageration {
|
.pageration {
|
||||||
margin-top: 12px;
|
margin-top: 12px;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
:deep(.el-table__body tr) {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.goods-dialog-cascader-popper {
|
.goods-dialog-cascader-popper {
|
||||||
|
|||||||
@@ -1,6 +1,33 @@
|
|||||||
<template>
|
<template>
|
||||||
|
<el-drawer
|
||||||
|
v-model="goodsVisible"
|
||||||
|
title="选择商品"
|
||||||
|
direction="rtl"
|
||||||
|
size="85%"
|
||||||
|
:z-index="10000"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
append-to-body
|
||||||
|
destroy-on-close
|
||||||
|
class="lili-goods-drawer"
|
||||||
|
>
|
||||||
|
<goodsDialog
|
||||||
|
v-if="goodsVisible"
|
||||||
|
ref="goodsDialog"
|
||||||
|
:selectedWay="goodsData"
|
||||||
|
@selected="
|
||||||
|
(val) => {
|
||||||
|
goodsData = val;
|
||||||
|
}
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
<template #footer>
|
||||||
|
<el-button @click="clickClose">取消</el-button>
|
||||||
|
<el-button type="primary" @click="clickOK">确定</el-button>
|
||||||
|
</template>
|
||||||
|
</el-drawer>
|
||||||
|
|
||||||
<el-dialog
|
<el-dialog
|
||||||
v-model="flag"
|
v-model="linkVisible"
|
||||||
width="1160px"
|
width="1160px"
|
||||||
top="120px"
|
top="120px"
|
||||||
:z-index="10000"
|
:z-index="10000"
|
||||||
@@ -9,27 +36,15 @@
|
|||||||
destroy-on-close
|
destroy-on-close
|
||||||
@close="clickClose"
|
@close="clickClose"
|
||||||
>
|
>
|
||||||
<template v-if="flag">
|
<linkDialog
|
||||||
<goodsDialog
|
v-if="linkVisible"
|
||||||
@selected="
|
class="linkDialog"
|
||||||
(val) => {
|
@selectedLink="
|
||||||
goodsData = val;
|
(val) => {
|
||||||
}
|
linkData = val;
|
||||||
"
|
}
|
||||||
v-if="goodsFlag"
|
"
|
||||||
ref="goodsDialog"
|
/>
|
||||||
:selectedWay="goodsData"
|
|
||||||
/>
|
|
||||||
<linkDialog
|
|
||||||
@selectedLink="
|
|
||||||
(val) => {
|
|
||||||
linkData = val;
|
|
||||||
}
|
|
||||||
"
|
|
||||||
v-else
|
|
||||||
class="linkDialog"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<el-button @click="clickClose">取消</el-button>
|
<el-button @click="clickClose">取消</el-button>
|
||||||
<el-button type="primary" @click="clickOK">确定</el-button>
|
<el-button type="primary" @click="clickOK">确定</el-button>
|
||||||
@@ -46,23 +61,44 @@ export default {
|
|||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
goodsVisible: false,
|
||||||
|
linkVisible: false,
|
||||||
goodsFlag: false,
|
goodsFlag: false,
|
||||||
goodsData: [],
|
goodsData: [],
|
||||||
linkData: "",
|
linkData: "",
|
||||||
flag: false,
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
flag: {
|
||||||
|
get() {
|
||||||
|
return this.goodsVisible || this.linkVisible;
|
||||||
|
},
|
||||||
|
set(val) {
|
||||||
|
if (val) {
|
||||||
|
if (this.goodsFlag) {
|
||||||
|
this.goodsVisible = true;
|
||||||
|
} else {
|
||||||
|
this.linkVisible = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.goodsVisible = false;
|
||||||
|
this.linkVisible = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
clearGoodsSelected() {
|
clearGoodsSelected() {
|
||||||
this.goodsData = [];
|
this.goodsData = [];
|
||||||
},
|
},
|
||||||
clickClose() {
|
clickClose() {
|
||||||
this.flag = false;
|
this.goodsVisible = false;
|
||||||
|
this.linkVisible = false;
|
||||||
this.goodsFlag = false;
|
this.goodsFlag = false;
|
||||||
this.$emit("closeFlag", false);
|
this.$emit("closeFlag", false);
|
||||||
},
|
},
|
||||||
singleGoods() {
|
singleGoods() {
|
||||||
var timer = setInterval(() => {
|
const timer = setInterval(() => {
|
||||||
if (this.$refs.goodsDialog) {
|
if (this.$refs.goodsDialog) {
|
||||||
this.$refs.goodsDialog.type = "single";
|
this.$refs.goodsDialog.type = "single";
|
||||||
clearInterval(timer);
|
clearInterval(timer);
|
||||||
@@ -70,34 +106,50 @@ export default {
|
|||||||
}, 100);
|
}, 100);
|
||||||
},
|
},
|
||||||
clickOK() {
|
clickOK() {
|
||||||
if (this.goodsFlag) {
|
if (this.goodsVisible) {
|
||||||
this.$emit("selectedGoodsData", this.goodsData);
|
this.$emit("selectedGoodsData", this.goodsData);
|
||||||
} else {
|
} else if (this.linkVisible) {
|
||||||
this.$emit("selectedLink", this.linkData);
|
this.$emit("selectedLink", this.linkData);
|
||||||
}
|
}
|
||||||
this.clickClose();
|
this.clickClose();
|
||||||
},
|
},
|
||||||
open(type, mutiple) {
|
open(type, mutiple) {
|
||||||
this.flag = true;
|
|
||||||
if (type == "goods") {
|
if (type == "goods") {
|
||||||
this.goodsFlag = true;
|
this.goodsFlag = true;
|
||||||
|
this.goodsVisible = true;
|
||||||
if (mutiple) {
|
if (mutiple) {
|
||||||
this.singleGoods();
|
this.singleGoods();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.goodsFlag = false;
|
this.goodsFlag = false;
|
||||||
|
this.linkVisible = true;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
close() {
|
close() {
|
||||||
this.flag = false;
|
this.clickClose();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
.lili-goods-drawer {
|
||||||
|
:deep(.el-drawer__body) {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 12px 20px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-drawer__footer) {
|
||||||
|
border-top: 1px solid #ebeef5;
|
||||||
|
padding: 12px 20px;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
:deep(.el-dialog__body) {
|
:deep(.el-dialog__body) {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 500px;
|
height: 640px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user