This commit is contained in:
parent
262de67a8e
commit
8ae6dcfa88
|
|
@ -1,4 +1,4 @@
|
||||||
using MiAssessment.Admin.Business.Attributes;
|
using MiAssessment.Admin.Business.Attributes;
|
||||||
using MiAssessment.Admin.Business.Models;
|
using MiAssessment.Admin.Business.Models;
|
||||||
using MiAssessment.Admin.Business.Models.Common;
|
using MiAssessment.Admin.Business.Models.Common;
|
||||||
using MiAssessment.Admin.Business.Models.User;
|
using MiAssessment.Admin.Business.Models.User;
|
||||||
|
|
@ -129,6 +129,64 @@ public class UserController : BusinessControllerBase
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 绑定上下级关系(渠道商-合伙人)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="request">绑定请求</param>
|
||||||
|
/// <returns>操作结果</returns>
|
||||||
|
[HttpPost("bindParent")]
|
||||||
|
[BusinessPermission("user:update")]
|
||||||
|
public async Task<IActionResult> BindParentUser([FromBody] BindParentUserRequest request)
|
||||||
|
{
|
||||||
|
if (!ModelState.IsValid)
|
||||||
|
{
|
||||||
|
return ValidationError("参数验证失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var result = await _userService.BindParentUserAsync(request.UserId, request.ParentUserId);
|
||||||
|
if (result)
|
||||||
|
{
|
||||||
|
return Ok("绑定上下级关系成功");
|
||||||
|
}
|
||||||
|
return Error(ErrorCodes.BusinessError, "绑定失败");
|
||||||
|
}
|
||||||
|
catch (BusinessException ex)
|
||||||
|
{
|
||||||
|
return Error(ex.Code, ex.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 解除上下级关系
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="request">请求(包含用户ID)</param>
|
||||||
|
/// <returns>操作结果</returns>
|
||||||
|
[HttpPost("unbindParent")]
|
||||||
|
[BusinessPermission("user:update")]
|
||||||
|
public async Task<IActionResult> UnbindParentUser([FromBody] DeleteRequest request)
|
||||||
|
{
|
||||||
|
if (!ModelState.IsValid)
|
||||||
|
{
|
||||||
|
return ValidationError("参数验证失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var result = await _userService.UnbindParentUserAsync(request.Id);
|
||||||
|
if (result)
|
||||||
|
{
|
||||||
|
return Ok("解除上下级关系成功");
|
||||||
|
}
|
||||||
|
return Error(ErrorCodes.BusinessError, "解除失败");
|
||||||
|
}
|
||||||
|
catch (BusinessException ex)
|
||||||
|
{
|
||||||
|
return Error(ex.Code, ex.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 导出用户列表
|
/// 导出用户列表
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -182,6 +182,11 @@ public static class ErrorCodes
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const int UserNotFound = 3301;
|
public const int UserNotFound = 3301;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 上级用户不存在
|
||||||
|
/// </summary>
|
||||||
|
public const int ParentUserNotFound = 3302;
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region 订单模块错误 (3400-3499)
|
#region 订单模块错误 (3400-3499)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
|
namespace MiAssessment.Admin.Business.Models.User;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 绑定上下级关系请求
|
||||||
|
/// </summary>
|
||||||
|
public class BindParentUserRequest
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 下级用户ID(合伙人)
|
||||||
|
/// </summary>
|
||||||
|
[Required]
|
||||||
|
public long UserId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 上级用户ID(渠道商)
|
||||||
|
/// </summary>
|
||||||
|
[Required]
|
||||||
|
public long ParentUserId { get; set; }
|
||||||
|
}
|
||||||
|
|
@ -58,6 +58,21 @@ public interface IUserBusinessService
|
||||||
/// <returns>导出数据</returns>
|
/// <returns>导出数据</returns>
|
||||||
Task<List<UserDto>> ExportUsersAsync(UserQueryRequest request);
|
Task<List<UserDto>> ExportUsersAsync(UserQueryRequest request);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 绑定上下级关系(上级必须是渠道商,下级必须是合伙人)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="userId">下级用户ID(合伙人)</param>
|
||||||
|
/// <param name="parentUserId">上级用户ID(渠道商)</param>
|
||||||
|
/// <returns>是否成功</returns>
|
||||||
|
Task<bool> BindParentUserAsync(long userId, long parentUserId);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 解除上下级关系
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="userId">用户ID</param>
|
||||||
|
/// <returns>是否成功</returns>
|
||||||
|
Task<bool> UnbindParentUserAsync(long userId);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 删除用户(硬删除,同时清除登录记录和令牌)
|
/// 删除用户(硬删除,同时清除登录记录和令牌)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -211,12 +211,143 @@ public class UserBusinessService : IUserBusinessService
|
||||||
throw new BusinessException(ErrorCodes.UserNotFound, "用户不存在");
|
throw new BusinessException(ErrorCodes.UserNotFound, "用户不存在");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (userLevel < 1 || userLevel > 3)
|
||||||
|
{
|
||||||
|
throw new BusinessException(ErrorCodes.ParamError, "无效的用户等级");
|
||||||
|
}
|
||||||
|
|
||||||
|
var oldLevel = user.UserLevel;
|
||||||
user.UserLevel = userLevel;
|
user.UserLevel = userLevel;
|
||||||
user.UpdateTime = DateTime.Now;
|
user.UpdateTime = DateTime.Now;
|
||||||
|
|
||||||
|
// 等级变更时自动处理上下级关系
|
||||||
|
if (user.ParentUserId.HasValue && oldLevel != userLevel)
|
||||||
|
{
|
||||||
|
var shouldUnbind = await ShouldUnbindParentOnLevelChange(user.ParentUserId.Value, userLevel);
|
||||||
|
if (shouldUnbind)
|
||||||
|
{
|
||||||
|
_logger.LogInformation(
|
||||||
|
"用户 {UserId} 等级从 {OldLevel} 变更为 {NewLevel},自动解除与上级 {ParentUserId} 的关系",
|
||||||
|
id, oldLevel, userLevel, user.ParentUserId.Value);
|
||||||
|
user.ParentUserId = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await _dbContext.SaveChangesAsync();
|
||||||
|
|
||||||
|
_logger.LogInformation("更新用户等级成功,ID: {UserId}, 等级: {OldLevel} -> {UserLevel}", id, oldLevel, userLevel);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 判断等级变更时是否需要解除与上级的关系
|
||||||
|
/// 规则:
|
||||||
|
/// - 提升为渠道商(3):一律解除上下级关系
|
||||||
|
/// - 提升为合伙人(2):仅当上级是渠道商(3)时保留,否则解除
|
||||||
|
/// - 变为普通用户(1):保留原有关系不变
|
||||||
|
/// </summary>
|
||||||
|
private async Task<bool> ShouldUnbindParentOnLevelChange(long parentUserId, int newLevel)
|
||||||
|
{
|
||||||
|
if (newLevel == 3)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (newLevel == 2)
|
||||||
|
{
|
||||||
|
var parentLevel = await _dbContext.Users
|
||||||
|
.AsNoTracking()
|
||||||
|
.Where(u => u.Id == parentUserId && !u.IsDeleted)
|
||||||
|
.Select(u => u.UserLevel)
|
||||||
|
.FirstOrDefaultAsync();
|
||||||
|
|
||||||
|
return parentLevel != 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public async Task<bool> BindParentUserAsync(long userId, long parentUserId)
|
||||||
|
{
|
||||||
|
if (userId == parentUserId)
|
||||||
|
{
|
||||||
|
throw new BusinessException(ErrorCodes.InvalidOperation, "不能绑定自己为上级");
|
||||||
|
}
|
||||||
|
|
||||||
|
var user = await _dbContext.Users
|
||||||
|
.Where(u => u.Id == userId && !u.IsDeleted)
|
||||||
|
.FirstOrDefaultAsync();
|
||||||
|
|
||||||
|
if (user == null)
|
||||||
|
{
|
||||||
|
throw new BusinessException(ErrorCodes.UserNotFound, "用户不存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
var parentUser = await _dbContext.Users
|
||||||
|
.AsNoTracking()
|
||||||
|
.Where(u => u.Id == parentUserId && !u.IsDeleted)
|
||||||
|
.FirstOrDefaultAsync();
|
||||||
|
|
||||||
|
if (parentUser == null)
|
||||||
|
{
|
||||||
|
throw new BusinessException(ErrorCodes.ParentUserNotFound, "上级用户不存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parentUser.UserLevel != 3)
|
||||||
|
{
|
||||||
|
throw new BusinessException(ErrorCodes.InvalidOperation, "上级用户必须是渠道商");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (user.UserLevel != 2)
|
||||||
|
{
|
||||||
|
throw new BusinessException(ErrorCodes.InvalidOperation, "只有合伙人才能绑定上级渠道商");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (user.ParentUserId.HasValue)
|
||||||
|
{
|
||||||
|
throw new BusinessException(ErrorCodes.InvalidOperation, "该用户已有上级,请先解除现有上下级关系");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 防止循环引用:检查上级的上级是否是当前用户
|
||||||
|
if (parentUser.ParentUserId == userId)
|
||||||
|
{
|
||||||
|
throw new BusinessException(ErrorCodes.InvalidOperation, "不能形成循环上下级关系");
|
||||||
|
}
|
||||||
|
|
||||||
|
user.ParentUserId = parentUserId;
|
||||||
|
user.UpdateTime = DateTime.Now;
|
||||||
|
|
||||||
await _dbContext.SaveChangesAsync();
|
await _dbContext.SaveChangesAsync();
|
||||||
|
|
||||||
_logger.LogInformation("更新用户等级成功,ID: {UserId}, 等级: {UserLevel}", id, userLevel);
|
_logger.LogInformation("绑定上下级关系成功,用户 {UserId} 的上级设为 {ParentUserId}", userId, parentUserId);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public async Task<bool> UnbindParentUserAsync(long userId)
|
||||||
|
{
|
||||||
|
var user = await _dbContext.Users
|
||||||
|
.Where(u => u.Id == userId && !u.IsDeleted)
|
||||||
|
.FirstOrDefaultAsync();
|
||||||
|
|
||||||
|
if (user == null)
|
||||||
|
{
|
||||||
|
throw new BusinessException(ErrorCodes.UserNotFound, "用户不存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!user.ParentUserId.HasValue)
|
||||||
|
{
|
||||||
|
throw new BusinessException(ErrorCodes.InvalidOperation, "该用户没有上级,无需解除");
|
||||||
|
}
|
||||||
|
|
||||||
|
var oldParentId = user.ParentUserId.Value;
|
||||||
|
user.ParentUserId = null;
|
||||||
|
user.UpdateTime = DateTime.Now;
|
||||||
|
|
||||||
|
await _dbContext.SaveChangesAsync();
|
||||||
|
|
||||||
|
_logger.LogInformation("解除上下级关系成功,用户 {UserId} 与上级 {ParentUserId} 解绑", userId, oldParentId);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -93,6 +93,16 @@ export interface UpdateUserLevelRequest {
|
||||||
userLevel: number
|
userLevel: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 绑定上下级关系请求
|
||||||
|
*/
|
||||||
|
export interface BindParentUserRequest {
|
||||||
|
/** 下级用户ID(合伙人) */
|
||||||
|
userId: number
|
||||||
|
/** 上级用户ID(渠道商) */
|
||||||
|
parentUserId: number
|
||||||
|
}
|
||||||
|
|
||||||
// ==================== User API ====================
|
// ==================== User API ====================
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -161,6 +171,32 @@ export function exportUsers(params: UserQuery): Promise<ApiResponse<Blob>> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 绑定上下级关系(渠道商-合伙人)
|
||||||
|
* @param data 绑定请求
|
||||||
|
* @returns 绑定结果
|
||||||
|
*/
|
||||||
|
export function bindParentUser(data: BindParentUserRequest): Promise<ApiResponse<boolean>> {
|
||||||
|
return request<boolean>({
|
||||||
|
url: '/admin/user/bindParent',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解除上下级关系
|
||||||
|
* @param id 用户ID
|
||||||
|
* @returns 解除结果
|
||||||
|
*/
|
||||||
|
export function unbindParentUser(id: number): Promise<ApiResponse<boolean>> {
|
||||||
|
return request<boolean>({
|
||||||
|
url: '/admin/user/unbindParent',
|
||||||
|
method: 'post',
|
||||||
|
data: { id }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 删除用户(硬删除,同时清除登录记录和令牌)
|
* 删除用户(硬删除,同时清除登录记录和令牌)
|
||||||
* @param id 用户ID
|
* @param id 用户ID
|
||||||
|
|
|
||||||
|
|
@ -161,7 +161,7 @@
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
|
||||||
<!-- 操作 -->
|
<!-- 操作 -->
|
||||||
<el-table-column label="操作" width="250" fixed="right" align="center">
|
<el-table-column label="操作" width="320" fixed="right" align="center">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<el-button type="primary" link size="small" @click="handleViewDetail(row)">
|
<el-button type="primary" link size="small" @click="handleViewDetail(row)">
|
||||||
<el-icon><View /></el-icon>
|
<el-icon><View /></el-icon>
|
||||||
|
|
@ -175,6 +175,10 @@
|
||||||
<el-icon><Edit /></el-icon>
|
<el-icon><Edit /></el-icon>
|
||||||
等级
|
等级
|
||||||
</el-button>
|
</el-button>
|
||||||
|
<el-button type="success" link size="small" @click="handleBindParent(row)">
|
||||||
|
<el-icon><Link /></el-icon>
|
||||||
|
上级
|
||||||
|
</el-button>
|
||||||
<el-button type="danger" link size="small" @click="handleDelete(row)">
|
<el-button type="danger" link size="small" @click="handleDelete(row)">
|
||||||
<el-icon><Delete /></el-icon>
|
<el-icon><Delete /></el-icon>
|
||||||
删除
|
删除
|
||||||
|
|
@ -246,6 +250,24 @@
|
||||||
{{ state.userDetail.parentUserNickname || '-' }}
|
{{ state.userDetail.parentUserNickname || '-' }}
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
</el-descriptions>
|
</el-descriptions>
|
||||||
|
<div style="margin-top: 8px;">
|
||||||
|
<el-button
|
||||||
|
v-if="state.userDetail.parentUserId"
|
||||||
|
type="danger"
|
||||||
|
size="small"
|
||||||
|
@click="handleDetailUnbind"
|
||||||
|
>
|
||||||
|
解除上级关系
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
v-else-if="state.userDetail.userLevel === USER_LEVEL.PARTNER"
|
||||||
|
type="primary"
|
||||||
|
size="small"
|
||||||
|
@click="handleBindParent(state.userDetail)"
|
||||||
|
>
|
||||||
|
绑定上级渠道商
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 财务信息 -->
|
<!-- 财务信息 -->
|
||||||
|
|
@ -348,7 +370,7 @@
|
||||||
<el-dialog
|
<el-dialog
|
||||||
v-model="state.levelDialogVisible"
|
v-model="state.levelDialogVisible"
|
||||||
title="修改用户等级"
|
title="修改用户等级"
|
||||||
width="400px"
|
width="460px"
|
||||||
:close-on-click-modal="false"
|
:close-on-click-modal="false"
|
||||||
>
|
>
|
||||||
<el-form
|
<el-form
|
||||||
|
|
@ -371,6 +393,14 @@
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
|
<el-alert
|
||||||
|
v-if="levelChangeWarning"
|
||||||
|
:title="levelChangeWarning"
|
||||||
|
type="warning"
|
||||||
|
show-icon
|
||||||
|
:closable="false"
|
||||||
|
style="margin-top: 8px;"
|
||||||
|
/>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<el-button @click="state.levelDialogVisible = false">取消</el-button>
|
<el-button @click="state.levelDialogVisible = false">取消</el-button>
|
||||||
<el-button type="primary" :loading="state.levelFormLoading" @click="handleLevelSubmit">
|
<el-button type="primary" :loading="state.levelFormLoading" @click="handleLevelSubmit">
|
||||||
|
|
@ -378,6 +408,74 @@
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
|
||||||
|
<!-- 绑定上级对话框 -->
|
||||||
|
<el-dialog
|
||||||
|
v-model="state.bindDialogVisible"
|
||||||
|
title="管理上级关系"
|
||||||
|
width="500px"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
destroy-on-close
|
||||||
|
>
|
||||||
|
<div class="bind-info">
|
||||||
|
<el-descriptions :column="1" border>
|
||||||
|
<el-descriptions-item label="当前用户">
|
||||||
|
{{ state.bindFormData.userNickname }}({{ state.bindFormData.userUid }})
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="用户等级">
|
||||||
|
<el-tag :type="getLevelTagType(state.bindFormData.userLevel)">
|
||||||
|
{{ getLevelName(state.bindFormData.userLevel) }}
|
||||||
|
</el-tag>
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="当前上级">
|
||||||
|
<template v-if="state.bindFormData.currentParentNickname">
|
||||||
|
{{ state.bindFormData.currentParentNickname }}({{ state.bindFormData.currentParentUid }})
|
||||||
|
<el-button type="danger" link size="small" @click="handleUnbindParent" :loading="state.bindFormLoading" style="margin-left: 8px;">
|
||||||
|
解除关系
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
<span v-else class="no-parent">暂无上级</span>
|
||||||
|
</el-descriptions-item>
|
||||||
|
</el-descriptions>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-divider />
|
||||||
|
|
||||||
|
<template v-if="!state.bindFormData.currentParentNickname">
|
||||||
|
<el-alert
|
||||||
|
v-if="state.bindFormData.userLevel !== USER_LEVEL.PARTNER"
|
||||||
|
title="只有合伙人才能绑定上级渠道商"
|
||||||
|
type="info"
|
||||||
|
show-icon
|
||||||
|
:closable="false"
|
||||||
|
style="margin-bottom: 16px;"
|
||||||
|
/>
|
||||||
|
<el-form
|
||||||
|
v-else
|
||||||
|
ref="bindFormRef"
|
||||||
|
:model="state.bindFormData"
|
||||||
|
:rules="bindFormRules"
|
||||||
|
label-width="100px"
|
||||||
|
>
|
||||||
|
<el-form-item label="上级渠道商" prop="parentUid">
|
||||||
|
<el-input
|
||||||
|
v-model="state.bindFormData.parentUid"
|
||||||
|
placeholder="请输入渠道商的UID"
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" :loading="state.bindFormLoading" @click="handleBindSubmit">
|
||||||
|
确认绑定
|
||||||
|
</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #footer>
|
||||||
|
<el-button @click="state.bindDialogVisible = false">关闭</el-button>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
@ -387,8 +485,8 @@
|
||||||
* @description 管理C端用户信息,支持搜索、查看详情、状态管理、等级修改、导出
|
* @description 管理C端用户信息,支持搜索、查看详情、状态管理、等级修改、导出
|
||||||
* @requirements 9.1, 9.2, 9.3, 9.4, 9.5, 9.6, 9.7
|
* @requirements 9.1, 9.2, 9.3, 9.4, 9.5, 9.6, 9.7
|
||||||
*/
|
*/
|
||||||
import { reactive, ref, onMounted } from 'vue'
|
import { reactive, ref, computed, onMounted } from 'vue'
|
||||||
import { Search, Refresh, View, Edit, Download, User, Delete } from '@element-plus/icons-vue'
|
import { Search, Refresh, View, Edit, Download, User, Delete, Link } from '@element-plus/icons-vue'
|
||||||
import { ElMessage, ElMessageBox, type FormInstance, type FormRules } from 'element-plus'
|
import { ElMessage, ElMessageBox, type FormInstance, type FormRules } from 'element-plus'
|
||||||
import {
|
import {
|
||||||
getUserList,
|
getUserList,
|
||||||
|
|
@ -397,6 +495,8 @@ import {
|
||||||
updateUserLevel,
|
updateUserLevel,
|
||||||
exportUsers,
|
exportUsers,
|
||||||
deleteUser,
|
deleteUser,
|
||||||
|
bindParentUser,
|
||||||
|
unbindParentUser,
|
||||||
type UserItem,
|
type UserItem,
|
||||||
type UserDetail,
|
type UserDetail,
|
||||||
type UserQuery
|
type UserQuery
|
||||||
|
|
@ -461,6 +561,16 @@ interface LevelFormData {
|
||||||
userLevel: string
|
userLevel: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface BindFormData {
|
||||||
|
userId: number
|
||||||
|
userNickname: string
|
||||||
|
userUid: string
|
||||||
|
userLevel: number
|
||||||
|
currentParentNickname: string
|
||||||
|
currentParentUid: string
|
||||||
|
parentUid: string
|
||||||
|
}
|
||||||
|
|
||||||
interface UserPageState {
|
interface UserPageState {
|
||||||
loading: boolean
|
loading: boolean
|
||||||
tableData: UserItemWithLoading[]
|
tableData: UserItemWithLoading[]
|
||||||
|
|
@ -481,12 +591,16 @@ interface UserPageState {
|
||||||
subParentId: number
|
subParentId: number
|
||||||
subParentNickname: string
|
subParentNickname: string
|
||||||
subParentUid: string
|
subParentUid: string
|
||||||
|
bindDialogVisible: boolean
|
||||||
|
bindFormData: BindFormData
|
||||||
|
bindFormLoading: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============ Refs ============
|
// ============ Refs ============
|
||||||
|
|
||||||
const tableRef = ref<InstanceType<typeof import('element-plus')['ElTable']>>()
|
const tableRef = ref<InstanceType<typeof import('element-plus')['ElTable']>>()
|
||||||
const levelFormRef = ref<FormInstance>()
|
const levelFormRef = ref<FormInstance>()
|
||||||
|
const bindFormRef = ref<FormInstance>()
|
||||||
const dateRange = ref<[string, string] | null>(null)
|
const dateRange = ref<[string, string] | null>(null)
|
||||||
|
|
||||||
// ============ State ============
|
// ============ State ============
|
||||||
|
|
@ -527,7 +641,18 @@ const state = reactive<UserPageState>({
|
||||||
subPageSize: 10,
|
subPageSize: 10,
|
||||||
subParentId: 0,
|
subParentId: 0,
|
||||||
subParentNickname: '',
|
subParentNickname: '',
|
||||||
subParentUid: ''
|
subParentUid: '',
|
||||||
|
bindDialogVisible: false,
|
||||||
|
bindFormData: {
|
||||||
|
userId: 0,
|
||||||
|
userNickname: '',
|
||||||
|
userUid: '',
|
||||||
|
userLevel: 0,
|
||||||
|
currentParentNickname: '',
|
||||||
|
currentParentUid: '',
|
||||||
|
parentUid: ''
|
||||||
|
},
|
||||||
|
bindFormLoading: false
|
||||||
})
|
})
|
||||||
|
|
||||||
// ============ Form Rules ============
|
// ============ Form Rules ============
|
||||||
|
|
@ -538,6 +663,26 @@ const levelFormRules: FormRules = {
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const bindFormRules: FormRules = {
|
||||||
|
parentUid: [
|
||||||
|
{ required: true, message: '请输入渠道商的UID', trigger: 'blur' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
const levelChangeWarning = computed(() => {
|
||||||
|
const newLevel = Number(state.levelFormData.userLevel)
|
||||||
|
const currentLevel = state.levelFormData.currentLevel
|
||||||
|
if (!newLevel || newLevel === currentLevel) return ''
|
||||||
|
|
||||||
|
if (newLevel === USER_LEVEL.CHANNEL) {
|
||||||
|
return '提升为渠道商后,该用户与其上级的关系将自动解除'
|
||||||
|
}
|
||||||
|
if (newLevel === USER_LEVEL.PARTNER && currentLevel === USER_LEVEL.NORMAL) {
|
||||||
|
return '提升为合伙人后,仅当上级是渠道商时保留上下级关系,否则自动解除'
|
||||||
|
}
|
||||||
|
return ''
|
||||||
|
})
|
||||||
|
|
||||||
// ============ Helper Functions ============
|
// ============ Helper Functions ============
|
||||||
|
|
||||||
function getLevelTagType(level: number): 'info' | 'success' | 'warning' | 'danger' {
|
function getLevelTagType(level: number): 'info' | 'success' | 'warning' | 'danger' {
|
||||||
|
|
@ -802,6 +947,141 @@ async function handleLevelSubmit() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function handleDetailUnbind() {
|
||||||
|
if (!state.userDetail) return
|
||||||
|
|
||||||
|
try {
|
||||||
|
await ElMessageBox.confirm(
|
||||||
|
`确认解除 "${state.userDetail.nickname}" 与上级 "${state.userDetail.parentUserNickname}" 的关系吗?`,
|
||||||
|
'解除确认',
|
||||||
|
{ confirmButtonText: '确认解除', cancelButtonText: '取消', type: 'warning' }
|
||||||
|
)
|
||||||
|
} catch {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await unbindParentUser(state.userDetail.id)
|
||||||
|
if (res.code === 0) {
|
||||||
|
ElMessage.success('解除上下级关系成功')
|
||||||
|
await loadUserDetail(state.userDetail.id)
|
||||||
|
await loadUserList()
|
||||||
|
} else {
|
||||||
|
throw new Error(res.message || '解除失败')
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
const message = error instanceof Error ? error.message : '解除失败'
|
||||||
|
ElMessage.error(message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleBindParent(row: UserItem) {
|
||||||
|
state.bindFormData = {
|
||||||
|
userId: row.id,
|
||||||
|
userNickname: row.nickname,
|
||||||
|
userUid: row.uid,
|
||||||
|
userLevel: row.userLevel,
|
||||||
|
currentParentNickname: '',
|
||||||
|
currentParentUid: '',
|
||||||
|
parentUid: ''
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取用户详情以了解当前上级
|
||||||
|
try {
|
||||||
|
const res = await getUserDetail(row.id)
|
||||||
|
if (res.code === 0 && res.data) {
|
||||||
|
state.bindFormData.currentParentNickname = res.data.parentUserNickname || ''
|
||||||
|
state.bindFormData.currentParentUid = res.data.parentUserUid || ''
|
||||||
|
state.bindFormData.userLevel = res.data.userLevel
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
|
||||||
|
state.bindDialogVisible = true
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleBindSubmit() {
|
||||||
|
if (!bindFormRef.value) return
|
||||||
|
try {
|
||||||
|
await bindFormRef.value.validate()
|
||||||
|
} catch {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 先通过 UID 查找渠道商用户
|
||||||
|
state.bindFormLoading = true
|
||||||
|
try {
|
||||||
|
const searchRes = await getUserList({
|
||||||
|
page: 1,
|
||||||
|
pageSize: 1,
|
||||||
|
uid: state.bindFormData.parentUid,
|
||||||
|
userLevel: USER_LEVEL.CHANNEL
|
||||||
|
})
|
||||||
|
|
||||||
|
if (searchRes.code !== 0 || !searchRes.data?.list?.length) {
|
||||||
|
ElMessage.error('未找到该UID对应的渠道商用户,请确认UID是否正确')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const parentUser = searchRes.data.list[0]
|
||||||
|
|
||||||
|
await ElMessageBox.confirm(
|
||||||
|
`确认将 "${state.bindFormData.userNickname}" 绑定到渠道商 "${parentUser.nickname}"(${parentUser.uid})下吗?`,
|
||||||
|
'绑定确认',
|
||||||
|
{ confirmButtonText: '确认绑定', cancelButtonText: '取消', type: 'warning' }
|
||||||
|
)
|
||||||
|
|
||||||
|
const res = await bindParentUser({
|
||||||
|
userId: state.bindFormData.userId,
|
||||||
|
parentUserId: parentUser.id
|
||||||
|
})
|
||||||
|
|
||||||
|
if (res.code === 0) {
|
||||||
|
ElMessage.success('绑定上下级关系成功')
|
||||||
|
state.bindDialogVisible = false
|
||||||
|
await loadUserList()
|
||||||
|
} else {
|
||||||
|
throw new Error(res.message || '绑定失败')
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
if (error === 'cancel' || (error instanceof Object && 'action' in error)) return
|
||||||
|
const message = error instanceof Error ? error.message : '绑定失败'
|
||||||
|
ElMessage.error(message)
|
||||||
|
} finally {
|
||||||
|
state.bindFormLoading = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleUnbindParent() {
|
||||||
|
try {
|
||||||
|
await ElMessageBox.confirm(
|
||||||
|
`确认解除 "${state.bindFormData.userNickname}" 与上级 "${state.bindFormData.currentParentNickname}" 的关系吗?`,
|
||||||
|
'解除确认',
|
||||||
|
{ confirmButtonText: '确认解除', cancelButtonText: '取消', type: 'warning' }
|
||||||
|
)
|
||||||
|
} catch {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
state.bindFormLoading = true
|
||||||
|
try {
|
||||||
|
const res = await unbindParentUser(state.bindFormData.userId)
|
||||||
|
if (res.code === 0) {
|
||||||
|
ElMessage.success('解除上下级关系成功')
|
||||||
|
state.bindDialogVisible = false
|
||||||
|
await loadUserList()
|
||||||
|
} else {
|
||||||
|
throw new Error(res.message || '解除失败')
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
const message = error instanceof Error ? error.message : '解除失败'
|
||||||
|
ElMessage.error(message)
|
||||||
|
} finally {
|
||||||
|
state.bindFormLoading = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function handleExport() {
|
async function handleExport() {
|
||||||
state.exportLoading = true
|
state.exportLoading = true
|
||||||
try {
|
try {
|
||||||
|
|
@ -1007,4 +1287,12 @@ onMounted(() => {
|
||||||
:deep(.el-descriptions) {
|
:deep(.el-descriptions) {
|
||||||
--el-descriptions-item-bordered-label-background: var(--bg-light, #f5f7fa);
|
--el-descriptions-item-bordered-label-background: var(--bg-light, #f5f7fa);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.bind-info {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-parent {
|
||||||
|
color: var(--text-secondary, #909399);
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user