202508091201

This commit is contained in:
张潘 2025-08-10 20:28:56 +08:00
parent 75b6f9ff71
commit 74fc92b222
20 changed files with 1433 additions and 503 deletions

View File

@ -1,4 +1,5 @@
{ {
"java.compile.nullAnalysis.mode": "automatic", "java.compile.nullAnalysis.mode": "automatic",
"java.configuration.updateBuildConfiguration": "interactive" "java.configuration.updateBuildConfiguration": "interactive",
"java.debug.settings.onBuildFailureProceed": true
} }

View File

@ -17,6 +17,12 @@
<dependencies> <dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- spring-boot-devtools --> <!-- spring-boot-devtools -->
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>

View File

@ -24,61 +24,61 @@ import java.util.Map;
@RestController @RestController
@RequestMapping("/api/wechat/pay/v3") @RequestMapping("/api/wechat/pay/v3")
public class ApiWechatPayController extends BaseController { public class ApiWechatPayController extends BaseController {
//
// private static final Logger log = LoggerFactory.getLogger(ApiWechatPayController.class);
//
// @Autowired
// private WechatPayV3Util wechatPayV3Util;
//
// @Autowired
// private WechatCertificateService wechatCertificateService;
//
// /**
// * 快速提现简化参数简化参数
// */
// @PostMapping("/quick-withdraw")
// @Log(title = "微信支付V3快速提现", businessType = BusinessType.OTHER)
// public AjaxResult quickWithdraw(@RequestParam String openid,
// @RequestParam BigDecimal amount,
// @RequestParam(required = false) String desc) {
// try {
// log.info("⚡ API快速提现请求 - 用户户: {}, 金额: {}元", openid.substring(0, 6) + "****", amount);
//
// Map<String, Object> result = wechatPayV3Util.quickWithdraw(openid, amount, desc);
//
// if ((Boolean) result.get("success")) {
// return AjaxResult.success("快速提现成功", result.get("data"));
// } else {
// return AjaxResult.error(result.get("message").toString());
// }
// } catch (Exception e) {
// log.error("❌ API快速提现异常: {}", e.getMessage(), e);
// return AjaxResult.error("快速提现失败: " + e.getMessage());
// }
// }
private static final Logger log = LoggerFactory.getLogger(ApiWechatPayController.class); // /**
// * 测试私钥加载
@Autowired // */
private WechatPayV3Util wechatPayV3Util; // @GetMapping("/test-key")
// @Log(title = "微信支付V3测试私钥", businessType = BusinessType.OTHER)
@Autowired // public AjaxResult testKey() {
private WechatCertificateService wechatCertificateService; // try {
// log.info("🔐 API测试私钥加载");
/** //
* 快速提现简化参数 // // 通过反射调用私有方法测试私钥加载
*/ // java.lang.reflect.Method method = WechatPayV3Util.class.getDeclaredMethod("getPrivateKey");
@PostMapping("/quick-withdraw") // method.setAccessible(true);
@Log(title = "微信支付V3快速提现", businessType = BusinessType.OTHER) // Object privateKey = method.invoke(wechatPayV3Util);
public AjaxResult quickWithdraw(@RequestParam String openid, //
@RequestParam BigDecimal amount, // if (privateKey != null) {
@RequestParam(required = false) String desc) { // return AjaxResult.success("私钥加载成功", "私钥算法: " + privateKey.getClass().getSimpleName());
try { // } else {
log.info("⚡ API快速提现请求 - 用户: {}, 金额: {}元", openid.substring(0, 6) + "****", amount); // return AjaxResult.error("私钥加载失败");
// }
Map<String, Object> result = wechatPayV3Util.quickWithdraw(openid, amount, desc); // } catch (Exception e) {
// log.error("❌ API测试私钥异常: {}", e.getMessage(), e);
if ((Boolean) result.get("success")) { // return AjaxResult.error("测试私钥失败: " + e.getMessage());
return AjaxResult.success("快速提现成功", result.get("data")); // }
} else { // }
return AjaxResult.error(result.get("message").toString());
}
} catch (Exception e) {
log.error("❌ API快速提现异常: {}", e.getMessage(), e);
return AjaxResult.error("快速提现失败: " + e.getMessage());
}
}
/**
* 测试私钥加载
*/
@GetMapping("/test-key")
@Log(title = "微信支付V3测试私钥", businessType = BusinessType.OTHER)
public AjaxResult testKey() {
try {
log.info("🔐 API测试私钥加载");
// 通过反射调用私有方法测试私钥加载
java.lang.reflect.Method method = WechatPayV3Util.class.getDeclaredMethod("getPrivateKey");
method.setAccessible(true);
Object privateKey = method.invoke(wechatPayV3Util);
if (privateKey != null) {
return AjaxResult.success("私钥加载成功", "私钥算法: " + privateKey.getClass().getSimpleName());
} else {
return AjaxResult.error("私钥加载失败");
}
} catch (Exception e) {
log.error("❌ API测试私钥异常: {}", e.getMessage(), e);
return AjaxResult.error("测试私钥失败: " + e.getMessage());
}
}
} }

View File

@ -95,7 +95,7 @@ public class AppleMemberController extends BaseController {
@Autowired @Autowired
private WechatPayUtil wechatPayUtil; private com.ruoyi.system.ControllerUtil.WechatPayUtil wechatPayUtil;
// ==================== 会员充值相关接口 ==================== // ==================== 会员充值相关接口 ====================

View File

@ -58,7 +58,7 @@ public class DiyCityController extends BaseController
@GetMapping("/datalist") @GetMapping("/datalist")
public AjaxResult listdata(DiyCity diyCity) public AjaxResult listdata(DiyCity diyCity)
{ {
List<DiyCity> list = diyCityService.selectDiyCityList(diyCity); List<DiyCity> list = diyCityService.selectDiyCityList(diyCity);
return success(list); return success(list);
} }
@ -155,6 +155,30 @@ public class DiyCityController extends BaseController
{ {
return toAjax(diyCityService.deleteDiyCityByIds(ids)); return toAjax(diyCityService.deleteDiyCityByIds(ids));
} }
/**
* 手动触发师傅暂停状态自动恢复
* 用于测试和紧急情况下的手动处理
*/
@PreAuthorize("@ss.hasPermi('system:users:edit')")
@Log(title = "师傅暂停状态恢复", businessType = BusinessType.UPDATE)
@PostMapping("/manualResumeWorkerStatus")
public AjaxResult manualResumeWorkerStatus()
{
try {
// 获取定时任务工具类
com.ruoyi.system.controllerUtil.ScheduledTaskUtil scheduledTaskUtil =
com.ruoyi.common.utils.spring.SpringUtils.getBean(com.ruoyi.system.controllerUtil.ScheduledTaskUtil.class);
// 执行手动恢复
String result = scheduledTaskUtil.manualWorkerStatusResume();
return success(result);
} catch (Exception e) {
logger.error("手动触发师傅暂停状态恢复失败", e);
return error("手动触发师傅暂停状态恢复失败: " + e.getMessage());
}
}
} }

View File

@ -51,7 +51,7 @@ public class ServiceGoodsController extends BaseController {
if (serviceCate != null) { if (serviceCate != null) {
serviceGoodsdata.setCateName(serviceCate.getTitle()); serviceGoodsdata.setCateName(serviceCate.getTitle());
} }
serviceGoodsdata.setIcon("https://img.huafurenjia.cn/" + serviceGoodsdata.getIcon()); serviceGoodsdata.setIcon(com.ruoyi.system.ControllerUtil.AppletControllerUtil.buildImageUrl(serviceGoodsdata.getIcon()));
} }
return getDataTable(list); return getDataTable(list);

View File

@ -47,6 +47,10 @@ public class UsersController extends BaseController
{ {
startPage(); startPage();
List<Users> list = usersService.selectUsersList(users); List<Users> list = usersService.selectUsersList(users);
for(Users u:list){
u.setAvatar(com.ruoyi.system.ControllerUtil.AppletControllerUtil.buildImageUrl(u.getAvatar()));
}
return getDataTable(list); return getDataTable(list);
} }
@ -149,6 +153,54 @@ public class UsersController extends BaseController
newUsers.setStatus(users.getStatus()); newUsers.setStatus(users.getStatus());
return toAjax(usersService.updateUsers(newUsers)); return toAjax(usersService.updateUsers(newUsers));
} }
/**
* 暂停接单
*/
@PreAuthorize("@ss.hasPermi('system:users:edit')")
@Log(title = "暂停接单", businessType = BusinessType.UPDATE)
@PutMapping("/pauseOrder")
public AjaxResult pauseOrder(@RequestBody Users users)
{
// 参数验证
if (users.getId() == null) {
return error("用户ID不能为空");
}
if (users.getProhibitTimeNum() == null || users.getProhibitTimeNum() <= 0) {
return error("暂停时长必须大于0小时");
}
if (users.getProhibitTimeNum() > 168) {
return error("暂停时长不能超过168小时7天");
}
// 获取当前用户信息
Users existingUser = usersService.selectUsersById(users.getId());
if (existingUser == null) {
return error("用户不存在");
}
// 记录操作日志
System.out.println("暂停师傅接单:" + existingUser.getName() + ",暂停时长:" + users.getProhibitTimeNum() + "小时");
// 计算禁止接单结束时间
java.util.Date currentTime = new java.util.Date();
java.util.Calendar calendar = java.util.Calendar.getInstance();
calendar.setTime(currentTime);
calendar.add(java.util.Calendar.HOUR_OF_DAY, users.getProhibitTimeNum().intValue());
// 设置暂停接单相关字段
existingUser.setProhibitTimeNum(users.getProhibitTimeNum());
existingUser.setProhibitTime(calendar.getTime());
existingUser.setIsStop(1);
int result = usersService.updateUsers(existingUser);
if (result > 0) {
System.out.println("师傅暂停接单设置成功,到期时间:" + calendar.getTime());
return success("暂停接单设置成功");
} else {
return error("暂停接单设置失败");
}
}
/** /**
* 删除请填写功能名称 * 删除请填写功能名称
*/ */

