refactor(shop):优化分销商推荐关系模块

- 移除文章API中的MODULES_API_URL前缀,统一使用相对路径
- 调整MODULES_API_URL配置逻辑,支持本地存储自定义接口地址
-修复生产环境判断逻辑错误,确保自定义接口地址正确加载
- 新增下划线与驼峰命名转换工具函数
- 扩展ShopDealerReferee模型字段,增加推荐人和被推荐人的详细信息
- 新增推荐关系树状图展示组件RefereeTree.vue- 修改推荐关系列表页面布局,优化用户信息展示方式- 移除推荐层级筛选条件及部分冗余操作按钮
- 简化编辑弹窗标题及表单项,移除不必要的字段输入
- 调整表格列配置,优化推荐关系可视化展示效果
- 移除详情查看和解除推荐关系功能,简化操作流程
- 修复分页查询参数默认值处理问题,增强代码健壮性
This commit is contained in:
2025-10-21 20:52:50 +08:00
parent bf38e7e6d9
commit eebd164be4
11 changed files with 356 additions and 172 deletions

View File

@@ -1,14 +1,13 @@
import request from '@/utils/request'; import request from '@/utils/request';
import type {ApiResult, PageResult} from '@/api'; import type {ApiResult, PageResult} from '@/api';
import type {CmsArticle, CmsArticleParam} from './model'; import type {CmsArticle, CmsArticleParam} from './model';
import {MODULES_API_URL} from '@/config/setting';
/** /**
* 分页查询文章 * 分页查询文章
*/ */
export async function pageCmsArticle(params: CmsArticleParam) { export async function pageCmsArticle(params: CmsArticleParam) {
const res = await request.get<ApiResult<PageResult<CmsArticle>>>( const res = await request.get<ApiResult<PageResult<CmsArticle>>>(
MODULES_API_URL + '/cms/cms-article/page', '/cms/cms-article/page',
{ {
params params
} }
@@ -24,7 +23,7 @@ export async function pageCmsArticle(params: CmsArticleParam) {
*/ */
export async function listCmsArticle(params?: CmsArticleParam) { export async function listCmsArticle(params?: CmsArticleParam) {
const res = await request.get<ApiResult<CmsArticle[]>>( const res = await request.get<ApiResult<CmsArticle[]>>(
MODULES_API_URL + '/cms/cms-article', '/cms/cms-article',
{ {
params params
} }
@@ -40,7 +39,7 @@ export async function listCmsArticle(params?: CmsArticleParam) {
*/ */
export async function addCmsArticle(data: CmsArticle) { export async function addCmsArticle(data: CmsArticle) {
const res = await request.post<ApiResult<unknown>>( const res = await request.post<ApiResult<unknown>>(
MODULES_API_URL + '/cms/cms-article', '/cms/cms-article',
data data
); );
if (res.data.code === 0) { if (res.data.code === 0) {
@@ -54,7 +53,7 @@ export async function addCmsArticle(data: CmsArticle) {
*/ */
export async function updateCmsArticle(data: CmsArticle) { export async function updateCmsArticle(data: CmsArticle) {
const res = await request.put<ApiResult<unknown>>( const res = await request.put<ApiResult<unknown>>(
MODULES_API_URL + '/cms/cms-article', '/cms/cms-article',
data data
); );
if (res.data.code === 0) { if (res.data.code === 0) {
@@ -68,7 +67,7 @@ export async function updateCmsArticle(data: CmsArticle) {
*/ */
export async function updateBatchCmsArticle(data: any) { export async function updateBatchCmsArticle(data: any) {
const res = await request.put<ApiResult<unknown>>( const res = await request.put<ApiResult<unknown>>(
MODULES_API_URL + '/cms/cms-article/batch', '/cms/cms-article/batch',
data data
); );
if (res.data.code === 0) { if (res.data.code === 0) {
@@ -82,7 +81,7 @@ export async function updateBatchCmsArticle(data: any) {
*/ */
export async function removeCmsArticle(id?: number) { export async function removeCmsArticle(id?: number) {
const res = await request.delete<ApiResult<unknown>>( const res = await request.delete<ApiResult<unknown>>(
MODULES_API_URL + '/cms/cms-article/' + id '/cms/cms-article/' + id
); );
if (res.data.code === 0) { if (res.data.code === 0) {
return res.data.message; return res.data.message;
@@ -95,7 +94,7 @@ export async function removeCmsArticle(id?: number) {
*/ */
export async function removeBatchCmsArticle(data: (number | undefined)[]) { export async function removeBatchCmsArticle(data: (number | undefined)[]) {
const res = await request.delete<ApiResult<unknown>>( const res = await request.delete<ApiResult<unknown>>(
MODULES_API_URL + '/cms/cms-article/batch', '/cms/cms-article/batch',
{ {
data data
} }
@@ -111,7 +110,7 @@ export async function removeBatchCmsArticle(data: (number | undefined)[]) {
*/ */
export async function getCmsArticle(id: number) { export async function getCmsArticle(id: number) {
const res = await request.get<ApiResult<CmsArticle>>( const res = await request.get<ApiResult<CmsArticle>>(
MODULES_API_URL + '/cms/cms-article/' + id '/cms/cms-article/' + id
); );
if (res.data.code === 0 && res.data.data) { if (res.data.code === 0 && res.data.data) {
return res.data.data; return res.data.data;
@@ -133,7 +132,7 @@ export async function getByCode(code: string) {
} }
export async function getCount(params: CmsArticleParam) { export async function getCount(params: CmsArticleParam) {
const res = await request.get(MODULES_API_URL + '/cms/cms-article/data', { const res = await request.get('/cms/cms-article/data', {
params params
}); });
if (res.data.code === 0) { if (res.data.code === 0) {
@@ -150,7 +149,7 @@ export async function importArticles(file: File) {
const formData = new FormData(); const formData = new FormData();
formData.append('file', file); formData.append('file', file);
const res = await request.post<ApiResult<unknown>>( const res = await request.post<ApiResult<unknown>>(
MODULES_API_URL + '/cms/cms-article/import', '/cms/cms-article/import',
formData formData
); );
if (res.data.code === 0) { if (res.data.code === 0) {

View File

@@ -8,8 +8,22 @@ export interface ShopDealerReferee {
id?: number; id?: number;
// 分销商用户ID // 分销商用户ID
dealerId?: number; dealerId?: number;
// 分销商名称
dealerName?: string;
// 分销商头像
dealerAvatar?: string;
// 分销商手机号
dealerPhone?: string;
// 用户id(被推荐人) // 用户id(被推荐人)
userId?: number; userId?: number;
// 昵称
nickname?: string;
// 头像
avatar?: string;
// 别名
alias?: string;
// 手机号
phone?: string;
// 推荐关系层级(1,2,3) // 推荐关系层级(1,2,3)
level?: number; level?: number;
// 商城ID // 商城ID

View File

@@ -9,7 +9,7 @@ export const domain = import.meta.env.VITE_DOMAIN || 'https://your-domain.com';
// 主节点 // 主节点
export const SERVER_API_URL = import.meta.env.VITE_SERVER_API_URL || 'https://your-api.com/api'; export const SERVER_API_URL = import.meta.env.VITE_SERVER_API_URL || 'https://your-api.com/api';
// 模块节点 // 模块节点
export const MODULES_API_URL = import.meta.env.VITE_API_URL; export const MODULES_API_URL = localStorage.getItem('ApiUrl') || import.meta.env.VITE_API_URL;
// 文件服务器地址 // 文件服务器地址
export const FILE_SERVER = import.meta.env.VITE_FILE_SERVER || 'https://your-file-server.com'; export const FILE_SERVER = import.meta.env.VITE_FILE_SERVER || 'https://your-file-server.com';

View File

@@ -246,7 +246,7 @@ const reload = () => {
}); });
} }
// 检查是否启动自定义接口 // 检查是否启动自定义接口
if(import.meta.env.PROD){ if(!import.meta.env.PROD){
getCmsWebsiteFieldByCode('ApiUrl').then(res => { getCmsWebsiteFieldByCode('ApiUrl').then(res => {
if(res){ if(res){
localStorage.setItem('ApiUrl', `${res.value}`); localStorage.setItem('ApiUrl', `${res.value}`);

View File

@@ -563,3 +563,31 @@ export const getTokenBySpm = () => {
return `${token}`; return `${token}`;
} }
}; };
/**
* 下划线转驼峰命名
*/
export function toCamelCase(str: string): string {
return str.replace(/_([a-z])/g, function (_, letter) {
return letter.toUpperCase();
});
}
/**
* 下划线转大驼峰命名
*/
export function toCamelCaseUpper(str: string): string {
return toCamelCase(str).replace(/^[a-z]/, function (letter) {
return letter.toUpperCase();
});
}
/**
* 转为短下划线
*/
export function toShortUnderline(str: string): string {
return str.replace(/[A-Z]/g, function (letter) {
return '_' + letter.toLowerCase();
}).replace(/^_/, '');
}

View File

@@ -4,7 +4,6 @@
<a-tag>{{ website?.appName }}</a-tag> <a-tag>{{ website?.appName }}</a-tag>
</a-descriptions> </a-descriptions>
<a-descriptions-item label="后台管理"> <a-descriptions-item label="后台管理">
{{ website.apiUrl }}
<a-tag>https://mp.websoft.top</a-tag> <a-tag>https://mp.websoft.top</a-tag>
</a-descriptions-item> </a-descriptions-item>
<a-descriptions-item label="API"> <a-descriptions-item label="API">

View File

@@ -0,0 +1,205 @@
<template>
<a-modal
:visible="visible"
:title="title"
:width="1000"
:footer="null"
@cancel="handleCancel"
>
<div class="tree-container">
<v-chart
ref="chartRef"
class="chart"
:option="chartOption"
:loading="loading"
autoresize
/>
</div>
</a-modal>
</template>
<script lang="ts" setup>
import { ref, watch, computed } from 'vue';
import { use } from 'echarts/core';
import { CanvasRenderer } from 'echarts/renderers';
import { TreeChart } from 'echarts/charts';
import {
TooltipComponent,
TitleComponent
} from 'echarts/components';
import VChart from 'vue-echarts';
import type { ShopDealerReferee } from '@/api/shop/shopDealerReferee/model';
// 注册 echarts 组件
use([
CanvasRenderer,
TreeChart,
TooltipComponent,
TitleComponent
]);
// 定义组件属性
const props = withDefaults(defineProps<{
visible: boolean;
data?: ShopDealerReferee[];
title?: string;
}>(), {
visible: false,
data: () => [],
title: '推荐关系树'
});
// 定义事件
const emit = defineEmits<{
(e: 'update:visible', value: boolean): void;
(e: 'cancel'): void;
}>();
// 图表引用
const chartRef = ref<InstanceType<typeof VChart> | null>(null);
// 加载状态
const loading = ref(false);
// 处理取消事件
const handleCancel = () => {
emit('update:visible', false);
emit('cancel');
};
// 转换数据为树形结构
const transformToTreeData = (data: ShopDealerReferee[]) => {
if (!data || data.length === 0) {
return { name: '暂无数据', children: [] };
}
// 构建节点映射
const nodeMap = new Map<number, any>();
const rootNodes: any[] = [];
// 创建所有节点
data.forEach(item => {
// 推荐人节点
if (item.dealerId && !nodeMap.has(item.dealerId)) {
nodeMap.set(item.dealerId, {
id: item.dealerId,
name: `推荐人\nID:${item.dealerId}\n${item.dealerName || ''}`,
level: 'dealer',
children: []
});
}
// 被推荐人节点
if (item.userId && !nodeMap.has(item.userId)) {
nodeMap.set(item.userId, {
id: item.userId,
name: `被推荐人\nID:${item.userId}\n${item.nickname || ''}`,
level: 'user',
children: []
});
}
});
// 构建关系树
data.forEach(item => {
if (!item.dealerId || !item.userId) return;
const dealerNode = nodeMap.get(item.dealerId);
const userNode = nodeMap.get(item.userId);
if (dealerNode && userNode) {
// 添加层级标签
const levelText = { 1: '一级', 2: '二级', 3: '三级' }[item.level || 0] || `${item.level || 0}`;
userNode.name += `\n${levelText}推荐`;
dealerNode.children.push(userNode);
}
});
// 查找根节点(没有被推荐关系的节点)
const referencedIds = new Set(data.map(item => item.userId).filter(id => id));
data.forEach(item => {
if (item.dealerId && !referencedIds.has(item.dealerId)) {
const rootNode = nodeMap.get(item.dealerId);
if (rootNode) {
rootNodes.push(rootNode);
}
}
});
// 如果没有明确的根节点,使用第一个节点作为根
if (rootNodes.length === 0 && data.length > 0 && data[0].dealerId) {
const firstNode = nodeMap.get(data[0].dealerId);
if (firstNode) {
rootNodes.push(firstNode);
}
}
// 如果还是没有根节点,返回默认节点
if (rootNodes.length === 0) {
return { name: '暂无数据', children: [] };
}
return rootNodes[0];
};
// 图表配置
const chartOption = computed(() => {
const treeData = transformToTreeData(props.data || []);
return {
tooltip: {
trigger: 'item',
triggerOn: 'mousemove'
},
series: [
{
type: 'tree',
data: [treeData],
top: '1%',
left: '7%',
bottom: '1%',
right: '20%',
symbolSize: 12,
symbol: 'circle',
orient: 'LR', // 从左到右
expandAndCollapse: true,
label: {
position: 'left',
verticalAlign: 'middle',
align: 'right',
fontSize: 12,
backgroundColor: '#fff',
padding: [2, 4],
borderRadius: 4,
borderWidth: 1,
borderColor: '#ccc'
},
leaves: {
label: {
position: 'right',
verticalAlign: 'middle',
align: 'left'
}
},
emphasis: {
focus: 'descendant'
},
animationDuration: 500,
animationDurationUpdate: 750
}
]
};
});
</script>
<style lang="less" scoped>
.tree-container {
height: 600px;
width: 100%;
}
.chart {
height: 100%;
width: 100%;
}
</style>

View File

@@ -1,6 +1,5 @@
<!-- 搜索表单 --> <!-- 搜索表单 -->
<template> <template>
<div class="search-container">
<!-- 搜索表单 --> <!-- 搜索表单 -->
<a-form <a-form
:model="searchForm" :model="searchForm"
@@ -26,19 +25,6 @@
/> />
</a-form-item> </a-form-item>
<a-form-item label="推荐层级">
<a-select
v-model:value="searchForm.level"
placeholder="全部层级"
allow-clear
style="width: 120px"
>
<a-select-option :value="1">一级推荐</a-select-option>
<a-select-option :value="2">二级推荐</a-select-option>
<a-select-option :value="3">三级推荐</a-select-option>
</a-select>
</a-form-item>
<a-form-item label="建立时间"> <a-form-item label="建立时间">
<a-range-picker <a-range-picker
v-model:value="searchForm.dateRange" v-model:value="searchForm.dateRange"
@@ -62,29 +48,28 @@
</a-form> </a-form>
<!-- 操作按钮 --> <!-- 操作按钮 -->
<div class="action-buttons"> <!-- <div class="action-buttons">-->
<a-space> <!-- <a-space>-->
<a-button type="primary" @click="add" class="ele-btn-icon"> <!-- <a-button type="primary" @click="add" class="ele-btn-icon">-->
<template #icon> <!-- <template #icon>-->
<PlusOutlined/> <!-- <PlusOutlined/>-->
</template> <!-- </template>-->
建立推荐关系 <!-- 建立推荐关系-->
</a-button> <!-- </a-button>-->
<a-button @click="viewTree" class="ele-btn-icon"> <!-- <a-button @click="viewTree" class="ele-btn-icon">-->
<template #icon> <!-- <template #icon>-->
<ApartmentOutlined/> <!-- <ApartmentOutlined/>-->
</template> <!-- </template>-->
推荐关系树 <!-- 推荐关系树-->
</a-button> <!-- </a-button>-->
<a-button @click="exportData" class="ele-btn-icon"> <!-- <a-button @click="exportData" class="ele-btn-icon">-->
<template #icon> <!-- <template #icon>-->
<ExportOutlined/> <!-- <ExportOutlined/>-->
</template> <!-- </template>-->
导出数据 <!-- 导出数据-->
</a-button> <!-- </a-button>-->
</a-space> <!-- </a-space>-->
</div> <!-- </div>-->
</div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>

View File

@@ -5,7 +5,7 @@
:visible="visible" :visible="visible"
:maskClosable="false" :maskClosable="false"
:maxable="maxable" :maxable="maxable"
:title="isUpdate ? '编辑分销商推荐关系表' : '添加分销商推荐关系'" :title="isUpdate ? '编辑推荐' : '添加推荐关系'"
:body-style="{ paddingBottom: '28px' }" :body-style="{ paddingBottom: '28px' }"
@update:visible="updateVisible" @update:visible="updateVisible"
@ok="save" @ok="save"
@@ -19,34 +19,20 @@
styleResponsive ? { md: 19, sm: 19, xs: 24 } : { flex: '1' } styleResponsive ? { md: 19, sm: 19, xs: 24 } : { flex: '1' }
" "
> >
<a-form-item label="分销商用户ID" name="dealerId"> <a-form-item label="推荐人信息" name="dealerId">
<a-input <a-input
allow-clear allow-clear
placeholder="请输入分销商用户ID" placeholder="请输入分销商用户ID"
v-model:value="form.dealerId" v-model:value="form.dealerId"
/> />
</a-form-item> </a-form-item>
<a-form-item label="用户id(被推荐人)" name="userId"> <a-form-item label="被推荐人信息" name="userId">
<a-input <a-input
allow-clear allow-clear
placeholder="请输入用户id(被推荐人)" placeholder="请输入用户id(被推荐人)"
v-model:value="form.userId" v-model:value="form.userId"
/> />
</a-form-item> </a-form-item>
<a-form-item label="推荐关系层级(1,2,3)" name="level">
<a-input
allow-clear
placeholder="请输入推荐关系层级(1,2,3)"
v-model:value="form.level"
/>
</a-form-item>
<a-form-item label="修改时间" name="updateTime">
<a-input
allow-clear
placeholder="请输入修改时间"
v-model:value="form.updateTime"
/>
</a-form-item>
</a-form> </a-form>
</ele-modal> </ele-modal>
</template> </template>
@@ -54,14 +40,13 @@
<script lang="ts" setup> <script lang="ts" setup>
import { ref, reactive, watch } from 'vue'; import { ref, reactive, watch } from 'vue';
import { Form, message } from 'ant-design-vue'; import { Form, message } from 'ant-design-vue';
import { assignObject, uuid } from 'ele-admin-pro'; import { assignObject } from 'ele-admin-pro';
import { addShopDealerReferee, updateShopDealerReferee } from '@/api/shop/shopDealerReferee'; import { addShopDealerReferee, updateShopDealerReferee } from '@/api/shop/shopDealerReferee';
import { ShopDealerReferee } from '@/api/shop/shopDealerReferee/model'; import { ShopDealerReferee } from '@/api/shop/shopDealerReferee/model';
import { useThemeStore } from '@/store/modules/theme'; import { useThemeStore } from '@/store/modules/theme';
import { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
import { ItemType } from 'ele-admin-pro/es/ele-image-upload/types'; import { ItemType } from 'ele-admin-pro/es/ele-image-upload/types';
import { FormInstance } from 'ant-design-vue/es/form'; import { FormInstance } from 'ant-design-vue/es/form';
import { FileRecord } from '@/api/system/file/model';
// 是否是修改 // 是否是修改
const isUpdate = ref(false); const isUpdate = ref(false);
@@ -98,10 +83,7 @@
level: undefined, level: undefined,
tenantId: undefined, tenantId: undefined,
createTime: undefined, createTime: undefined,
updateTime: undefined, updateTime: undefined
status: 0,
comments: '',
sortNumber: 100
}); });
/* 更新visible */ /* 更新visible */
@@ -121,20 +103,6 @@
] ]
}); });
const chooseImage = (data: FileRecord) => {
images.value.push({
uid: data.id,
url: data.path,
status: 'done'
});
form.image = data.path;
};
const onDeleteItem = (index: number) => {
images.value.splice(index, 1);
form.image = '';
};
const { resetFields } = useForm(form, rules); const { resetFields } = useForm(form, rules);
/* 保存编辑 */ /* 保存编辑 */

View File

@@ -3,7 +3,7 @@
<a-card :bordered="false" :body-style="{ padding: '16px' }"> <a-card :bordered="false" :body-style="{ padding: '16px' }">
<ele-pro-table <ele-pro-table
ref="tableRef" ref="tableRef"
row-key="shopDealerRefereeId" row-key="id"
:columns="columns" :columns="columns"
:datasource="datasource" :datasource="datasource"
:customRow="customRow" :customRow="customRow"
@@ -21,16 +21,21 @@
<template #bodyCell="{ column, record }"> <template #bodyCell="{ column, record }">
<template v-if="column.key === 'dealerInfo'"> <template v-if="column.key === 'dealerInfo'">
<div class="user-info"> <div class="user-info">
<div class="user-id">ID: {{ record.dealerId }}</div> <div class="user-id">{{ record.dealerName }}({{ record.dealerId }})</div>
<div class="user-id"></div>
<div class="user-role"> <div class="user-role">
<a-tag color="blue">推荐人</a-tag> <a-tag color="blue">推荐人</a-tag>
</div> </div>
</div> </div>
</template> </template>
<template v-if="column.key === 'relationship'">
<ArrowRightOutlined />
</template>
<template v-if="column.key === 'userInfo'"> <template v-if="column.key === 'userInfo'">
<div class="user-info"> <div class="user-info">
<div class="user-id">ID: {{ record.userId }}</div> <div class="user-id">{{ record.nickname }}({{ record.userId }})</div>
<div class="user-role"> <div class="user-role">
<a-tag color="green">被推荐人</a-tag> <a-tag color="green">被推荐人</a-tag>
</div> </div>
@@ -39,10 +44,10 @@
<template v-if="column.key === 'level'"> <template v-if="column.key === 'level'">
<a-tag <a-tag
:color="getLevelColor(record.level)" :color="getLevelColor(record.level || 0)"
class="level-tag" class="level-tag"
> >
{{ getLevelText(record.level) }} {{ getLevelText(record.level || 0) }}
</a-tag> </a-tag>
</template> </template>
@@ -59,23 +64,19 @@
<template v-if="column.key === 'action'"> <template v-if="column.key === 'action'">
<a-space> <a-space>
<a @click="viewDetail(record)" class="ele-text-info">
<EyeOutlined /> 详情
</a>
<a-divider type="vertical" />
<a @click="openEdit(record)" class="ele-text-primary"> <a @click="openEdit(record)" class="ele-text-primary">
<EditOutlined /> 编辑 <EditOutlined /> 编辑
</a> </a>
<a-divider type="vertical" /> <!-- <a-divider type="vertical" />-->
<a-popconfirm <!-- <a-popconfirm-->
title="确定要解除此推荐关系吗?" <!-- title="确定要解除此推荐关系吗?"-->
@confirm="remove(record)" <!-- @confirm="remove(record)"-->
placement="topRight" <!-- placement="topRight"-->
> <!-- >-->
<a class="ele-text-danger"> <!-- <a class="ele-text-danger">-->
<DisconnectOutlined /> 解除 <!-- <DisconnectOutlined /> 解除-->
</a> <!-- </a>-->
</a-popconfirm> <!-- </a-popconfirm>-->
</a-space> </a-space>
</template> </template>
</template> </template>
@@ -84,6 +85,14 @@
<!-- 编辑弹窗 --> <!-- 编辑弹窗 -->
<ShopDealerRefereeEdit v-model:visible="showEdit" :data="current" @done="reload" /> <ShopDealerRefereeEdit v-model:visible="showEdit" :data="current" @done="reload" />
<!-- 树状图弹窗 -->
<RefereeTree
v-model:visible="showTree"
:data="treeData"
:title="'推荐关系树'"
@cancel="showTree = false"
/>
</a-page-header> </a-page-header>
</template> </template>
@@ -93,9 +102,8 @@
import { import {
ExclamationCircleOutlined, ExclamationCircleOutlined,
TeamOutlined, TeamOutlined,
EyeOutlined,
EditOutlined, EditOutlined,
DisconnectOutlined ArrowRightOutlined
} from '@ant-design/icons-vue'; } from '@ant-design/icons-vue';
import type { EleProTable } from 'ele-admin-pro'; import type { EleProTable } from 'ele-admin-pro';
import { toDateString } from 'ele-admin-pro'; import { toDateString } from 'ele-admin-pro';
@@ -106,6 +114,7 @@
import Search from './components/search.vue'; import Search from './components/search.vue';
import {getPageTitle} from '@/utils/common'; import {getPageTitle} from '@/utils/common';
import ShopDealerRefereeEdit from './components/shopDealerRefereeEdit.vue'; import ShopDealerRefereeEdit from './components/shopDealerRefereeEdit.vue';
import RefereeTree from './components/RefereeTree.vue';
import { pageShopDealerReferee, removeShopDealerReferee, removeBatchShopDealerReferee } from '@/api/shop/shopDealerReferee'; import { pageShopDealerReferee, removeShopDealerReferee, removeBatchShopDealerReferee } from '@/api/shop/shopDealerReferee';
import type { ShopDealerReferee, ShopDealerRefereeParam } from '@/api/shop/shopDealerReferee/model'; import type { ShopDealerReferee, ShopDealerRefereeParam } from '@/api/shop/shopDealerReferee/model';
@@ -118,8 +127,10 @@
const current = ref<ShopDealerReferee | null>(null); const current = ref<ShopDealerReferee | null>(null);
// 是否显示编辑弹窗 // 是否显示编辑弹窗
const showEdit = ref(false); const showEdit = ref(false);
// 是否显示批量移动弹窗 // 是否显示树状图弹窗
const showMove = ref(false); const showTree = ref(false);
// 树状图数据
const treeData = ref<ShopDealerReferee[]>([]);
// 加载状态 // 加载状态
const loading = ref(true); const loading = ref(true);
@@ -150,29 +161,29 @@
align: 'left', align: 'left',
width: 150 width: 150
}, },
{
title: '',
key: 'relationship',
align: 'center',
width: 50
},
{ {
title: '被推荐人信息', title: '被推荐人信息',
key: 'userInfo', key: 'userInfo',
align: 'left', align: 'left',
width: 150 width: 150
}, },
{ // {
title: '推荐层级', // title: '推荐层级',
key: 'level', // key: 'level',
align: 'center', // align: 'center',
width: 120, // width: 120,
filters: [ // filters: [
{ text: '一级推荐', value: 1 }, // { text: '一级推荐', value: 1 },
{ text: '二级推荐', value: 2 }, // { text: '二级推荐', value: 2 },
{ text: '三级推荐', value: 3 } // { text: '三级推荐', value: 3 }
] // ]
}, // },
{
title: '关系链',
key: 'relationChain',
align: 'center',
width: 120
},
{ {
title: '建立时间', title: '建立时间',
dataIndex: 'createTime', dataIndex: 'createTime',
@@ -212,30 +223,6 @@
return texts[level] || `${level}级推荐`; return texts[level] || `${level}级推荐`;
}; };
/* 查看详情 */
const viewDetail = (record: ShopDealerReferee) => {
Modal.info({
title: '推荐关系详情',
width: 600,
content: createVNode('div', { class: 'referee-detail' }, [
createVNode('div', { class: 'detail-section' }, [
createVNode('h4', null, '推荐关系信息'),
createVNode('p', null, `推荐人ID: ${record.dealerId}`),
createVNode('p', null, `被推荐人ID: ${record.userId}`),
createVNode('p', null, [
'推荐层级: ',
createVNode('span', {
class: 'level-badge',
style: `color: ${getLevelColor(record.level)}; font-weight: bold;`
}, getLevelText(record.level))
]),
createVNode('p', null, `建立时间: ${toDateString(record.createTime, 'yyyy-MM-dd HH:mm:ss')}`),
])
]),
okText: '关闭'
});
};
/* 查看关系链 */ /* 查看关系链 */
const viewRelationChain = (record: ShopDealerReferee) => { const viewRelationChain = (record: ShopDealerReferee) => {
// 这里可以调用API获取完整的推荐关系链 // 这里可以调用API获取完整的推荐关系链
@@ -253,7 +240,7 @@
createVNode('div', { class: 'chain-node user' }, [ createVNode('div', { class: 'chain-node user' }, [
createVNode('div', { class: 'node-title' }, '被推荐人'), createVNode('div', { class: 'node-title' }, '被推荐人'),
createVNode('div', { class: 'node-id' }, `用户ID: ${record.userId}`), createVNode('div', { class: 'node-id' }, `用户ID: ${record.userId}`),
createVNode('div', { class: 'node-level' }, getLevelText(record.level)) createVNode('div', { class: 'node-level' }, getLevelText(record.level || 0))
]) ])
]), ]),
createVNode('div', { class: 'chain-info' }, [ createVNode('div', { class: 'chain-info' }, [
@@ -267,13 +254,17 @@
/* 查看推荐树 */ /* 查看推荐树 */
const viewRefereeTree = () => { const viewRefereeTree = () => {
Modal.info({ // 加载所有数据用于树状图展示
title: '推荐关系树', loading.value = true;
width: 1000, pageShopDealerReferee({ page: 1, limit: 10000 }) // 获取所有数据
content: createVNode('div', null, [ .then((result) => {
createVNode('p', null, '推荐关系树功能开发中,将展示完整的推荐网络结构') treeData.value = result?.list || [];
]), showTree.value = true;
okText: '关闭' loading.value = false;
})
.catch((e) => {
loading.value = false;
message.error('加载数据失败: ' + e.message);
}); });
}; };
@@ -299,15 +290,10 @@
showEdit.value = true; showEdit.value = true;
}; };
/* 打开批量移动弹窗 */
const openMove = () => {
showMove.value = true;
};
/* 删除单个 */ /* 删除单个 */
const remove = (row: ShopDealerReferee) => { const remove = (row: ShopDealerReferee) => {
const hide = message.loading('请求中..', 0); const hide = message.loading('请求中..', 0);
removeShopDealerReferee(row.shopDealerRefereeId) removeShopDealerReferee(row.id)
.then((msg) => { .then((msg) => {
hide(); hide();
message.success(msg); message.success(msg);
@@ -332,7 +318,7 @@
maskClosable: true, maskClosable: true,
onOk: () => { onOk: () => {
const hide = message.loading('请求中..', 0); const hide = message.loading('请求中..', 0);
removeBatchShopDealerReferee(selection.value.map((d) => d.shopDealerRefereeId)) removeBatchShopDealerReferee(selection.value.map((d) => d.id))
.then((msg) => { .then((msg) => {
hide(); hide();
message.success(msg); message.success(msg);