refactor(shop):优化分销商推荐关系模块
- 移除文章API中的MODULES_API_URL前缀,统一使用相对路径 - 调整MODULES_API_URL配置逻辑,支持本地存储自定义接口地址 -修复生产环境判断逻辑错误,确保自定义接口地址正确加载 - 新增下划线与驼峰命名转换工具函数 - 扩展ShopDealerReferee模型字段,增加推荐人和被推荐人的详细信息 - 新增推荐关系树状图展示组件RefereeTree.vue- 修改推荐关系列表页面布局,优化用户信息展示方式- 移除推荐层级筛选条件及部分冗余操作按钮 - 简化编辑弹窗标题及表单项,移除不必要的字段输入 - 调整表格列配置,优化推荐关系可视化展示效果 - 移除详情查看和解除推荐关系功能,简化操作流程 - 修复分页查询参数默认值处理问题,增强代码健壮性
This commit is contained in:
@@ -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) {
|
||||||
|
|||||||
@@ -148,4 +148,4 @@ export async function importCmsNavigation(file: File) {
|
|||||||
return res.data.message;
|
return res.data.message;
|
||||||
}
|
}
|
||||||
return Promise.reject(new Error(res.data.message));
|
return Promise.reject(new Error(res.data.message));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -31,4 +45,4 @@ export interface ShopDealerRefereeParam extends PageParam {
|
|||||||
startTime?: string;
|
startTime?: string;
|
||||||
endTime?: string;
|
endTime?: string;
|
||||||
keywords?: string;
|
keywords?: string;
|
||||||
}
|
}
|
||||||
@@ -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';
|
||||||
|
|
||||||
|
|||||||
@@ -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}`);
|
||||||
|
|||||||
@@ -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(/^_/, '');
|
||||||
|
}
|
||||||
|
|||||||
@@ -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">
|
||||||
|
|||||||
205
src/views/sdy/shopDealerReferee/components/RefereeTree.vue
Normal file
205
src/views/sdy/shopDealerReferee/components/RefereeTree.vue
Normal 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>
|
||||||
@@ -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>
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
/* 保存编辑 */
|
/* 保存编辑 */
|
||||||
|
|||||||
@@ -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,14 +254,18 @@
|
|||||||
|
|
||||||
/* 查看推荐树 */
|
/* 查看推荐树 */
|
||||||
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);
|
||||||
|
|||||||
Reference in New Issue
Block a user