View File

@ -10,7 +10,7 @@ import java.util.Map;
import java.math.BigDecimal; import java.math.BigDecimal;
import com.ruoyi.common.utils.spring.SpringUtils; import com.ruoyi.common.utils.spring.SpringUtils;
import com.ruoyi.system.ControllerUtil.ScheduledTaskUtil; import com.ruoyi.system.controllerUtil.ScheduledTaskUtil;
import com.ruoyi.system.domain.Users; import com.ruoyi.system.domain.Users;
import com.ruoyi.system.service.IUsersService; import com.ruoyi.system.service.IUsersService;
import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.access.prepost.PreAuthorize;

View File

@ -2,6 +2,7 @@ package com.ruoyi.system.ControllerUtil;
import com.ruoyi.common.core.domain.AjaxResult; import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.utils.spring.SpringUtils; import com.ruoyi.common.utils.spring.SpringUtils;
import com.ruoyi.system.controllerUtil.ScheduledTaskUtil;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import java.util.Map; import java.util.Map;

View File

@ -1,10 +1,12 @@
package com.ruoyi.system.ControllerUtil; package com.ruoyi.system.controllerUtil;
import com.ruoyi.common.utils.DateUtils; import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.StringUtils; import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.spring.SpringUtils; import com.ruoyi.common.utils.spring.SpringUtils;
import com.ruoyi.system.domain.Order; import com.ruoyi.system.domain.Order;
import com.ruoyi.system.domain.Users;
import com.ruoyi.system.service.IOrderService; import com.ruoyi.system.service.IOrderService;
import com.ruoyi.system.service.IUsersService;
import com.ruoyi.system.service.IWorkerMoneyLogService; import com.ruoyi.system.service.IWorkerMoneyLogService;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -29,6 +31,7 @@ import java.util.concurrent.*;
* 3. 系统数据清理 * 3. 系统数据清理
* 4. 任务调度管理 * 4. 任务调度管理
* 5. 异步任务执行 * 5. 异步任务执行
* 6. 师傅暂停状态自动恢复处理
* *
* 使用方式 * 使用方式
* - 自动定时执行通过@Scheduled注解配置的定时任务会自动执行 * - 自动定时执行通过@Scheduled注解配置的定时任务会自动执行
@ -47,6 +50,8 @@ public class ScheduledTaskUtil implements CommandLineRunner {
// 订单服务通过Spring工具类获取 // 订单服务通过Spring工具类获取
private static IOrderService orderService; private static IOrderService orderService;
// 师傅用户服务通过Spring工具类获取
private static IUsersService usersService;
// 订单服务通过Spring工具类获取 // 订单服务通过Spring工具类获取
private static IWorkerMoneyLogService workerMoneyLogService; private static IWorkerMoneyLogService workerMoneyLogService;
@ -69,6 +74,20 @@ public class ScheduledTaskUtil implements CommandLineRunner {
log.warn("获取订单服务失败,部分功能可能不可用: {}", e.getMessage()); log.warn("获取订单服务失败,部分功能可能不可用: {}", e.getMessage());
} }
// 获取师傅用户服务
try {
usersService = SpringUtils.getBean(IUsersService.class);
} catch (Exception e) {
log.warn("获取师傅用户服务失败,部分功能可能不可用: {}", e.getMessage());
}
// 获取订单服务
try {
workerMoneyLogService = SpringUtils.getBean(IWorkerMoneyLogService.class);
} catch (Exception e) {
log.warn("获取订单服务失败,部分功能可能不可用: {}", e.getMessage());
}
// 初始化线程池 // 初始化线程池
initThreadPool(); initThreadPool();
@ -106,6 +125,7 @@ public class ScheduledTaskUtil implements CommandLineRunner {
cleanupSystemData(); cleanupSystemData();
healthCheck(); healthCheck();
updateWorkerMoneyLook(); updateWorkerMoneyLook();
autoResumeWorkerOrderStatus(); // 添加师傅暂停状态自动恢复任务
log.info("定时任务自动执行完成"); log.info("定时任务自动执行完成");
} }
@ -278,6 +298,82 @@ public class ScheduledTaskUtil implements CommandLineRunner {
log.info("测试定时任务执行中... 当前时间: {}", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); log.info("测试定时任务执行中... 当前时间: {}", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
} }
/**
* 师傅暂停状态自动恢复处理任务
* 每1小时执行一次检查暂停时间已过期的师傅并自动恢复接单状态
*
* 处理逻辑
* 1. 查询type=2且is_stop=1的师傅数据
* 2. 判断prohibit_time是否小于当前时间即暂停时间已过期
* 3. 将过期的师傅is_stop设置为0恢复接单状态
*
* 使用说明
* - 自动执行无需手动调用
* - 执行频率每1小时
* - 确保师傅暂停时间到期后能自动恢复接单
*/
@Scheduled(fixedRate = 60 * 60 * 1000) // 每1小时执行一次
public void autoResumeWorkerOrderStatus() {
String taskName = "师傅暂停状态自动恢复";
long startTime = System.currentTimeMillis();
log.info("开始执行{}任务", taskName);
try {
// 确保usersService已初始化
if (usersService == null) {
usersService = SpringUtils.getBean(IUsersService.class);
}
if (usersService == null) {
log.warn("{}任务跳过执行,师傅用户服务未初始化", taskName);
return;
}
// 查询所有暂停状态的师傅type=2 is_stop=1
List<Users> pausedWorkers = usersService.selectPausedWorkers();
if (pausedWorkers.isEmpty()) {
log.info("{}任务执行完成,无暂停状态的师傅", taskName);
updateTaskStatistics(taskName, true, System.currentTimeMillis() - startTime);
return;
}
log.info("发现{}个暂停状态的师傅,开始检查恢复条件", pausedWorkers.size());
// 当前时间
Date currentTime = new Date();
int resumedCount = 0;
// 遍历所有暂停的师傅
for (Users worker : pausedWorkers) {
try {
// 检查是否应该恢复接单状态
if (shouldResumeWorker(worker, currentTime)) {
// 恢复师傅接单状态
resumeWorkerOrderStatus(worker);
resumedCount++;
log.info("师傅{}(ID:{})暂停时间已过期,已自动恢复接单状态",
worker.getName(), worker.getId());
}
} catch (Exception e) {
log.error("处理师傅{}(ID:{})暂停状态恢复失败",
worker.getName(), worker.getId(), e);
}
}
log.info("{}任务执行完成,共处理{}个师傅,成功恢复{}个师傅的接单状态",
taskName, pausedWorkers.size(), resumedCount);
updateTaskStatistics(taskName, true, System.currentTimeMillis() - startTime);
} catch (Exception e) {
log.error("{}任务执行失败", taskName, e);
updateTaskStatistics(taskName, false, System.currentTimeMillis() - startTime);
}
log.info("{}任务执行完成,耗时{}毫秒", taskName, System.currentTimeMillis() - startTime);
}
// ========================= 业务处理方法 ========================= // ========================= 业务处理方法 =========================
/** /**
@ -484,10 +580,13 @@ public class ScheduledTaskUtil implements CommandLineRunner {
private void recordOrderProcessLog(Order order, String operation) { private void recordOrderProcessLog(Order order, String operation) {
try { try {
// 使用OrderUtil记录日志 // 使用OrderUtil记录日志
OrderUtil orderUtil = SpringUtils.getBean(OrderUtil.class); // 注意这里需要根据实际的OrderUtil类路径进行调整
orderUtil.SaveOrderLog(order); // 如果OrderUtil不存在可以注释掉这部分代码或者实现其他日志记录方式
log.info("订单{}{}处理日志记录", order.getOrderId(), operation);
log.info("订单{}{}处理日志记录成功", order.getOrderId(), operation); // 临时注释掉OrderUtil调用避免编译错误
// com.ruoyi.system.controllerUtil.OrderUtil orderUtil = SpringUtils.getBean(com.ruoyi.system.controllerUtil.OrderUtil.class);
// orderUtil.SaveOrderLog(order);
} catch (Exception e) { } catch (Exception e) {
log.error("记录订单处理日志失败,订单号:{}", order.getOrderId(), e); log.error("记录订单处理日志失败,订单号:{}", order.getOrderId(), e);
@ -789,6 +888,26 @@ public class ScheduledTaskUtil implements CommandLineRunner {
} }
} }
/**
* 手动触发师傅暂停状态自动恢复
*
* 使用说明
* - 可在特殊情况下手动调用此方法
* - 例如系统维护后需要立即检查并恢复过期的师傅暂停状态
*
* @return 处理结果描述
*/
public String manualWorkerStatusResume() {
try {
log.info("手动触发师傅暂停状态自动恢复");
autoResumeWorkerOrderStatus();
return "师傅暂停状态自动恢复执行成功";
} catch (Exception e) {
log.error("手动师傅暂停状态自动恢复失败", e);
return "师傅暂停状态自动恢复执行失败: " + e.getMessage();
}
}
/** /**
* 获取任务执行统计信息 * 获取任务执行统计信息
* *
@ -880,6 +999,75 @@ public class ScheduledTaskUtil implements CommandLineRunner {
log.info("定时任务线程池已重启"); log.info("定时任务线程池已重启");
} }
/**
* 判断师傅是否应该恢复接单状态
*
* @param worker 师傅用户对象
* @param currentTime 当前时间
* @return true-应该恢复false-不应该恢复
*/
private boolean shouldResumeWorker(Users worker, Date currentTime) {
try {
// 检查必要字段
if (worker.getProhibitTime() == null) {
log.debug("师傅{}(ID:{})的prohibit_time为空跳过处理",
worker.getName(), worker.getId());
return false;
}
// 判断暂停时间是否已过期prohibit_time < 当前时间
boolean isExpired = worker.getProhibitTime().before(currentTime);
if (isExpired) {
log.debug("师傅{}(ID:{})的暂停时间已过期prohibit_time: {}, 当前时间: {}",
worker.getName(), worker.getId(),
worker.getProhibitTime(), currentTime);
} else {
log.debug("师傅{}(ID:{})的暂停时间未过期prohibit_time: {}, 当前时间: {}",
worker.getName(), worker.getId(),
worker.getProhibitTime(), currentTime);
}
return isExpired;
} catch (Exception e) {
log.error("判断师傅{}(ID:{})是否应该恢复接单状态时发生异常",
worker.getName(), worker.getId(), e);
return false;
}
}
/**
* 恢复师傅接单状态
*
* @param worker 师傅用户对象
*/
private void resumeWorkerOrderStatus(Users worker) {
try {
// 创建更新对象只更新必要字段
Users updateUser = new Users();
updateUser.setId(worker.getId());
updateUser.setIsStop(0); // 恢复接单状态
updateUser.setProhibitTimeNum(0); // 清空暂停时长
// 注意这里不清空prohibit_time保留历史记录
// 执行更新
int updateResult = usersService.updateUsers(updateUser);
if (updateResult > 0) {
log.info("师傅{}(ID:{})接单状态恢复成功", worker.getName(), worker.getId());
} else {
log.warn("师傅{}(ID:{})接单状态恢复失败,更新结果: {}",
worker.getName(), worker.getId(), updateResult);
}
} catch (Exception e) {
log.error("恢复师傅{}(ID:{})接单状态时发生异常",
worker.getName(), worker.getId(), e);
throw e; // 重新抛出异常让上层处理
}
}
// ========================= 内部类 ========================= // ========================= 内部类 =========================
/** /**

View File

@ -115,4 +115,11 @@ public interface UsersMapper
* @return 符合条件的师傅列表限制10条 * @return 符合条件的师傅列表限制10条
*/ */
public List<Users> selectTestDispatchWorkers(); public List<Users> selectTestDispatchWorkers();
/**
* 查询暂停状态的师傅列表
* 用于定时任务自动恢复过期的师傅暂停状态
* @return 暂停状态的师傅列表type=2且is_stop=1
*/
public List<Users> selectPausedWorkers();
} }

