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 type { ApiResult, PageResult } from '@/api';
|
||||
import type { CmsArticle, CmsArticleParam } from './model';
|
||||
import {MODULES_API_URL} from '@/config/setting';
|
||||
import type {ApiResult, PageResult} from '@/api';
|
||||
import type {CmsArticle, CmsArticleParam} from './model';
|
||||
|
||||
/**
|
||||
* 分页查询文章
|
||||
*/
|
||||
export async function pageCmsArticle(params: CmsArticleParam) {
|
||||
const res = await request.get<ApiResult<PageResult<CmsArticle>>>(
|
||||
MODULES_API_URL + '/cms/cms-article/page',
|
||||
'/cms/cms-article/page',
|
||||
{
|
||||
params
|
||||
}
|
||||
@@ -24,7 +23,7 @@ export async function pageCmsArticle(params: CmsArticleParam) {
|
||||
*/
|
||||
export async function listCmsArticle(params?: CmsArticleParam) {
|
||||
const res = await request.get<ApiResult<CmsArticle[]>>(
|
||||
MODULES_API_URL + '/cms/cms-article',
|
||||
'/cms/cms-article',
|
||||
{
|
||||
params
|
||||
}
|
||||
@@ -40,7 +39,7 @@ export async function listCmsArticle(params?: CmsArticleParam) {
|
||||
*/
|
||||
export async function addCmsArticle(data: CmsArticle) {
|
||||
const res = await request.post<ApiResult<unknown>>(
|
||||
MODULES_API_URL + '/cms/cms-article',
|
||||
'/cms/cms-article',
|
||||
data
|
||||
);
|
||||
if (res.data.code === 0) {
|
||||
@@ -54,7 +53,7 @@ export async function addCmsArticle(data: CmsArticle) {
|
||||
*/
|
||||
export async function updateCmsArticle(data: CmsArticle) {
|
||||
const res = await request.put<ApiResult<unknown>>(
|
||||
MODULES_API_URL + '/cms/cms-article',
|
||||
'/cms/cms-article',
|
||||
data
|
||||
);
|
||||
if (res.data.code === 0) {
|
||||
@@ -68,7 +67,7 @@ export async function updateCmsArticle(data: CmsArticle) {
|
||||
*/
|
||||
export async function updateBatchCmsArticle(data: any) {
|
||||
const res = await request.put<ApiResult<unknown>>(
|
||||
MODULES_API_URL + '/cms/cms-article/batch',
|
||||
'/cms/cms-article/batch',
|
||||
data
|
||||
);
|
||||
if (res.data.code === 0) {
|
||||
@@ -82,7 +81,7 @@ export async function updateBatchCmsArticle(data: any) {
|
||||
*/
|
||||
export async function removeCmsArticle(id?: number) {
|
||||
const res = await request.delete<ApiResult<unknown>>(
|
||||
MODULES_API_URL + '/cms/cms-article/' + id
|
||||
'/cms/cms-article/' + id
|
||||
);
|
||||
if (res.data.code === 0) {
|
||||
return res.data.message;
|
||||
@@ -95,7 +94,7 @@ export async function removeCmsArticle(id?: number) {
|
||||
*/
|
||||
export async function removeBatchCmsArticle(data: (number | undefined)[]) {
|
||||
const res = await request.delete<ApiResult<unknown>>(
|
||||
MODULES_API_URL + '/cms/cms-article/batch',
|
||||
'/cms/cms-article/batch',
|
||||
{
|
||||
data
|
||||
}
|
||||
@@ -111,7 +110,7 @@ export async function removeBatchCmsArticle(data: (number | undefined)[]) {
|
||||
*/
|
||||
export async function getCmsArticle(id: number) {
|
||||
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) {
|
||||
return res.data.data;
|
||||
@@ -133,7 +132,7 @@ export async function getByCode(code: string) {
|
||||
}
|
||||
|
||||
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
|
||||
});
|
||||
if (res.data.code === 0) {
|
||||
@@ -150,7 +149,7 @@ export async function importArticles(file: File) {
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
const res = await request.post<ApiResult<unknown>>(
|
||||
MODULES_API_URL + '/cms/cms-article/import',
|
||||
'/cms/cms-article/import',
|
||||
formData
|
||||
);
|
||||
if (res.data.code === 0) {
|
||||
|
||||
@@ -148,4 +148,4 @@ export async function importCmsNavigation(file: File) {
|
||||
return res.data.message;
|
||||
}
|
||||
return Promise.reject(new Error(res.data.message));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,8 +8,22 @@ export interface ShopDealerReferee {
|
||||
id?: number;
|
||||
// 分销商用户ID
|
||||
dealerId?: number;
|
||||
// 分销商名称
|
||||
dealerName?: string;
|
||||
// 分销商头像
|
||||
dealerAvatar?: string;
|
||||
// 分销商手机号
|
||||
dealerPhone?: string;
|
||||
// 用户id(被推荐人)
|
||||
userId?: number;
|
||||
// 昵称
|
||||
nickname?: string;
|
||||
// 头像
|
||||
avatar?: string;
|
||||
// 别名
|
||||
alias?: string;
|
||||
// 手机号
|
||||
phone?: string;
|
||||
// 推荐关系层级(1,2,3)
|
||||
level?: number;
|
||||
// 商城ID
|
||||
@@ -31,4 +45,4 @@ export interface ShopDealerRefereeParam extends PageParam {
|
||||
startTime?: string;
|
||||
endTime?: 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 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';
|
||||
|
||||
|
||||
@@ -246,7 +246,7 @@ const reload = () => {
|
||||
});
|
||||
}
|
||||
// 检查是否启动自定义接口
|
||||
if(import.meta.env.PROD){
|
||||
if(!import.meta.env.PROD){
|
||||
getCmsWebsiteFieldByCode('ApiUrl').then(res => {
|
||||
if(res){
|
||||
localStorage.setItem('ApiUrl', `${res.value}`);
|
||||
|
||||
@@ -563,3 +563,31 @@ export const getTokenBySpm = () => {
|
||||
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-descriptions>
|
||||
<a-descriptions-item label="后台管理">
|
||||
{{ website.apiUrl }}
|
||||
<a-tag>https://mp.websoft.top</a-tag>
|
||||
</a-descriptions-item>
|
||||
<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>
|
||||
<div class="search-container">
|
||||
<!-- 搜索表单 -->
|
||||
<a-form
|
||||
:model="searchForm"
|
||||
@@ -26,19 +25,6 @@
|
||||
/>
|
||||
</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-range-picker
|
||||
v-model:value="searchForm.dateRange"
|
||||
@@ -62,29 +48,28 @@
|
||||
</a-form>
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
<div class="action-buttons">
|
||||
<a-space>
|
||||
<a-button type="primary" @click="add" class="ele-btn-icon">
|
||||
<template #icon>
|
||||
<PlusOutlined/>
|
||||
</template>
|
||||
建立推荐关系
|
||||
</a-button>
|
||||
<a-button @click="viewTree" class="ele-btn-icon">
|
||||
<template #icon>
|
||||
<ApartmentOutlined/>
|
||||
</template>
|
||||
推荐关系树
|
||||
</a-button>
|
||||
<a-button @click="exportData" class="ele-btn-icon">
|
||||
<template #icon>
|
||||
<ExportOutlined/>
|
||||
</template>
|
||||
导出数据
|
||||
</a-button>
|
||||
</a-space>
|
||||
</div>
|
||||
</div>
|
||||
<!-- <div class="action-buttons">-->
|
||||
<!-- <a-space>-->
|
||||
<!-- <a-button type="primary" @click="add" class="ele-btn-icon">-->
|
||||
<!-- <template #icon>-->
|
||||
<!-- <PlusOutlined/>-->
|
||||
<!-- </template>-->
|
||||
<!-- 建立推荐关系-->
|
||||
<!-- </a-button>-->
|
||||
<!-- <a-button @click="viewTree" class="ele-btn-icon">-->
|
||||
<!-- <template #icon>-->
|
||||
<!-- <ApartmentOutlined/>-->
|
||||
<!-- </template>-->
|
||||
<!-- 推荐关系树-->
|
||||
<!-- </a-button>-->
|
||||
<!-- <a-button @click="exportData" class="ele-btn-icon">-->
|
||||
<!-- <template #icon>-->
|
||||
<!-- <ExportOutlined/>-->
|
||||
<!-- </template>-->
|
||||
<!-- 导出数据-->
|
||||
<!-- </a-button>-->
|
||||
<!-- </a-space>-->
|
||||
<!-- </div>-->
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
:visible="visible"
|
||||
:maskClosable="false"
|
||||
:maxable="maxable"
|
||||
:title="isUpdate ? '编辑分销商推荐关系表' : '添加分销商推荐关系表'"
|
||||
:title="isUpdate ? '编辑推荐' : '添加推荐关系'"
|
||||
:body-style="{ paddingBottom: '28px' }"
|
||||
@update:visible="updateVisible"
|
||||
@ok="save"
|
||||
@@ -19,34 +19,20 @@
|
||||
styleResponsive ? { md: 19, sm: 19, xs: 24 } : { flex: '1' }
|
||||
"
|
||||
>
|
||||
<a-form-item label="分销商用户ID" name="dealerId">
|
||||
<a-form-item label="推荐人信息" name="dealerId">
|
||||
<a-input
|
||||
allow-clear
|
||||
placeholder="请输入分销商用户ID"
|
||||
v-model:value="form.dealerId"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="用户id(被推荐人)" name="userId">
|
||||
<a-form-item label="被推荐人信息" name="userId">
|
||||
<a-input
|
||||
allow-clear
|
||||
placeholder="请输入用户id(被推荐人)"
|
||||
v-model:value="form.userId"
|
||||
/>
|
||||
</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>
|
||||
</ele-modal>
|
||||
</template>
|
||||
@@ -54,14 +40,13 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive, watch } from '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 { ShopDealerReferee } from '@/api/shop/shopDealerReferee/model';
|
||||
import { useThemeStore } from '@/store/modules/theme';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { ItemType } from 'ele-admin-pro/es/ele-image-upload/types';
|
||||
import { FormInstance } from 'ant-design-vue/es/form';
|
||||
import { FileRecord } from '@/api/system/file/model';
|
||||
|
||||
// 是否是修改
|
||||
const isUpdate = ref(false);
|
||||
@@ -98,10 +83,7 @@
|
||||
level: undefined,
|
||||
tenantId: undefined,
|
||||
createTime: undefined,
|
||||
updateTime: undefined,
|
||||
status: 0,
|
||||
comments: '',
|
||||
sortNumber: 100
|
||||
updateTime: undefined
|
||||
});
|
||||
|
||||
/* 更新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);
|
||||
|
||||
/* 保存编辑 */
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<a-card :bordered="false" :body-style="{ padding: '16px' }">
|
||||
<ele-pro-table
|
||||
ref="tableRef"
|
||||
row-key="shopDealerRefereeId"
|
||||
row-key="id"
|
||||
:columns="columns"
|
||||
:datasource="datasource"
|
||||
:customRow="customRow"
|
||||
@@ -21,16 +21,21 @@
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'dealerInfo'">
|
||||
<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">
|
||||
<a-tag color="blue">推荐人</a-tag>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template v-if="column.key === 'relationship'">
|
||||
<ArrowRightOutlined />
|
||||
</template>
|
||||
|
||||
<template v-if="column.key === 'userInfo'">
|
||||
<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">
|
||||
<a-tag color="green">被推荐人</a-tag>
|
||||
</div>
|
||||
@@ -39,10 +44,10 @@
|
||||
|
||||
<template v-if="column.key === 'level'">
|
||||
<a-tag
|
||||
:color="getLevelColor(record.level)"
|
||||
:color="getLevelColor(record.level || 0)"
|
||||
class="level-tag"
|
||||
>
|
||||
{{ getLevelText(record.level) }}
|
||||
{{ getLevelText(record.level || 0) }}
|
||||
</a-tag>
|
||||
</template>
|
||||
|
||||
@@ -59,23 +64,19 @@
|
||||
|
||||
<template v-if="column.key === 'action'">
|
||||
<a-space>
|
||||
<a @click="viewDetail(record)" class="ele-text-info">
|
||||
<EyeOutlined /> 详情
|
||||
</a>
|
||||
<a-divider type="vertical" />
|
||||
<a @click="openEdit(record)" class="ele-text-primary">
|
||||
<EditOutlined /> 编辑
|
||||
</a>
|
||||
<a-divider type="vertical" />
|
||||
<a-popconfirm
|
||||
title="确定要解除此推荐关系吗?"
|
||||
@confirm="remove(record)"
|
||||
placement="topRight"
|
||||
>
|
||||
<a class="ele-text-danger">
|
||||
<DisconnectOutlined /> 解除
|
||||
</a>
|
||||
</a-popconfirm>
|
||||
<!-- <a-divider type="vertical" />-->
|
||||
<!-- <a-popconfirm-->
|
||||
<!-- title="确定要解除此推荐关系吗?"-->
|
||||
<!-- @confirm="remove(record)"-->
|
||||
<!-- placement="topRight"-->
|
||||
<!-- >-->
|
||||
<!-- <a class="ele-text-danger">-->
|
||||
<!-- <DisconnectOutlined /> 解除-->
|
||||
<!-- </a>-->
|
||||
<!-- </a-popconfirm>-->
|
||||
</a-space>
|
||||
</template>
|
||||
</template>
|
||||
@@ -84,6 +85,14 @@
|
||||
|
||||
<!-- 编辑弹窗 -->
|
||||
<ShopDealerRefereeEdit v-model:visible="showEdit" :data="current" @done="reload" />
|
||||
|
||||
<!-- 树状图弹窗 -->
|
||||
<RefereeTree
|
||||
v-model:visible="showTree"
|
||||
:data="treeData"
|
||||
:title="'推荐关系树'"
|
||||
@cancel="showTree = false"
|
||||
/>
|
||||
</a-page-header>
|
||||
</template>
|
||||
|
||||
@@ -93,9 +102,8 @@
|
||||
import {
|
||||
ExclamationCircleOutlined,
|
||||
TeamOutlined,
|
||||
EyeOutlined,
|
||||
EditOutlined,
|
||||
DisconnectOutlined
|
||||
ArrowRightOutlined
|
||||
} from '@ant-design/icons-vue';
|
||||
import type { EleProTable } from 'ele-admin-pro';
|
||||
import { toDateString } from 'ele-admin-pro';
|
||||
@@ -106,6 +114,7 @@
|
||||
import Search from './components/search.vue';
|
||||
import {getPageTitle} from '@/utils/common';
|
||||
import ShopDealerRefereeEdit from './components/shopDealerRefereeEdit.vue';
|
||||
import RefereeTree from './components/RefereeTree.vue';
|
||||
import { pageShopDealerReferee, removeShopDealerReferee, removeBatchShopDealerReferee } from '@/api/shop/shopDealerReferee';
|
||||
import type { ShopDealerReferee, ShopDealerRefereeParam } from '@/api/shop/shopDealerReferee/model';
|
||||
|
||||
@@ -118,8 +127,10 @@
|
||||
const current = ref<ShopDealerReferee | null>(null);
|
||||
// 是否显示编辑弹窗
|
||||
const showEdit = ref(false);
|
||||
// 是否显示批量移动弹窗
|
||||
const showMove = ref(false);
|
||||
// 是否显示树状图弹窗
|
||||
const showTree = ref(false);
|
||||
// 树状图数据
|
||||
const treeData = ref<ShopDealerReferee[]>([]);
|
||||
// 加载状态
|
||||
const loading = ref(true);
|
||||
|
||||
@@ -150,29 +161,29 @@
|
||||
align: 'left',
|
||||
width: 150
|
||||
},
|
||||
{
|
||||
title: '',
|
||||
key: 'relationship',
|
||||
align: 'center',
|
||||
width: 50
|
||||
},
|
||||
{
|
||||
title: '被推荐人信息',
|
||||
key: 'userInfo',
|
||||
align: 'left',
|
||||
width: 150
|
||||
},
|
||||
{
|
||||
title: '推荐层级',
|
||||
key: 'level',
|
||||
align: 'center',
|
||||
width: 120,
|
||||
filters: [
|
||||
{ text: '一级推荐', value: 1 },
|
||||
{ text: '二级推荐', value: 2 },
|
||||
{ text: '三级推荐', value: 3 }
|
||||
]
|
||||
},
|
||||
{
|
||||
title: '关系链',
|
||||
key: 'relationChain',
|
||||
align: 'center',
|
||||
width: 120
|
||||
},
|
||||
// {
|
||||
// title: '推荐层级',
|
||||
// key: 'level',
|
||||
// align: 'center',
|
||||
// width: 120,
|
||||
// filters: [
|
||||
// { text: '一级推荐', value: 1 },
|
||||
// { text: '二级推荐', value: 2 },
|
||||
// { text: '三级推荐', value: 3 }
|
||||
// ]
|
||||
// },
|
||||
{
|
||||
title: '建立时间',
|
||||
dataIndex: 'createTime',
|
||||
@@ -212,30 +223,6 @@
|
||||
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) => {
|
||||
// 这里可以调用API获取完整的推荐关系链
|
||||
@@ -253,7 +240,7 @@
|
||||
createVNode('div', { class: 'chain-node user' }, [
|
||||
createVNode('div', { class: 'node-title' }, '被推荐人'),
|
||||
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' }, [
|
||||
@@ -267,14 +254,18 @@
|
||||
|
||||
/* 查看推荐树 */
|
||||
const viewRefereeTree = () => {
|
||||
Modal.info({
|
||||
title: '推荐关系树',
|
||||
width: 1000,
|
||||
content: createVNode('div', null, [
|
||||
createVNode('p', null, '推荐关系树功能开发中,将展示完整的推荐网络结构')
|
||||
]),
|
||||
okText: '关闭'
|
||||
});
|
||||
// 加载所有数据用于树状图展示
|
||||
loading.value = true;
|
||||
pageShopDealerReferee({ page: 1, limit: 10000 }) // 获取所有数据
|
||||
.then((result) => {
|
||||
treeData.value = result?.list || [];
|
||||
showTree.value = true;
|
||||
loading.value = false;
|
||||
})
|
||||
.catch((e) => {
|
||||
loading.value = false;
|
||||
message.error('加载数据失败: ' + e.message);
|
||||
});
|
||||
};
|
||||
|
||||
/* 导出数据 */
|
||||
@@ -299,15 +290,10 @@
|
||||
showEdit.value = true;
|
||||
};
|
||||
|
||||
/* 打开批量移动弹窗 */
|
||||
const openMove = () => {
|
||||
showMove.value = true;
|
||||
};
|
||||
|
||||
/* 删除单个 */
|
||||
const remove = (row: ShopDealerReferee) => {
|
||||
const hide = message.loading('请求中..', 0);
|
||||
removeShopDealerReferee(row.shopDealerRefereeId)
|
||||
removeShopDealerReferee(row.id)
|
||||
.then((msg) => {
|
||||
hide();
|
||||
message.success(msg);
|
||||
@@ -332,7 +318,7 @@
|
||||
maskClosable: true,
|
||||
onOk: () => {
|
||||
const hide = message.loading('请求中..', 0);
|
||||
removeBatchShopDealerReferee(selection.value.map((d) => d.shopDealerRefereeId))
|
||||
removeBatchShopDealerReferee(selection.value.map((d) => d.id))
|
||||
.then((msg) => {
|
||||
hide();
|
||||
message.success(msg);
|
||||
|
||||
Reference in New Issue
Block a user