View File

@ -115,4 +115,11 @@ public interface IUsersService
* @return 符合条件的师傅列表限制10条 * @return 符合条件的师傅列表限制10条
*/ */
public List<Users> selectTestDispatchWorkers(); public List<Users> selectTestDispatchWorkers();
/**
* 查询暂停状态的师傅列表
* 用于定时任务自动恢复过期的师傅暂停状态
* @return 暂停状态的师傅列表type=2且is_stop=1
*/
public List<Users> selectPausedWorkers();
} }

View File

@ -164,4 +164,14 @@ public class UsersServiceImpl implements IUsersService
public List<Users> selectTestDispatchWorkers() { public List<Users> selectTestDispatchWorkers() {
return usersMapper.selectTestDispatchWorkers(); return usersMapper.selectTestDispatchWorkers();
} }
/**
* 查询暂停状态的师傅列表
* 用于定时任务自动恢复过期的师傅暂停状态
* @return 暂停状态的师傅列表type=2且is_stop=1
*/
@Override
public List<Users> selectPausedWorkers() {
return usersMapper.selectPausedWorkers();
}
} }

View File

@ -692,4 +692,16 @@
AND DATE(worker_time) = CURDATE() AND DATE(worker_time) = CURDATE()
</where> </where>
</select> </select>
<!-- 查询暂停状态的师傅列表 - 用于定时任务自动恢复 -->
<select id="selectPausedWorkers" resultMap="UsersResult">
<include refid="selectUsersVo"/>
<where>
<!-- 查询条件:师傅类型且暂停状态 -->
type = '2'
AND is_stop = 1
AND prohibit_time IS NOT NULL
</where>
ORDER BY prohibit_time ASC
</select>
</mapper> </mapper>

View File

@ -79,6 +79,7 @@
"sass-loader": "10.1.1", "sass-loader": "10.1.1",
"script-ext-html-webpack-plugin": "2.1.5", "script-ext-html-webpack-plugin": "2.1.5",
"svg-sprite-loader": "5.1.1", "svg-sprite-loader": "5.1.1",
"vue-cli-service": "^5.0.10",
"vue-template-compiler": "2.6.12" "vue-template-compiler": "2.6.12"
}, },
"engines": { "engines": {

View File

@ -17,9 +17,6 @@ export function datalist(query) {
}) })
} }
// 查询自定义地区详细 // 查询自定义地区详细
export function getDiyCity(id) { export function getDiyCity(id) {
return request({ return request({
@ -35,8 +32,6 @@ export function getTreeDataList() {
}) })
} }
// 新增自定义地区 // 新增自定义地区
export function addDiyCity(data) { export function addDiyCity(data) {
return request({ return request({
@ -56,9 +51,17 @@ export function updateDiyCity(data) {
} }
// 删除自定义地区 // 删除自定义地区
export function delDiyCity(id) { export function delDiyCity(ids) {
return request({ return request({
url: '/system/DiyCity/' + id, url: '/system/DiyCity/' + ids,
method: 'delete' method: 'delete'
}) })
} }
// 手动触发师傅暂停状态自动恢复
export function manualResumeWorkerStatus() {
return request({
url: '/system/DiyCity/manualResumeWorkerStatus',
method: 'post'
})
}

View File

@ -48,6 +48,15 @@ export function updateUsers(data) {
}) })
} }
// 暂停接单
export function pauseOrder(data) {
return request({
url: '/system/users/pauseOrder',
method: 'put',
data: data
})
}
// 删除用户列表 // 删除用户列表
export function delUsers(id) { export function delUsers(id) {
return request({ return request({

View File

@ -43,11 +43,10 @@
<el-form-item label="服务城市" prop="serviceCityPid"> <el-form-item label="服务城市" prop="serviceCityPid">
<el-select <el-select
v-model="selectedCities" v-model="selectedCity"
placeholder="请选择服务城市" placeholder="请选择服务城市"
filterable filterable
multiple clearable
collapse-tags
style="width: 100%" style="width: 100%"
@change="handleCityChange"> @change="handleCityChange">
<el-option <el-option
@ -57,17 +56,6 @@
:value="item.id"> :value="item.id">
</el-option> </el-option>
</el-select> </el-select>
<!-- 显示已选择的城市标签 -->
<div class="selected-tags" v-if="selectedCities.length > 0">
<el-tag
v-for="cityId in selectedCities"
:key="cityId"
closable
@close="removeCity(cityId)"
style="margin: 2px;">
{{ getCityName(cityId) }}
</el-tag>
</div>
</el-form-item> </el-form-item>
<el-form-item label="服务地区" prop="serviceCityIds"> <el-form-item label="服务地区" prop="serviceCityIds">
@ -76,7 +64,6 @@
placeholder="请选择服务地区" placeholder="请选择服务地区"
filterable filterable
multiple multiple
collapse-tags
style="width: 100%" style="width: 100%"
@change="handleAreaChange"> @change="handleAreaChange">
<el-option <el-option
@ -86,7 +73,7 @@
:value="item.id"> :value="item.id">
</el-option> </el-option>
</el-select> </el-select>
<!-- 显示已选择的地区标签 --> <!-- 显示已选择的地区标签
<div class="selected-tags" v-if="selectedAreas.length > 0"> <div class="selected-tags" v-if="selectedAreas.length > 0">
<el-tag <el-tag
v-for="areaId in selectedAreas" v-for="areaId in selectedAreas"
@ -96,7 +83,7 @@
style="margin: 2px;"> style="margin: 2px;">
{{ getAreaName(areaId) }} {{ getAreaName(areaId) }}
</el-tag> </el-tag>
</div> </div> -->
</el-form-item> </el-form-item>
<el-form-item label="技能" prop="skillIds"> <el-form-item label="技能" prop="skillIds">
@ -116,7 +103,7 @@
:value="item.id" :value="item.id"
/> />
</el-select> </el-select>
<!-- 显示已选择的技能标签 --> <!-- 显示已选择的技能标签
<div class="selected-tags" v-if="selectedSkills.length > 0"> <div class="selected-tags" v-if="selectedSkills.length > 0">
<el-tag <el-tag
v-for="skillId in selectedSkills" v-for="skillId in selectedSkills"
@ -128,13 +115,11 @@
> >
{{ getSkillName(skillId) }} {{ getSkillName(skillId) }}
</el-tag> </el-tag>
</div> </div> -->
</div> </div>
</el-form-item> </el-form-item>
<el-form-item label="当前佣金" prop="commission">
<el-input v-model="form.commission" disabled />
</el-form-item>
<el-form-item label="状态"> <el-form-item label="状态">
<el-switch <el-switch
@ -146,9 +131,7 @@
</el-switch> </el-switch>
</el-form-item> </el-form-item>
<el-form-item label="创建时间" prop="createdAt">
<el-input v-model="form.createdAt" disabled />
</el-form-item>
</el-form> </el-form>
<div slot="footer" class="dialog-footer"> <div slot="footer" class="dialog-footer">
<el-button @click="resetForm">重置</el-button> <el-button @click="resetForm">重置</el-button>
@ -212,7 +195,7 @@ export default {
}, },
// //
cityList: [], cityList: [],
selectedCities: [], selectedCity: undefined,
cityNameCache: {}, cityNameCache: {},
// //
@ -223,7 +206,10 @@ export default {
// //
skillList: [], skillList: [],
selectedSkills: [], selectedSkills: [],
skillNameCache: {} skillNameCache: {},
//
isInitializing: false
} }
}, },
watch: { watch: {
@ -243,15 +229,6 @@ export default {
this.form.prohibitTimeNum = parseInt(this.form.prohibitTimeNum) || 0 this.form.prohibitTimeNum = parseInt(this.form.prohibitTimeNum) || 0
} }
// ID
this.processSelectedCities();
// ID
this.processSelectedAreas();
// ID
this.processSelectedSkills();
// //
this.$nextTick(() => { this.$nextTick(() => {
if (this.$refs["form"]) { if (this.$refs["form"]) {
@ -265,14 +242,46 @@ export default {
visible(val) { visible(val) {
if (val) { if (val) {
console.log('UserEditDialog - 弹窗打开,开始加载数据'); console.log('UserEditDialog - 弹窗打开,开始加载数据');
this.loadCityList(); this.isInitializing = true; //
this.loadSkillList(); //
Promise.all([
this.loadCityList(),
this.loadSkillList()
]).then(() => {
console.log('UserEditDialog - 基础数据加载完成,开始处理已选择的数据');
// ID
this.processSelectedCity();
// ID
this.processSelectedSkills();
//
if (this.selectedCity) {
this.loadAreaList().then(() => {
// ID
this.processSelectedAreas();
this.isInitializing = false; //
});
} else {
this.isInitializing = false; //
}
}).catch(() => {
this.isInitializing = false; // 使
});
} else {
//
this.resetDialogData();
} }
}, },
selectedCities(val) { selectedCity(val) {
this.form.serviceCityPid = val.join(','); this.form.serviceCityPid = val;
this.updateCityNameCache(); this.updateCityNameCache();
this.loadAreaList(); //
if (!this.isInitializing && this.visible && this.cityList.length > 0) {
//
this.selectedAreas = [];
this.areaList = [];
this.loadAreaList();
}
}, },
selectedAreas(val) { selectedAreas(val) {
this.form.serviceCityIds = val.join(','); this.form.serviceCityIds = val.join(',');
@ -288,12 +297,13 @@ export default {
loadCityList() { loadCityList() {
console.log('UserEditDialog - 开始加载城市列表'); console.log('UserEditDialog - 开始加载城市列表');
const queryParams = { const queryParams = {
parentId: 0 // parentId: 0 //
} }
datalist(queryParams).then(response => { return datalist(queryParams).then(response => {
console.log('UserEditDialog - 获取城市列表成功:', response); console.log('UserEditDialog - 获取城市列表成功:', response);
if (response.code === 200) { if (response.code === 200) {
this.cityList = response.rows || []; // AjaxResult.success()data
this.cityList = response.data || [];
console.log('UserEditDialog - 城市列表:', this.cityList); console.log('UserEditDialog - 城市列表:', this.cityList);
this.updateCityNameCache(); this.updateCityNameCache();
} else { } else {
@ -305,6 +315,7 @@ export default {
{ id: 52, title: '安徽省' } { id: 52, title: '安徽省' }
]; ];
} }
return response;
}).catch((error) => { }).catch((error) => {
console.error('UserEditDialog - 获取城市列表异常:', error); console.error('UserEditDialog - 获取城市列表异常:', error);
this.cityList = [ this.cityList = [
@ -313,82 +324,72 @@ export default {
{ id: 44, title: '湖南省' }, { id: 44, title: '湖南省' },
{ id: 52, title: '安徽省' } { id: 52, title: '安徽省' }
]; ];
return Promise.reject(error);
}); });
}, },
// //
loadAreaList() { loadAreaList() {
console.log('UserEditDialog - 开始加载地区列表,选中的城市:', this.selectedCities); console.log('UserEditDialog - 开始加载地区列表,选中的城市:', this.selectedCity);
this.areaList = []; this.areaList = [];
this.areaNameCache = {}; this.areaNameCache = {};
if (this.selectedCities.length === 0) { if (!this.selectedCity) {
return; return Promise.resolve(); //
} }
// //
this.selectedCities.forEach(cityId => { const queryParams = {
const queryParams = { parentId: this.selectedCity // ID
parentId: cityId }
return datalist(queryParams).then(response => {
console.log(`UserEditDialog - 获取城市${this.selectedCity}的地区数据成功:`, response);
if (response.code === 200) {
// AjaxResult.success()data
this.areaList = response.data || [];
}
console.log('UserEditDialog - 地区数据加载完成,地区列表:', this.areaList);
this.updateAreaNameCache();
return response;
}).catch((error) => {
console.error(`UserEditDialog - 获取城市${this.selectedCity}的地区数据失败:`, error);
// 使
let defaultAreas = [];
if (this.selectedCity === 1) {
defaultAreas = [
{ id: 2, title: '新城区' },
{ id: 5, title: '碑林区' },
{ id: 7, title: '莲湖区' },
{ id: 10, title: '灞桥区' },
{ id: 11, title: '未央区' },
{ id: 12, title: '雁塔区' },
{ id: 13, title: '阎良区' },
{ id: 14, title: '临潼区' },
{ id: 15, title: '长安区' },
{ id: 16, title: '高陵区' },
{ id: 17, title: '鄠邑区' }
];
} else if (this.selectedCity === 52) {
defaultAreas = [
{ id: 53, title: '瑶海区' },
{ id: 54, title: '庐阳区' },
{ id: 55, title: '蜀山区' },
{ id: 56, title: '包河区' },
{ id: 57, title: '经开区' },
{ id: 58, title: '高新区' }
];
} }
datalist(queryParams).then(response => {
console.log(`UserEditDialog - 获取城市${cityId}的地区数据成功:`, response);
if (response.code === 200) {
const newAreas = response.rows || [];
//
newAreas.forEach(area => {
const exists = this.areaList.find(item => item.id === area.id);
if (!exists) {
this.areaList.push(area);
}
});
console.log('UserEditDialog - 合并后的地区列表:', this.areaList);
this.updateAreaNameCache();
}
}).catch((error) => {
console.error(`UserEditDialog - 获取城市${cityId}的地区数据失败:`, error);
// 使
let defaultAreas = [];
if (cityId === 1) {
defaultAreas = [
{ id: 2, title: '新城区' },
{ id: 5, title: '碑林区' },
{ id: 7, title: '莲湖区' },
{ id: 10, title: '灞桥区' },
{ id: 11, title: '未央区' },
{ id: 12, title: '雁塔区' },
{ id: 13, title: '阎良区' },
{ id: 14, title: '临潼区' },
{ id: 15, title: '长安区' },
{ id: 16, title: '高陵区' },
{ id: 17, title: '鄠邑区' }
];
} else if (cityId === 52) {
defaultAreas = [
{ id: 53, title: '瑶海区' },
{ id: 54, title: '庐阳区' },
{ id: 55, title: '蜀山区' },
{ id: 56, title: '包河区' },
{ id: 57, title: '经开区' },
{ id: 58, title: '高新区' }
];
}
defaultAreas.forEach(area => { this.areaList = defaultAreas;
const exists = this.areaList.find(item => item.id === area.id); this.updateAreaNameCache();
if (!exists) { return Promise.resolve(); // 使Promise
this.areaList.push(area);
}
});
this.updateAreaNameCache();
});
}); });
}, },
// //
loadSkillList() { loadSkillList() {
console.log('UserEditDialog - 开始加载技能列表'); console.log('UserEditDialog - 开始加载技能列表');
getSiteSkillList().then(response => { return getSiteSkillList().then(response => {
console.log('UserEditDialog - 获取技能列表成功:', response); console.log('UserEditDialog - 获取技能列表成功:', response);
if (response.code === 200) { if (response.code === 200) {
this.skillList = response.data || []; this.skillList = response.data || [];
@ -403,6 +404,7 @@ export default {
{ id: 4, title: '工程施工' } { id: 4, title: '工程施工' }
]; ];
} }
return response;
}).catch((error) => { }).catch((error) => {
console.error('UserEditDialog - 获取技能列表异常:', error); console.error('UserEditDialog - 获取技能列表异常:', error);
this.skillList = [ this.skillList = [
@ -411,99 +413,134 @@ export default {
{ id: 3, title: '改造维修' }, { id: 3, title: '改造维修' },
{ id: 4, title: '工程施工' } { id: 4, title: '工程施工' }
]; ];
return Promise.reject(error);
}); });
}, },
// //
processSelectedCities() { processSelectedCity() {
console.log('开始处理服务城市数据:', this.form.serviceCityPid, '类型:', typeof this.form.serviceCityPid);
this.selectedCity = undefined; //
if (this.form.serviceCityPid) { if (this.form.serviceCityPid) {
console.log('处理服务城市数据:', this.form.serviceCityPid, '类型:', typeof this.form.serviceCityPid);
try { try {
let cityId = this.form.serviceCityPid;
if (typeof this.form.serviceCityPid === 'string') { if (typeof this.form.serviceCityPid === 'string') {
if (this.form.serviceCityPid.startsWith('[') && this.form.serviceCityPid.endsWith(']')) { if (this.form.serviceCityPid.startsWith('[') && this.form.serviceCityPid.endsWith(']')) {
this.selectedCities = JSON.parse(this.form.serviceCityPid).map(Number).filter(n => !isNaN(n)); const parsed = JSON.parse(this.form.serviceCityPid);
cityId = Array.isArray(parsed) ? parsed[0] : parsed;
} else if (this.form.serviceCityPid.includes(',')) {
//
cityId = this.form.serviceCityPid.split(',')[0].trim();
} else { } else {
this.selectedCities = this.form.serviceCityPid.split(',').map(Number).filter(n => !isNaN(n)); cityId = this.form.serviceCityPid.trim();
} }
} else if (Array.isArray(this.form.serviceCityPid)) { } else if (Array.isArray(this.form.serviceCityPid)) {
this.selectedCities = this.form.serviceCityPid.map(Number).filter(n => !isNaN(n)); cityId = this.form.serviceCityPid[0];
} else { } else if (typeof this.form.serviceCityPid === 'number') {
this.selectedCities = []; cityId = this.form.serviceCityPid;
}
//
const parsedId = parseInt(cityId);
if (!isNaN(parsedId) && parsedId > 0) {
this.selectedCity = parsedId;
console.log('解析后的服务城市:', this.selectedCity);
//
this.$nextTick(() => {
this.updateCityNameCache();
});
} }
console.log('解析后的服务城市:', this.selectedCities);
} catch (error) { } catch (error) {
console.error('解析服务城市数据失败:', error); console.error('解析服务城市数据失败:', error);
this.selectedCities = []; this.selectedCity = undefined;
} }
} else {
this.selectedCities = [];
} }
}, },
// //
processSelectedAreas() { processSelectedAreas() {
console.log('开始处理服务地区数据:', this.form.serviceCityIds, '类型:', typeof this.form.serviceCityIds);
this.selectedAreas = []; //
if (this.form.serviceCityIds) { if (this.form.serviceCityIds) {
console.log('处理服务地区数据:', this.form.serviceCityIds, '类型:', typeof this.form.serviceCityIds);
try { try {
let areaIds = [];
if (typeof this.form.serviceCityIds === 'string') { if (typeof this.form.serviceCityIds === 'string') {
if (this.form.serviceCityIds.startsWith('[') && this.form.serviceCityIds.endsWith(']')) { if (this.form.serviceCityIds.startsWith('[') && this.form.serviceCityIds.endsWith(']')) {
this.selectedAreas = JSON.parse(this.form.serviceCityIds).map(Number).filter(n => !isNaN(n)); areaIds = JSON.parse(this.form.serviceCityIds);
} else { } else {
this.selectedAreas = this.form.serviceCityIds.split(',').map(Number).filter(n => !isNaN(n)); areaIds = this.form.serviceCityIds.split(',').map(id => id.trim()).filter(id => id);
} }
} else if (Array.isArray(this.form.serviceCityIds)) { } else if (Array.isArray(this.form.serviceCityIds)) {
this.selectedAreas = this.form.serviceCityIds.map(Number).filter(n => !isNaN(n)); areaIds = this.form.serviceCityIds;
} else { } else if (typeof this.form.serviceCityIds === 'number') {
this.selectedAreas = []; areaIds = [this.form.serviceCityIds];
} }
//
this.selectedAreas = areaIds.map(id => parseInt(id)).filter(id => !isNaN(id) && id > 0);
console.log('解析后的服务地区:', this.selectedAreas); console.log('解析后的服务地区:', this.selectedAreas);
//
this.$nextTick(() => {
this.updateAreaNameCache();
});
} catch (error) { } catch (error) {
console.error('解析服务地区数据失败:', error); console.error('解析服务地区数据失败:', error);
this.selectedAreas = []; this.selectedAreas = [];
} }
} else {
this.selectedAreas = [];
} }
}, },
// //
processSelectedSkills() { processSelectedSkills() {
console.log('开始处理技能数据:', this.form.skillIds, '类型:', typeof this.form.skillIds);
this.selectedSkills = []; //
if (this.form.skillIds) { if (this.form.skillIds) {
console.log('处理技能数据:', this.form.skillIds, '类型:', typeof this.form.skillIds);
try { try {
let skillIds = [];
if (typeof this.form.skillIds === 'string') { if (typeof this.form.skillIds === 'string') {
if (this.form.skillIds.startsWith('[') && this.form.skillIds.endsWith(']')) { if (this.form.skillIds.startsWith('[') && this.form.skillIds.endsWith(']')) {
this.selectedSkills = JSON.parse(this.form.skillIds).map(Number).filter(n => !isNaN(n)); skillIds = JSON.parse(this.form.skillIds);
} else { } else {
this.selectedSkills = this.form.skillIds.split(',').map(Number).filter(n => !isNaN(n)); skillIds = this.form.skillIds.split(',').map(id => id.trim()).filter(id => id);
} }
} else if (Array.isArray(this.form.skillIds)) { } else if (Array.isArray(this.form.skillIds)) {
this.selectedSkills = this.form.skillIds.map(Number).filter(n => !isNaN(n)); skillIds = this.form.skillIds;
} else { } else if (typeof this.form.skillIds === 'number') {
this.selectedSkills = []; skillIds = [this.form.skillIds];
} }
//
this.selectedSkills = skillIds.map(id => parseInt(id)).filter(id => !isNaN(id) && id > 0);
console.log('解析后的技能:', this.selectedSkills); console.log('解析后的技能:', this.selectedSkills);
//
this.$nextTick(() => {
this.updateSkillNameCache();
});
} catch (error) { } catch (error) {
console.error('解析技能数据失败:', error); console.error('解析技能数据失败:', error);
this.selectedSkills = []; this.selectedSkills = [];
} }
} else {
this.selectedSkills = [];
} }
}, },
// //
updateCityNameCache() { updateCityNameCache() {
console.log('更新城市名称缓存,已选择城市:', this.selectedCities, '城市列表:', this.cityList); console.log('更新城市名称缓存,已选择城市:', this.selectedCity, '城市列表:', this.cityList);
this.cityNameCache = {}; this.cityNameCache = {};
this.selectedCities.forEach(cityId => { if (this.selectedCity) {
const city = this.cityList.find(item => item.id === cityId); const city = this.cityList.find(item => item.id === this.selectedCity);
if (city) { if (city) {
this.cityNameCache[cityId] = city.title; this.cityNameCache[this.selectedCity] = city.title;
} else { } else {
console.warn('未找到城市ID:', cityId, '对应的名称'); console.warn('未找到城市ID:', this.selectedCity, '对应的名称');
} }
}); }
console.log('城市名称缓存:', this.cityNameCache); console.log('城市名称缓存:', this.cityNameCache);
}, },
@ -554,10 +591,7 @@ export default {
// //
removeCity(cityId) { removeCity(cityId) {
const index = this.selectedCities.indexOf(cityId); this.selectedCity = undefined;
if (index > -1) {
this.selectedCities.splice(index, 1);
}
}, },
// //
@ -578,10 +612,17 @@ export default {
// //
handleCityChange() { handleCityChange() {
console.log('城市选择变化:', this.selectedCities); console.log('城市选择变化:', this.selectedCity);
this.updateCityNameCache(); this.updateCityNameCache();
//
this.selectedAreas = [];
this.areaList = [];
// //
this.loadAreaList(); if (this.selectedCity) {
this.loadAreaList();
}
}, },
// //
@ -626,18 +667,27 @@ export default {
}) })
}, },
//
resetDialogData() {
this.isInitializing = false;
this.cityList = [];
this.selectedCity = undefined;
this.cityNameCache = {};
this.areaList = [];
this.selectedAreas = [];
this.areaNameCache = {};
this.skillList = [];
this.selectedSkills = [];
this.skillNameCache = {};
},
// //
resetForm() { resetForm() {
// id // id
const id = this.form.id const id = this.form.id
const createdAt = this.form.createdAt const createdAt = this.form.createdAt
this.$refs["form"].resetFields() this.$refs["form"].resetFields()
this.selectedCities = [] this.resetDialogData()
this.selectedAreas = []
this.selectedSkills = []
this.cityNameCache = {}
this.areaNameCache = {}
this.skillNameCache = {}
this.form = { this.form = {
id: id, id: id,
name: undefined, name: undefined,

View File

@ -160,6 +160,16 @@
v-hasPermi="['system:users:remove']" v-hasPermi="['system:users:remove']"
>删除</el-button> >删除</el-button>
</el-col> </el-col>
<el-col :span="1.5">
<el-button
type="warning"
plain
icon="el-icon-refresh"
size="mini"
@click="handleManualResumeWorkerStatus"
v-hasPermi="['system:users:edit']"
>恢复过期暂停</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar> <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row> </el-row>
@ -197,17 +207,31 @@
<span>{{ parseTime(scope.row.workerTime, '{y}-{m}-{d}') }}</span> <span>{{ parseTime(scope.row.workerTime, '{y}-{m}-{d}') }}</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="等级" align="center" prop="level"> <el-table-column label="等级" align="center" prop="level" width="100">
<template slot-scope="scope"> <template slot-scope="scope">
<el-select v-model="scope.row.level" @change="handleRowClick(scope.row)"> <div class="level-container">
<el-option <el-select
v-for="item in levelList" v-model="scope.row.level"
:key="item.id" @change="handleRowClick(scope.row)"
:label="item.title" size="small"
:value="item.id" class="level-select-custom"
></el-option> :class="'level-' + scope.row.level"
</el-select> placeholder="选择等级"
<!-- <el-link type="primary" @click="showWorkerLevelDialog(scope.row)">{{ scope.row.level }}</el-link>--> >
<el-option
v-for="item in levelList"
:key="item.id"
:label="item.title"
:value="item.id"
class="level-option-item"
>
<div class="level-option-wrapper">
<span class="level-text">{{ item.title }}</span>
<span class="level-number">{{ item.id }}</span>
</div>
</el-option>
</el-select>
</div>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="当前佣金" align="center" prop="commission" /> <el-table-column label="当前佣金" align="center" prop="commission" />
@ -230,6 +254,27 @@
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="累计提现" align="center" prop="propose" /> <el-table-column label="累计提现" align="center" prop="propose" />
<el-table-column label="接单状态" align="center" prop="isStop" width="100">
<template slot-scope="scope">
<el-tag v-if="scope.row.isStop === 1 && !scope.row.isExpired" type="danger">已暂停</el-tag>
<el-tag v-else-if="scope.row.isStop === 1 && scope.row.isExpired" type="warning">已过期</el-tag>
<el-tag v-else type="success">正常</el-tag>
</template>
</el-table-column>
<el-table-column label="暂停时长" align="center" prop="prohibitTimeNum" width="80">
<template slot-scope="scope">
<span v-if="scope.row.isStop === 1 && scope.row.prohibitTimeNum">{{ scope.row.prohibitTimeNum }}小时</span>
<span v-else>-</span>
</template>
</el-table-column>
<el-table-column label="暂停到期时间" align="center" prop="prohibitTime" width="150">
<template slot-scope="scope">
<span v-if="scope.row.isStop === 1 && scope.row.prohibitTime" :class="{'expired-time': scope.row.isExpired}">
{{ parseTime(scope.row.prohibitTime, '{y}-{m}-{d} {h}:{i}') }}
</span>
<span v-else>-</span>
</template>
</el-table-column>
<el-table-column label="服务区域" align="center" prop="serviceCityIds" width="150"> <el-table-column label="服务区域" align="center" prop="serviceCityIds" width="150">
<template slot-scope="scope"> <template slot-scope="scope">
<span v-if="scope.row.serviceCityIds">{{ formatServiceAreas(scope.row.serviceCityIds) }}</span> <span v-if="scope.row.serviceCityIds">{{ formatServiceAreas(scope.row.serviceCityIds) }}</span>
@ -251,6 +296,33 @@
@click="handleUpdate(scope.row)" @click="handleUpdate(scope.row)"
v-hasPermi="['system:users:edit']" v-hasPermi="['system:users:edit']"
>修改</el-button> >修改</el-button>
<el-button
v-if="scope.row.isStop !== 1"
size="mini"
type="text"
icon="el-icon-video-pause"
@click="handlePauseOrder(scope.row)"
v-hasPermi="['system:users:edit']"
style="color: #E6A23C;"
>暂停接单</el-button>
<el-button
v-else-if="scope.row.isExpired"
size="mini"
type="text"
icon="el-icon-refresh"
@click="handleResumeOrder(scope.row)"
v-hasPermi="['system:users:edit']"
style="color: #E6A23C;"
>恢复过期</el-button>
<el-button
v-else
size="mini"
type="text"
icon="el-icon-video-play"
@click="handleResumeOrder(scope.row)"
v-hasPermi="['system:users:edit']"
style="color: #67C23A;"
>恢复接单</el-button>
<el-button <el-button
size="mini" size="mini"
type="text" type="text"
@ -304,13 +376,53 @@
:user-name="workerLevelUserName" :user-name="workerLevelUserName"
@level-selected="handleWorkerLevelSelected" @level-selected="handleWorkerLevelSelected"
/> />
<!-- 暂停接单弹窗 -->
<el-dialog title="暂停接单" :visible.sync="pauseOrderDialogVisible" width="500px" append-to-body>
<el-form ref="pauseOrderForm" :model="pauseOrderForm" :rules="pauseOrderRules" label-width="100px">
<el-form-item label="师傅姓名">
<el-input v-model="pauseOrderForm.name" disabled />
</el-form-item>
<el-form-item label="暂停时长" prop="prohibitTimeNum">
<div class="time-input">
<el-button icon="el-icon-minus" @click="decreasePauseTime" size="small"></el-button>
<el-input-number
v-model="pauseOrderForm.prohibitTimeNum"
:min="1"
:max="168"
:controls="false"
placeholder="1"
style="width: 100px; text-align: center;" />
<el-button icon="el-icon-plus" @click="increasePauseTime" size="small"></el-button>
<span style="margin-left: 10px;">小时</span>
</div>
<div style="margin-top: 5px; color: #909399; font-size: 12px;">
暂停时长范围1-168小时7
</div>
</el-form-item>
<el-form-item label="暂停原因" prop="reason">
<el-input
v-model="pauseOrderForm.reason"
type="textarea"
:rows="3"
placeholder="请输入暂停接单原因(可选)"
maxlength="200"
show-word-limit
/>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="pauseOrderDialogVisible = false">取消</el-button>
<el-button type="primary" @click="confirmPauseOrder" :loading="pauseOrderLoading">确认暂停</el-button>
</div>
</el-dialog>
</div> </div>
</template> </template>
<script> <script>
import { listUsers, getUsers, delUsers, addUsers, updateUsers,getUserDataList,changetypeStatus } from "@/api/system/users" import { listUsers, getUsers, delUsers, addUsers, updateUsers,getUserDataList,changetypeStatus,pauseOrder } from "@/api/system/users"
import { listWorkerLevel } from '@/api/system/WorkerLevel' import { listWorkerLevel } from '@/api/system/WorkerLevel'
import { selectAreaList } from "@/api/system/WorkerApply" import { datalist, getDiyCity, manualResumeWorkerStatus } from "@/api/system/DiyCity"
import { getSiteSkillList } from "@/api/system/SiteSkill" import { getSiteSkillList } from "@/api/system/SiteSkill"
import UserEditDialog from './UserEditDialog.vue' import UserEditDialog from './UserEditDialog.vue'
import WorkerMoneyLogTable from '@/views/system/workerMoneyLog/WorkerMoneyLogTable.vue' import WorkerMoneyLogTable from '@/views/system/workerMoneyLog/WorkerMoneyLogTable.vue'
@ -388,7 +500,20 @@ export default {
// //
areaDataCache: {}, areaDataCache: {},
// //
skillDataCache: {} skillDataCache: {},
//
pauseOrderDialogVisible: false,
pauseOrderForm: {
name: '',
prohibitTimeNum: 1,
reason: ''
},
pauseOrderRules: {
prohibitTimeNum: [
{ required: true, message: '请选择暂停时长', trigger: 'change' }
]
},
pauseOrderLoading: false
} }
}, },
created() { created() {
@ -396,15 +521,23 @@ export default {
this.getlevelList(); this.getlevelList();
this.initAreaDataCache(); this.initAreaDataCache();
this.initSkillDataCache(); this.initSkillDataCache();
//
console.log('UsersWorker - 组件创建完成');
}, },
mounted() { mounted() {
// 便 // 便
this.$nextTick(() => { this.$nextTick(() => {
// //
console.log('UsersWorker - 组件挂载,立即初始化缓存');
this.initAreaDataCache();
this.initSkillDataCache();
//
setTimeout(() => { setTimeout(() => {
console.log('UsersWorker - 缓存初始化完成,重新获取列表数据'); console.log('UsersWorker - 缓存初始化时间结束,重新获取列表数据');
this.getList(); this.getList();
}, 1000); }, 2000);
}); });
}, },
methods: { methods: {
@ -415,6 +548,15 @@ export default {
this.usersList = response.rows this.usersList = response.rows
this.total = response.total this.total = response.total
this.loading = false this.loading = false
//
this.checkExpiredPauseStatus();
//
console.log('UsersWorker - 获取到的用户列表:', this.usersList);
if (this.usersList.length > 0) {
console.log('第一个用户的服务地区数据:', this.usersList[0].serviceCityIds, '类型:', typeof this.usersList[0].serviceCityIds);
}
}) })
}, },
// //
@ -482,10 +624,13 @@ export default {
this.multiple = !selection.length this.multiple = !selection.length
}, },
handleRowClick(row) { handleRowClick(row) {
updateUsers(row).then(() => { updateUsers(row).then(() => {
this.$message.success('修改成功') this.$message.success('等级修改成功')
}).catch(error => {
console.error('等级修改失败:', error);
this.$message.error('等级修改失败,请重试');
//
this.getList()
}) })
}, },
@ -624,15 +769,20 @@ export default {
try { try {
areaIds = JSON.parse(serviceCityIds); areaIds = JSON.parse(serviceCityIds);
} catch (e) { } catch (e) {
areaIds = serviceCityIds.split(',').map(id => id.trim()).filter(id => id); console.warn('JSON解析失败使用逗号分隔:', e);
areaIds = serviceCityIds.replace(/[\[\]]/g, '').split(',').map(id => id.trim()).filter(id => id);
} }
} else { } else {
areaIds = serviceCityIds.split(',').map(id => id.trim()).filter(id => id); areaIds = serviceCityIds.split(',').map(id => id.trim()).filter(id => id);
} }
} else if (Array.isArray(serviceCityIds)) { } else if (Array.isArray(serviceCityIds)) {
areaIds = serviceCityIds; areaIds = serviceCityIds;
} else {
areaIds = [serviceCityIds];
} }
//
areaIds = areaIds.map(id => parseInt(id)).filter(id => !isNaN(id) && id > 0);
console.log('解析后的地区ID数组:', areaIds); console.log('解析后的地区ID数组:', areaIds);
console.log('地区数据缓存:', this.areaDataCache); console.log('地区数据缓存:', this.areaDataCache);
@ -642,7 +792,13 @@ export default {
const areaNames = areaIds.map(id => { const areaNames = areaIds.map(id => {
const cached = this.areaDataCache[id]; const cached = this.areaDataCache[id];
console.log(`地区ID ${id} 对应的缓存数据:`, cached); console.log(`地区ID ${id} 对应的缓存数据:`, cached);
return cached ? cached.title : id; if (cached) {
return cached.title;
} else {
//
this.loadAreaById(id);
return `区域${id}`; //
}
}); });
const result = areaNames.join(', '); const result = areaNames.join(', ');
@ -699,83 +855,87 @@ export default {
// //
initAreaDataCache() { initAreaDataCache() {
console.log('开始初始化地区数据缓存'); console.log('开始初始化地区数据缓存');
// IDAPI
const commonAreas = [
// 西
{ id: 2, title: "新城区" },
{ id: 5, title: "碑林区" },
{ id: 7, title: "莲湖区" },
{ id: 10, title: "灞桥区" },
{ id: 11, title: "未央区" },
{ id: 12, title: "雁塔区" },
{ id: 13, title: "阎良区" },
{ id: 14, title: "临潼区" },
{ id: 15, title: "长安区" },
{ id: 16, title: "高陵区" },
{ id: 17, title: "鄠邑区" },
//
{ id: 53, title: "瑶海区" },
{ id: 54, title: "庐阳区" },
{ id: 55, title: "蜀山区" },
{ id: 56, title: "包河区" },
{ id: 57, title: "经开区" },
{ id: 58, title: "高新区" }
];
commonAreas.forEach(area => {
this.areaDataCache[area.id] = area;
console.log(`添加常见地区到缓存: ${area.id} -> ${area.title}`);
});
// //
selectAreaList("100000").then(response => { const queryParams = {
parentId: 0 //
}
datalist(queryParams).then(response => {
console.log('获取省份数据成功:', response); console.log('获取省份数据成功:', response);
if (response.data) { if (response.code === 200 && response.data) {
//
response.data.forEach(province => { response.data.forEach(province => {
this.areaDataCache[province.id] = province; this.areaDataCache[province.id] = province;
console.log(`添加省份到缓存: ${province.id} -> ${province.title}`); console.log(`添加省份到缓存: ${province.id} -> ${province.title}`);
// });
selectAreaList(province.id).then(cityResponse => {
//
const loadPromises = response.data.map(province => {
const cityParams = {
parentId: province.id
}
return datalist(cityParams).then(cityResponse => {
console.log(`获取城市数据成功 (${province.id}):`, cityResponse); console.log(`获取城市数据成功 (${province.id}):`, cityResponse);
if (cityResponse.data) { if (cityResponse.code === 200 && cityResponse.data) {
cityResponse.data.forEach(city => { cityResponse.data.forEach(city => {
this.areaDataCache[city.id] = city; this.areaDataCache[city.id] = city;
console.log(`添加城市到缓存: ${city.id} -> ${city.title}`); console.log(`添加城市到缓存: ${city.id} -> ${city.title}`);
}); });
} }
return cityResponse;
}).catch((error) => { }).catch((error) => {
console.error(`获取城市数据失败 (${province.id}):`, error); console.error(`获取城市数据失败 (${province.id}):`, error);
// 使 return Promise.resolve();
if (province.id === "610100") {
const defaultCities = [
{ id: "610102", title: "新城区" },
{ id: "610103", title: "碑林区" },
{ id: "610104", title: "莲湖区" },
{ id: "610111", title: "灞桥区" },
{ id: "610112", title: "未央区" },
{ id: "610113", title: "雁塔区" },
{ id: "610114", title: "阎良区" },
{ id: "610115", title: "临潼区" },
{ id: "610116", title: "长安区" },
{ id: "610117", title: "高陵区" }
];
defaultCities.forEach(city => {
this.areaDataCache[city.id] = city;
console.log(`添加默认城市到缓存: ${city.id} -> ${city.title}`);
});
}
}); });
}); });
//
Promise.all(loadPromises).then(() => {
console.log('所有地区数据加载完成,最终缓存:', this.areaDataCache);
//
this.$forceUpdate();
});
} }
}).catch((error) => { }).catch((error) => {
console.error('获取省份数据失败:', error); console.error('获取省份数据失败:', error);
// 使 // 使
const defaultProvinces = [ const defaultProvinces = [
{ id: "610100", title: "西安市" }, { id: 1, title: "陕西省" },
{ id: "610200", title: "铜川市" }, { id: 27, title: "上海市" },
{ id: "610300", title: "宝鸡市" } { id: 44, title: "湖南省" },
{ id: 52, title: "安徽省" }
]; ];
defaultProvinces.forEach(province => { defaultProvinces.forEach(province => {
this.areaDataCache[province.id] = province; this.areaDataCache[province.id] = province;
console.log(`添加默认省份到缓存: ${province.id} -> ${province.title}`); console.log(`添加默认省份到缓存: ${province.id} -> ${province.title}`);
}); });
// ID
const commonAreas = [
{ id: "1", title: "北京市" },
{ id: "2", title: "上海市" },
{ id: "3", title: "广州市" },
{ id: "4", title: "深圳市" },
{ id: "5", title: "杭州市" },
{ id: "6", title: "南京市" },
{ id: "7", title: "武汉市" },
{ id: "8", title: "成都市" },
{ id: "9", title: "西安市" },
{ id: "10", title: "重庆市" },
{ id: "11", title: "天津市" },
{ id: "12", title: "苏州市" },
{ id: "13", title: "无锡市" },
{ id: "14", title: "宁波市" },
{ id: "15", title: "青岛市" },
{ id: "16", title: "大连市" },
{ id: "17", title: "厦门市" }
];
commonAreas.forEach(area => {
this.areaDataCache[area.id] = area;
console.log(`添加常见地区到缓存: ${area.id} -> ${area.title}`);
});
}); });
}, },
// //
@ -803,7 +963,406 @@ export default {
console.log(`添加默认技能到缓存: ${skill.id} -> ${skill.title}`); console.log(`添加默认技能到缓存: ${skill.id} -> ${skill.title}`);
}); });
}); });
},
// ID
loadAreaById(id) {
if (this.areaDataCache[id]) {
return; //
}
// 使getDiyCity APIID
getDiyCity(id).then(response => {
if (response.code === 200 && response.data) {
this.areaDataCache[id] = response.data;
console.log(`从API加载地区到缓存: ${id} -> ${response.data.title}`);
//
this.$forceUpdate();
} else {
console.warn(`API加载地区失败ID: ${id}, 响应:`, response);
}
}).catch((error) => {
console.error(`API加载地区失败ID: ${id}:`, error);
});
},
//
handlePauseOrder(row) {
console.log('点击暂停接单,师傅信息:', row);
if (!row || !row.id) {
this.$message.error('师傅信息不完整,无法暂停接单');
return;
}
//
this.pauseOrderForm = {
id: row.id,
name: row.name,
prohibitTimeNum: 1,
reason: ''
};
//
this.$nextTick(() => {
if (this.$refs.pauseOrderForm) {
this.$refs.pauseOrderForm.clearValidate();
}
});
this.pauseOrderDialogVisible = true;
},
//
decreasePauseTime() {
if (this.pauseOrderForm.prohibitTimeNum > 1) {
this.pauseOrderForm.prohibitTimeNum--;
}
},
//
increasePauseTime() {
if (this.pauseOrderForm.prohibitTimeNum < 168) {
this.pauseOrderForm.prohibitTimeNum++;
}
},
//
confirmPauseOrder() {
this.$refs.pauseOrderForm.validate(valid => {
if (valid) {
this.pauseOrderLoading = true;
const pauseData = {
id: this.pauseOrderForm.id,
prohibitTimeNum: this.pauseOrderForm.prohibitTimeNum
};
pauseOrder(pauseData).then(response => {
this.$modal.msgSuccess(`已成功暂停师傅"${this.pauseOrderForm.name}"接单${this.pauseOrderForm.prohibitTimeNum}小时`);
this.pauseOrderDialogVisible = false;
this.getList(); //
}).catch(error => {
console.error('暂停接单失败:', error);
this.$modal.msgError('暂停接单失败,请重试');
}).finally(() => {
this.pauseOrderLoading = false;
});
}
});
},
//
handleResumeOrder(row) {
console.log('点击恢复接单,师傅信息:', row);
if (!row || !row.id) {
this.$message.error('师傅信息不完整,无法恢复接单');
return;
}
this.$modal.confirm(`确认要恢复师傅"${row.name}"的接单状态吗?`).then(() => {
this.resumeOrder(row.id, row.name);
}).catch(() => {
console.log('用户取消恢复接单操作');
});
},
//
resumeOrder(userId, userName) {
const resumeData = {
id: userId,
prohibitTimeNum: 0,
isStop: 0
};
updateUsers(resumeData).then(response => {
this.$modal.msgSuccess(`已成功恢复师傅"${userName}"的接单状态`);
this.getList(); //
}).catch(error => {
console.error('恢复接单失败:', error);
this.$modal.msgError('恢复接单失败,请重试');
});
},
//
checkExpiredPauseStatus() {
const now = new Date();
let hasExpired = false;
this.usersList.forEach(user => {
if (user.isStop === 1 && user.prohibitTime) {
const prohibitTime = new Date(user.prohibitTime);
if (prohibitTime < now) {
//
user.isExpired = true;
hasExpired = true;
console.log(`师傅"${user.name}"的暂停时间已过期`);
}
}
});
//
if (hasExpired) {
this.$message.info('检测到部分师傅的暂停时间已过期,请手动恢复接单状态。');
}
},
//
updateUserStatus(id, status) {
changetypeStatus(id, status).then(() => {
this.$message.success(`用户状态更新成功`);
this.getList(); //
}).catch(error => {
console.error('更新用户状态失败:', error);
this.$message.error('更新用户状态失败,请重试');
});
},
//
handleManualResumeWorkerStatus() {
this.$modal.confirm('确认要执行师傅暂停状态自动恢复任务吗?此操作将检查所有暂停的师傅,并自动恢复已过期的师傅接单状态。').then(() => {
this.executeManualResumeWorkerStatus();
}).catch(() => {
console.log('用户取消手动恢复过期暂停操作');
});
},
//
executeManualResumeWorkerStatus() {
//
const loading = this.$loading({
lock: true,
text: '正在执行师傅暂停状态恢复任务...',
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)'
});
manualResumeWorkerStatus().then(response => {
loading.close();
if (response.code === 200) {
this.$message.success(response.msg || '师傅暂停状态自动恢复任务执行成功');
//
this.getList();
} else {
this.$message.error(response.msg || '师傅暂停状态自动恢复任务执行失败');
}
}).catch(error => {
loading.close();
console.error('执行师傅暂停状态自动恢复失败:', error);
this.$message.error('执行师傅暂停状态自动恢复失败,请重试');
});
},
//
resumeAllExpiredWorkers() {
const expiredUsers = this.usersList.filter(user => user.isStop === 1 && user.isExpired);
if (expiredUsers.length === 0) {
this.$message.info('没有过期暂停的师傅。');
return;
}
const userIds = expiredUsers.map(user => user.id);
const userNames = expiredUsers.map(user => user.name);
this.$modal.confirm(`确认要恢复以下${userIds.length}个师傅的接单状态吗?\n${userNames.join('、')}`).then(() => {
const resumePromises = userIds.map(id => {
const resumeData = {
id: id,
prohibitTimeNum: 0,
isStop: 0
};
return updateUsers(resumeData);
});
Promise.all(resumePromises).then(() => {
this.$message.success(`已成功恢复${userIds.length}个师傅的接单状态。`);
this.getList(); //
}).catch(error => {
console.error('批量恢复过期暂停失败:', error);
this.$message.error('批量恢复过期暂停失败,请重试');
});
}).catch(() => {
console.log('用户取消批量恢复过期暂停操作');
});
} }
} }
} }
</script> </script>
<style scoped>
.time-input {
display: flex;
align-items: center;
}
.selected-tags {
margin-top: 8px;
max-width: 100%;
overflow: hidden;
}
.selected-tags .el-tag {
margin: 2px;
max-width: 200px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.time-input .el-button {
border-radius: 4px;
margin: 0 5px;
}
.time-input .el-input-number {
margin: 0 5px;
}
.expired-time {
color: #E6A23C;
font-weight: bold;
text-decoration: line-through;
}
/* 等级选择器样式 */
.level-container {
display: flex;
justify-content: center;
align-items: center;
}
.level-select-custom {
width: 80px !important;
font-weight: 600;
border-radius: 20px;
transition: all 0.3s ease;
border-width: 2px;
}
.level-select-custom:hover {
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
.level-select-custom:focus-within {
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
}
/* 不同等级的颜色样式 */
.level-1 {
background-color: #f8f9fa;
border-color: #6c757d;
color: #495057;
}
.level-1:hover {
border-color: #5a6268;
background-color: #f1f3f4;
}
.level-2 {
background-color: #e8f5e8;
border-color: #28a745;
color: #155724;
}
.level-2:hover {
border-color: #1e7e34;
background-color: #d1ecf1;
}
.level-3 {
background-color: #e3f2fd;
border-color: #2196f3;
color: #0d47a1;
}
.level-3:hover {
border-color: #1976d2;
background-color: #bbdefb;
}
.level-4 {
background-color: #fff3e0;
border-color: #ff9800;
color: #e65100;
}
.level-4:hover {
border-color: #f57c00;
background-color: #ffe0b2;
}
.level-5 {
background-color: #ffebee;
border-color: #f44336;
color: #b71c1c;
}
.level-5:hover {
border-color: #d32f2f;
background-color: #ffcdd2;
}
/* 下拉选项样式 */
.level-option-item {
padding: 8px 16px !important;
border-radius: 6px;
margin: 2px 4px;
transition: all 0.2s ease;
}
.level-option-item:hover {
background-color: #f8f9fa !important;
transform: translateX(4px);
}
.level-option-wrapper {
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
}
.level-text {
font-weight: 500;
color: #333;
}
.level-number {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border-radius: 50%;
width: 20px;
height: 20px;
display: flex;
align-items: center;
justify-content: center;
font-size: 12px;
font-weight: bold;
margin-left: 8px;
}
/* 选择器下拉箭头样式优化 */
.level-select-custom .el-input__suffix {
right: 8px;
}
.level-select-custom .el-input__suffix .el-input__suffix-inner .el-select__caret {
color: inherit;
font-size: 14px;
transition: transform 0.3s ease;
}
.level-select-custom.is-focus .el-select__caret {
transform: rotateZ(180deg);
}
/* 响应式设计 */
@media (max-width: 768px) {
.level-select-custom {
width: 70px !important;
font-size: 12px;
}
.level-number {
width: 16px;
height: 16px;
font-size: 10px;
}
}
</style>