2025008071805

This commit is contained in:
张潘 2025-08-16 17:56:50 +08:00
parent 26b18184e3
commit 656a75b226
8 changed files with 1081 additions and 16 deletions

View File

@ -5241,7 +5241,7 @@ public class AppletController extends BaseController {
// orderService.updateOrder(order);
}
//给新师傅进行电话通知
YunXinPhoneUtilAPI.httpsAxbTransfer(newWorker.getPhone());
YunXinPhoneUtilAPI.httpsAxbTransfer(newWorker.getPhone(), order.getId());
}
return AjaxResult.success("转单成功");
} catch (Exception e) {

View File

@ -83,6 +83,9 @@ public class PayNotifyController extends BaseController {
@Autowired
private ISiteConfigService siteConfigService;
@Autowired
private IOrderCallService orderCallService;
@Autowired
private IIntegralLogService integralLogService;
@ -2814,7 +2817,39 @@ public class PayNotifyController extends BaseController {
return buildFailResponse("次卡退款回调处理异常");
}
}
@PostMapping("/api/YunXin/notify")
public String apiYunXinnotify(HttpServletRequest request) throws IOException {
// 1. 读取退款回调数据
StringBuilder xmlData = new StringBuilder();
BufferedReader reader = new BufferedReader(new InputStreamReader(request.getInputStream(), StandardCharsets.UTF_8));
String line;
while ((line = reader.readLine()) != null) {
xmlData.append(line);
}
reader.close();
JSONObject json = JSONObject.parseObject(xmlData.toString());
String oid=json.getString("customerData");
String answeredTime=json.getString("answeredTime");
String endTime=json.getString("endTime");
String duration=json.getString("duration");
String callState=json.getString("callState");
if (StringUtils.isNotBlank(oid)){
Order order=orderService.selectOrderById(Long.valueOf(oid));
if (order!=null){
OrderCall orderCall=new OrderCall();
orderCall.setOid(order.getId());
orderCall.setType(2);
orderCall.setStartCallTime(DateUtils.parseDate(answeredTime));
orderCall.setEndTime(DateUtils.parseDate(endTime));
orderCall.setCallerstate(Integer.valueOf(callState));
orderCall.setDuration(Long.valueOf(duration));
orderCallService.insertOrderCall(orderCall);
}
}
logger.info("------------------------------------------------------------云信回调原始数据:{}", xmlData.toString());
return buildFailResponse("非常成功");
}
/**
* 处理次卡退款成功后的业务逻辑
*

View File

@ -5536,6 +5536,7 @@ public class AppletControllerUtil {
order.setMainOrderId(mainorderId);
order.setCreateType(1); // 用户自主下单
order.setOrderId(orderId);
order.setCreatePhone(user.getPhone());
order.setUid(user.getId());
order.setUname(user.getName());
order.setProductId(serviceGoods.getId());
@ -5764,6 +5765,7 @@ public class AppletControllerUtil {
order.setFileData(attachments); // 设置附件数据
order.setType(1);
order.setTotalPrice(itemPrice);
order.setCreatePhone(user.getPhone());
int insertResult = orderService.insertOrder(order);
if (insertResult <= 0) {
result.put("success", false);
@ -5936,6 +5938,7 @@ public class AppletControllerUtil {
order.setDeduction(new BigDecimal(0));
order.setFileData(attachments); // 设置附件数据
order.setTotalPrice(totalAmount);
order.setCreatePhone(user.getPhone());
int insertResult = orderService.insertOrder(order);
if (insertResult <= 0) {
result.put("success", false);
@ -6060,7 +6063,7 @@ public class AppletControllerUtil {
JSONObject jsonObject = new JSONObject();
jsonObject.put("name", "用户创建报价订单,待报价");
orderLog.setContent(jsonObject.toJSONString());
order.setCreatePhone(user.getPhone());
orderLogService.insertOrderLog(orderLog);
// // 添加订单日志

View File

@ -105,7 +105,7 @@ public class CartOrderUtil {
order.setDeduction(BigDecimal.ZERO);
order.setBigtype(serviceGoods.getServicetype());
order.setReceiveType(Long.valueOf(dispatchtype)); // 自由抢单
order.setCreatePhone(user.getPhone());
int insertResult = orderService.insertOrder(order);
if (insertResult <= 0) {
result.put("success", false);

View File

@ -33,6 +33,7 @@ public class OrderUtil {
private static IUsersPayBeforService usersPayBeforService = SpringUtils.getBean(IUsersPayBeforService.class);
private static IQuoteMaterialService quoteMaterialService = SpringUtils.getBean(IQuoteMaterialService.class);
private static IServiceGoodsService serviceGoodsService = SpringUtils.getBean(IServiceGoodsService.class);
private static IOrderCallService orderCallService = SpringUtils.getBean(IOrderCallService.class);
private static OrderLogHandler orderLogHandler = SpringUtils.getBean(OrderLogHandler.class);
@ -654,6 +655,7 @@ public class OrderUtil {
for (GoodsOrder g: gorders){
g.setStatus(2L);
g.setPayTime(DateUtils.getNowDate());
g.setTransactionId(payBefor.getPaycode());
goodsOrderService.updateGoodsOrder(g);
// BenefitPointsUtil.processBenefitPoints(g.getId(),g.getTotalPrice(),"2");
}
@ -679,6 +681,7 @@ public class OrderUtil {
for (GoodsOrder goodsOrder : orderslist){
System.out.println("更新商品订单 - ID: " + goodsOrder.getId() + ", 原状态: " + goodsOrder.getStatus());
goodsOrder.setStatus(2L);
goodsOrder.setPayTime(DateUtils.getNowDate());
goodsOrder.setTransactionId(payBefor.getPaycode());
int updateResult = goodsOrderService.updateGoodsOrder(goodsOrder);
@ -701,7 +704,7 @@ public class OrderUtil {
if (order != null) {
order.setStatus(2L);
order.setPayTime(DateUtils.getNowDate());
order.setTransactionId(payBefor.getPaycode());
order.setTransactionId(order.getTransactionId()+" "+ payBefor.getPaycode());
//BenefitPointsUtil.processBenefitPoints(order.getId(),order.getTotalPrice(),"2");
//微信发送消息
WXsendMsgUtil.sendUserForMoneySuccess(user.getOpenid(),payBefor.getOrderid(),payBefor.getAllmoney().toString(),order.getAddress(),"商品支付","商品支付成功");
@ -721,7 +724,9 @@ public class OrderUtil {
int updateResult = orderLogService.updateOrderLog(orderLog);
Order order = orderService.selectOrderByOrderId(payBefor.getLastorderid());
if (order != null){
order.setPayTime(DateUtils.getNowDate());
order.setPayPrice(order.getPayPrice().add(payBefor.getAllmoney()));
order.setTransactionId(order.getTransactionId()+" "+ payBefor.getPaycode());
orderService.updateOrder(order);
}
@ -755,6 +760,8 @@ public class OrderUtil {
if (updateResult > 0){
Order order = orderService.selectOrderByOrderId(payBefor.getLastorderid());
if (order != null){
order.setPayTime(DateUtils.getNowDate());
order.setTransactionId(order.getTransactionId()+" "+ payBefor.getPaycode());
order.setPayPrice(order.getPayPrice().add(payBefor.getAllmoney()));
orderService.updateOrder(order);
}
@ -773,7 +780,8 @@ public class OrderUtil {
if (order != null) {
order.setPayTime(DateUtils.getNowDate());
order.setTransactionId(order.getTransactionId()+" "+ payBefor.getPaycode());
order.setPayPrice(order.getPayPrice().add(payBefor.getAllmoney()));
orderService.updateOrder(order);
@ -812,6 +820,8 @@ public class OrderUtil {
if (updateResult > 0){
Order order = orderService.selectOrderByOrderId(payBefor.getLastorderid());
if (order != null){
order.setPayTime(DateUtils.getNowDate());
order.setTransactionId(order.getTransactionId()+" "+ payBefor.getPaycode());
order.setPayPrice(order.getPayPrice().add(payBefor.getAllmoney()));
orderService.updateOrder(order);
WXsendMsgUtil.sendUserForMoneySuccess(user.getOpenid(),payBefor.getOrderid(),payBefor.getAllmoney().toString(),order.getAddress(),"差价支付","差价支付成功");
@ -852,7 +862,8 @@ public class OrderUtil {
System.out.println("查询到的师傅信息: " + (users != null ? users.toString() : "null"));
if (users != null){
order.setPayTime(DateUtils.getNowDate());
order.setTransactionId(order.getTransactionId()+" "+ payBefor.getPaycode());
order.setStatus(2L);
order.setJsonStatus(2);
order.setPayPrice(order.getPayPrice().add(userDemandQuotation.getMoney()));
@ -934,6 +945,8 @@ public class OrderUtil {
System.out.println("订单日志插入结果: " + logInsertResult);
System.out.println("更新订单状态为待派单");
order.setPayTime(DateUtils.getNowDate());
order.setTransactionId(order.getTransactionId()+" "+ payBefor.getPaycode());
order.setPayPrice(order.getPayPrice().add(payBefor.getAllmoney()));
order.setStatus(1L); // 1=待预约
int orderUpdateResult = orderService.updateOrder(order);
@ -980,7 +993,8 @@ public class OrderUtil {
Order order = new Order();
order.setOdertype(1); // 拼团
order.setStatus(9L); // 9=待成团
order.setPayTime(DateUtils.getNowDate());
order.setTransactionId(order.getTransactionId()+" "+ payBefor.getPaycode());
order.setOrderId(ptorderid);
order.setUid(payBefor.getUid());
order.setNum(payBefor.getNum()); // 默认1可根据业务调整
@ -1015,6 +1029,7 @@ public class OrderUtil {
//20250809处理过的一个故障
order.setPayPrice(payBefor.getAllmoney());
order.setReceiveType(Long.valueOf(dispatchtype)); // 自由抢单
order.setCreatePhone(user.getPhone());
// 预约时间
System.out.println("预约时间: " + payBefor.getMaketime());
if (payBefor.getMaketime() != null && !payBefor.getMaketime().isEmpty()) {
@ -1148,6 +1163,8 @@ public class OrderUtil {
if (logInsertResult>0){
DispatchUtil.dispatchOrder(order.getId());
}
order.setPayTime(DateUtils.getNowDate());
order.setTransactionId(order.getTransactionId()+" "+ payBefor.getPaycode());
order.setPayPrice(order.getPayPrice().add(payBefor.getAllmoney()));
order.setStatus(1L); // 1=待预约
int orderUpdateResult = orderService.updateOrder(order);
@ -1180,6 +1197,8 @@ public class OrderUtil {
System.out.println("订单日志插入结果: " + logInsertResult);
System.out.println("更新订单状态为待派单");
order.setPayTime(DateUtils.getNowDate());
order.setTransactionId(order.getTransactionId()+" "+ payBefor.getPaycode());
order.setPayPrice(order.getPayPrice().add(payBefor.getAllmoney()));
order.setStatus(1L); // 1=待预约
int orderUpdateResult = orderService.updateOrder(order);
@ -2034,6 +2053,41 @@ public class OrderUtil {
}
}
/**
* 定时任务拨打电话
*/
public static String TheCallWorder() throws Exception {
//第一步先去查询已经派单但是师傅还没接单的订单
System.out.println("开始执行定时查询任务---------------------------"+new Date());
IOrderService orderService = SpringUtils.getBean(IOrderService.class);
Order order = new Order();
order.setIsAccept(0);
List<Order> orderList = orderService.selectOrderList(order);
if (orderList != null && !orderList.isEmpty()){
for (Order order1 : orderList) {
if (order1.getIsAccept() == 0&&order1.getWorkerId()!=null){
OrderCall orderCall = new OrderCall();
orderCall.setOid(order1.getId());
List<OrderCall> orderCallList = orderCallService.selectOrderCallList(orderCall);
if (orderCallList != null && !orderCallList.isEmpty()){
if (orderCallList.size()<3){
if (orderCallList.getFirst().getCallerstate()!= 0L){
Users user = usersService.selectUsersById(order1.getWorkerId());
if (user != null){
String phone = user.getPhone();
YunXinPhoneUtilAPI.httpsAxbTransfer(phone,order1.getId());
}
}
}
}
}
}
}
return "";
}
/**
* 查找用户首次下单重载方法支持用户对象
* 根据用户对象查找该用户首次下单的订单ID

View File

@ -31,12 +31,12 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<include refid="selectOrderCallVo"/>
<where>
<if test="oid != null "> and oid = #{oid}</if>
<if test="type != null "> and type = #{type}</if>
<if test="startCallTime != null "> and start_call_time = #{startCallTime}</if>
<if test="callerstate != null "> and callerstate = #{callerstate}</if>
<if test="endTime != null "> and end_time = #{endTime}</if>
<if test="duration != null "> and duration = #{duration}</if>
<if test="recurl != null and recurl != ''"> and recurl = #{recurl}</if>
<!-- <if test="type != null "> and type = #{type}</if>-->
<!-- <if test="startCallTime != null "> and start_call_time = #{startCallTime}</if>-->
<!-- <if test="callerstate != null "> and callerstate = #{callerstate}</if>-->
<!-- <if test="endTime != null "> and end_time = #{endTime}</if>-->
<!-- <if test="duration != null "> and duration = #{duration}</if>-->
<!-- <if test="recurl != null and recurl != ''"> and recurl = #{recurl}</if>-->
</where>
order by id desc
</select>

View File

@ -0,0 +1,852 @@
<template>
<el-dialog
:title="`订单详情 - ${orderInfo.orderId || '未知订单'}`"
:visible.sync="visible"
width="90%"
:close-on-click-modal="false"
:close-on-press-escape="false"
class="order-detail-dialog"
top="5vh"
append-to-body
>
<div class="order-detail-container">
<!-- 左侧订单基本信息 -->
<div class="order-info-section">
<div class="section-header">
<i class="el-icon-document"></i>
<span>订单基本信息</span>
</div>
<div class="info-grid">
<div class="info-item">
<label>订单号</label>
<span class="value">{{ orderInfo.orderId || '未设置' }}</span>
</div>
<div class="info-item">
<label>订单状态</label>
<el-tag :type="getStatusTagType(orderInfo.status)" size="small">
{{ getStatusLabel(orderInfo.status) }}
</el-tag>
</div>
<div class="info-item">
<label>创建时间</label>
<span class="value">{{ formatTime(orderInfo.createdAt) }}</span>
</div>
<div class="info-item">
<label>支付时间</label>
<span class="value">{{ formatTime(orderInfo.payTime) }}</span>
</div>
<div class="info-item">
<label>用户姓名</label>
<span class="value">{{ orderInfo.userName || '未设置' }}</span>
</div>
<div class="info-item">
<label>联系电话</label>
<span class="value">{{ orderInfo.userPhone || '未设置' }}</span>
</div>
<div class="info-item">
<label>服务地址</label>
<span class="value address">{{ orderInfo.appointmentAddress || '未设置' }}</span>
</div>
<div class="info-item">
<label>服务项目</label>
<span class="value">{{ orderInfo.productName || '未设置' }}</span>
</div>
<div class="info-item">
<label>预约数量</label>
<span class="value">{{ orderInfo.num || '0' }}</span>
</div>
<div class="info-item">
<label>预约时间</label>
<span class="value">{{ orderInfo.appointmentTime || '未设置' }}</span>
</div>
<div class="info-item">
<label>订单总价</label>
<span class="value price">¥{{ (orderInfo.totalPrice || 0).toFixed(2) }}</span>
</div>
<div class="info-item">
<label>支付金额</label>
<span class="value price">¥{{ (orderInfo.payPrice || 0).toFixed(2) }}</span>
</div>
<div class="info-item">
<label>抵扣金额</label>
<span class="value discount">¥{{ (orderInfo.deduction || 0).toFixed(2) }}</span>
</div>
<div class="info-item">
<label>师傅姓名</label>
<span class="value">{{ getWorkerName(orderInfo.workerId) }}</span>
</div>
<div class="info-item">
<label>服务进度</label>
<el-tag :type="getProgressTagType(orderInfo.jsonStatus)" size="small">
{{ getProgressLabel(orderInfo.jsonStatus) }}
</el-tag>
</div>
</div>
<!-- 备注信息 -->
<div v-if="orderInfo.mark" class="remark-section">
<div class="section-header">
<i class="el-icon-edit-outline"></i>
<span>备注说明</span>
</div>
<div class="remark-content">{{ orderInfo.mark }}</div>
</div>
<!-- 附件信息 -->
<div v-if="getFileList().length > 0" class="attachment-section">
<div class="section-header">
<i class="el-icon-paperclip"></i>
<span>订单附件</span>
</div>
<div class="attachment-grid">
<div
v-for="(file, index) in getFileList()"
:key="index"
class="attachment-item"
@click="previewFile(file)"
>
<el-image
v-if="isImage(file)"
:src="file"
fit="cover"
class="attachment-image"
>
<div slot="error" class="image-slot">
<i class="el-icon-picture-outline"></i>
</div>
</el-image>
<div v-else class="attachment-icon">
<i class="el-icon-document"></i>
</div>
</div>
</div>
</div>
</div>
<!-- 右侧日志流水 -->
<div class="log-section">
<div class="section-header">
<i class="el-icon-time"></i>
<span>订单日志</span>
<div class="header-actions">
<el-button
type="text"
icon="el-icon-refresh"
@click="refreshLogs"
size="small"
>
刷新
</el-button>
<el-button
type="text"
icon="el-icon-download"
@click="exportLogs"
size="small"
>
导出
</el-button>
</div>
</div>
<div v-if="orderInfo.logs && orderInfo.logs.length > 0" class="log-timeline">
<div
v-for="(log, index) in orderInfo.logs"
:key="log.id || index"
class="log-item"
>
<div class="log-node" :class="getLogNodeClass(log.type)">
<i :class="getLogIcon(log.type)"></i>
</div>
<div class="log-content">
<div class="log-header">
<span class="log-title">{{ log.title || '未知操作' }}</span>
<span class="log-time">{{ formatTime(log.createdAt) }}</span>
</div>
<div class="log-body">
<div class="log-description">{{ log.content || '无描述信息' }}</div>
<!-- 显示图片 -->
<div v-if="log.images && log.images.length > 0" class="log-images">
<el-image
v-for="(img, imgIndex) in log.images"
:key="imgIndex"
:src="img"
fit="cover"
class="log-image"
@click="previewImage(img, log.images)"
>
<div slot="error" class="image-slot">
<i class="el-icon-picture-outline"></i>
</div>
</el-image>
</div>
<!-- 显示操作按钮 -->
<div class="log-actions" v-if="index === orderInfo.logs.length - 1">
<el-button
v-if="log.type === 'dispatch'"
type="primary"
size="small"
@click="handleDispatch"
:loading="actionLoading"
>
<i class="el-icon-s-promotion"></i>
派单
</el-button>
<el-button
v-if="log.type === 'accept'"
type="success"
size="small"
@click="handleAccept"
:loading="actionLoading"
>
<i class="el-icon-check"></i>
接单
</el-button>
<el-button
v-if="log.type === 'start'"
type="warning"
size="small"
@click="handleStart"
:loading="actionLoading"
>
<i class="el-icon-video-play"></i>
开始服务
</el-button>
<el-button
v-if="log.type === 'complete'"
type="success"
size="small"
@click="handleComplete"
:loading="actionLoading"
>
<i class="el-icon-circle-check"></i>
完成服务
</el-button>
</div>
</div>
</div>
<!-- 连接线 -->
<div v-if="index < orderInfo.logs.length - 1" class="log-connector"></div>
</div>
</div>
<div v-else class="empty-logs">
<el-empty description="暂无日志记录" :image-size="80">
<el-button type="primary" @click="refreshLogs">刷新数据</el-button>
</el-empty>
</div>
</div>
</div>
<div slot="footer" class="dialog-footer">
<el-button @click="closeDialog" type="primary" size="medium"> </el-button>
</div>
<!-- 图片预览 -->
<el-dialog :visible.sync="imagePreviewVisible" append-to-body>
<img width="100%" :src="previewImageUrl" alt="">
</el-dialog>
</el-dialog>
</template>
<script>
export default {
name: 'OrderDetailDialog',
props: {
visible: {
type: Boolean,
default: false
},
orderInfo: {
type: Object,
default: () => ({})
}
},
data() {
return {
actionLoading: false,
imagePreviewVisible: false,
previewImageUrl: ''
}
},
methods: {
//
closeDialog() {
this.$emit('update:visible', false)
},
//
formatTime(time) {
if (!time) return '未设置'
if (typeof time === 'number') {
// Unix
const date = new Date(time * 1000)
return date.toLocaleString()
}
const date = new Date(time)
return date.toLocaleString()
},
//
getStatusTagType(status) {
const statusMap = {
1: 'warning', //
2: 'primary', //
3: 'success', //
4: 'success', //
5: 'danger', //
6: 'info', // 退
7: 'info' //
}
return statusMap[status] || 'info'
},
//
getStatusLabel(status) {
const statusMap = {
1: '待支付',
2: '待服务',
3: '服务中',
4: '已完成',
5: '已取消',
6: '已退款',
7: '已结束'
}
return statusMap[status] || `状态${status}`
},
//
getProgressTagType(jsonStatus) {
const progressMap = {
1: 'info', //
2: 'primary', //
3: 'success', // '
8: 'warning', //
9: 'success' //
}
return progressMap[jsonStatus] || 'info'
},
//
getProgressLabel(jsonStatus) {
const progressMap = {
1: '未开始',
2: '进行中',
3: '已完成',
8: '已暂停',
9: '已恢复'
}
return progressMap[jsonStatus] || `进度${jsonStatus}`
},
//
getWorkerName(workerId) {
if (!workerId) return '未分配'
// workerId
return `师傅${workerId}`
},
//
getFileList() {
if (!this.orderInfo.fileData) return []
try {
let files
if (typeof this.orderInfo.fileData === 'string') {
try {
files = JSON.parse(this.orderInfo.fileData)
} catch (e) {
files = this.orderInfo.fileData.split(',').filter(Boolean)
}
} else if (Array.isArray(this.orderInfo.fileData)) {
files = this.orderInfo.fileData
} else {
files = []
}
return Array.isArray(files) ? files : []
} catch (e) {
console.error('解析文件数据失败:', e)
return []
}
},
//
isImage(fileUrl) {
if (!fileUrl || typeof fileUrl !== 'string') return false
const imageExtensions = ['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.webp']
const lowerUrl = fileUrl.toLowerCase()
return imageExtensions.some(ext => lowerUrl.includes(ext))
},
//
previewFile(file) {
if (this.isImage(file)) {
this.previewImageUrl = file
this.imagePreviewVisible = true
} else {
//
window.open(file, '_blank')
}
},
//
previewImage(img, images) {
this.previewImageUrl = img
this.imagePreviewVisible = true
},
//
getLogNodeClass(type) {
const classMap = {
'create': 'log-node-primary',
'pay': 'log-node-success',
'dispatch': 'log-node-info',
'accept': 'log-node-success',
'start': 'log-node-warning',
'complete': 'log-node-success',
'cancel': 'log-node-danger'
}
return classMap[type] || 'log-node-default'
},
//
getLogIcon(type) {
const iconMap = {
'create': 'el-icon-plus',
'pay': 'el-icon-money',
'dispatch': 'el-icon-s-promotion',
'accept': 'el-icon-check',
'start': 'el-icon-video-play',
'complete': 'el-icon-circle-check',
'cancel': 'el-icon-close'
}
return iconMap[type] || 'el-icon-info'
},
//
refreshLogs() {
this.$emit('refresh-logs')
},
//
exportLogs() {
this.$emit('export-logs')
},
//
handleDispatch() {
this.$emit('dispatch-order')
},
//
handleAccept() {
this.$emit('accept-order')
},
//
handleStart() {
this.$emit('start-service')
},
//
handleComplete() {
this.$emit('complete-service')
}
}
}
</script>
<style lang="scss" scoped>
.order-detail-dialog {
.el-dialog__body {
padding: 0;
}
}
.order-detail-container {
display: flex;
min-height: 600px;
gap: 20px;
}
/* 左侧订单信息区域 */
.order-info-section {
flex: 1;
padding: 20px;
background: #fff;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.section-header {
display: flex;
align-items: center;
margin-bottom: 20px;
padding-bottom: 12px;
border-bottom: 2px solid #f0f0f0;
i {
margin-right: 8px;
font-size: 18px;
color: #409EFF;
}
span {
font-size: 16px;
font-weight: 600;
color: #303133;
}
}
.info-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 16px;
margin-bottom: 24px;
}
.info-item {
display: flex;
flex-direction: column;
gap: 6px;
label {
font-size: 12px;
color: #909399;
font-weight: 500;
}
.value {
font-size: 14px;
color: #303133;
padding: 8px 12px;
background: #f8f9fa;
border-radius: 6px;
border: 1px solid #e9ecef;
min-height: 20px;
&.price {
color: #e6a23c;
font-weight: 600;
}
&.discount {
color: #67c23a;
font-weight: 500;
}
&.address {
word-break: break-all;
line-height: 1.4;
}
}
}
.remark-section,
.attachment-section {
margin-top: 24px;
}
.remark-content {
padding: 12px 16px;
background: #f8f9fa;
border: 1px solid #e9ecef;
border-radius: 6px;
color: #606266;
font-size: 14px;
line-height: 1.6;
min-height: 20px;
white-space: pre-wrap;
word-break: break-word;
}
.attachment-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(80px, 1fr));
gap: 12px;
}
.attachment-item {
cursor: pointer;
transition: all 0.3s ease;
&:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
}
.attachment-image {
width: 80px;
height: 80px;
border-radius: 8px;
border: 2px solid #e9ecef;
object-fit: cover;
}
.attachment-icon {
width: 80px;
height: 80px;
border-radius: 8px;
background: #f5f7fa;
border: 2px solid #e9ecef;
display: flex;
align-items: center;
justify-content: center;
color: #909399;
font-size: 24px;
}
.image-slot {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
background: #f5f7fa;
color: #909399;
font-size: 20px;
}
/* 右侧日志区域 */
.log-section {
flex: 1;
padding: 20px;
background: #fff;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.header-actions {
margin-left: auto;
display: flex;
gap: 8px;
}
.log-timeline {
position: relative;
}
.log-item {
position: relative;
display: flex;
align-items: flex-start;
margin-bottom: 24px;
&:last-child {
margin-bottom: 0;
}
}
.log-node {
width: 32px;
height: 32px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin-right: 16px;
flex-shrink: 0;
position: relative;
z-index: 2;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
i {
font-size: 14px;
color: white;
}
}
.log-node-primary {
background: linear-gradient(135deg, #409EFF, #36a3f7);
}
.log-node-success {
background: linear-gradient(135deg, #67C23A, #5daf34);
}
.log-node-warning {
background: linear-gradient(135deg, #E6A23C, #d49426);
}
.log-node-info {
background: linear-gradient(135deg, #909399, #7a7d83);
}
.log-node-danger {
background: linear-gradient(135deg, #F56C6C, #e64242);
}
.log-node-default {
background: linear-gradient(135deg, #909399, #7a7d83);
}
.log-content {
flex: 1;
background: white;
border-radius: 8px;
padding: 16px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
border: 1px solid #ebeef5;
}
.log-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
}
.log-title {
font-size: 14px;
font-weight: 600;
color: #303133;
}
.log-time {
font-size: 12px;
color: #909399;
background: #f5f7fa;
padding: 4px 8px;
border-radius: 12px;
}
.log-body {
color: #606266;
line-height: 1.6;
}
.log-description {
margin-bottom: 12px;
font-size: 14px;
}
.log-images {
display: flex;
gap: 8px;
flex-wrap: wrap;
margin-bottom: 12px;
}
.log-image {
width: 60px;
height: 60px;
border-radius: 6px;
border: 1px solid #ebeef5;
object-fit: cover;
cursor: pointer;
transition: all 0.3s ease;
&:hover {
border-color: #409eff;
transform: scale(1.05);
}
}
.log-actions {
margin-top: 12px;
padding-top: 12px;
border-top: 1px solid #f0f0f0;
display: flex;
gap: 8px;
flex-wrap: wrap;
}
.log-connector {
position: absolute;
left: 16px;
top: 32px;
width: 2px;
height: 24px;
background: linear-gradient(to bottom, #e4e7ed, #c0c4cc);
z-index: 1;
}
.empty-logs {
text-align: center;
padding: 40px 20px;
}
/* 对话框底部 */
.dialog-footer {
text-align: center;
padding: 15px 20px;
border-top: 1px solid #e4e7ed;
background: #f8f9fa;
}
/* 响应式设计 */
@media (max-width: 1200px) {
.order-detail-container {
flex-direction: column;
}
.info-grid {
grid-template-columns: 1fr;
}
}
@media (max-width: 768px) {
.order-detail-dialog {
.el-dialog {
width: 95% !important;
}
}
.order-detail-container {
gap: 16px;
}
.order-info-section,
.log-section {
padding: 16px;
}
.info-grid {
gap: 12px;
}
.attachment-grid {
grid-template-columns: repeat(auto-fill, minmax(60px, 1fr));
gap: 8px;
}
.attachment-image,
.attachment-icon {
width: 60px;
height: 60px;
}
.log-item {
margin-bottom: 20px;
}
.log-node {
width: 28px;
height: 28px;
margin-right: 12px;
i {
font-size: 12px;
}
}
.log-content {
padding: 12px;
}
.log-actions {
flex-direction: column;
gap: 6px;
}
.log-actions .el-button {
width: 100%;
margin-right: 0 !important;
}
}
</style>

View File

@ -325,7 +325,7 @@
</el-table-column>
<!-- 修复操作栏样式确保按钮是标准圆形 -->
<el-table-column label="操作" align="center" width="200" fixed="right" class-name="small-padding fixed-width">
<el-table-column label="操作" align="center" width="280" fixed="right" class-name="small-padding fixed-width">
<template slot-scope="scope">
<div class="button-group" v-if="scope.row.id">
<!-- 派单按钮 - 只有待接单状态才显示 -->
@ -354,6 +354,13 @@
@click="handleViewOrderDetail(scope.row)"
v-hasPermi="['system:Order:edit']"
>查看详情</el-button>
<el-button
size="mini"
type="info"
icon="el-icon-document"
@click="handleOrderDetail(scope.row)"
v-hasPermi="['system:Order:edit']"
>订单明细</el-button>
<el-button
size="mini"
type="danger"
@ -1858,6 +1865,18 @@
@success="handleUnifiedRefundSuccess"
/>
<!-- 订单详情对话框 -->
<OrderDetailDialog
:visible.sync="orderDetailVisible"
:orderInfo="currentOrderDetail"
@refresh-logs="handleRefreshOrderLogs"
@export-logs="handleExportOrderLogs"
@dispatch-order="handleDispatchFromDetail"
@accept-order="handleAcceptFromDetail"
@start-service="handleStartFromDetail"
@complete-service="handleCompleteFromDetail"
/>
<!-- 退款弹窗 -->
<el-dialog
title="退款申请"
@ -2104,6 +2123,7 @@ import CommentRecord from './components/CommentRecord'
import EditInfo from './components/EditInfo'
import Pagination from '@/components/Pagination'
import UnifiedRefundDialog from '../GoodsOrder/UnifiedRefundDialog'
import OrderDetailDialog from './components/OrderDetailDialog'
export default {
name: "Order",
@ -2116,7 +2136,8 @@ export default {
CommentRecord,
EditInfo,
Pagination,
UnifiedRefundDialog
UnifiedRefundDialog,
OrderDetailDialog
},
data() {
return {
@ -2494,6 +2515,10 @@ export default {
unifiedRefundDialogVisible: false, // 退
currentRefundOrderId: '', // 退ID
currentRefundPaymentData: {}, // 退
//
orderDetailVisible: false, //
currentOrderDetail: {}, //
refundRules: {
refundAmount: [
{ required: true, message: '请输入退款金额', trigger: 'blur' },
@ -4736,6 +4761,8 @@ export default {
this.loadReceiveRecords(row.orderId)
},
/** 获取时间轴节点样式类 */
getTimelineNodeClass(title) {
if (!title) return 'timeline-node-default'
@ -5099,6 +5126,91 @@ export default {
return jsonString; //
}
},
/** 刷新订单日志 */
handleRefreshOrderLogs() {
// API
this.$message.success('日志数据已刷新')
},
/** 导出订单日志 */
handleExportOrderLogs() {
//
this.$message.success('日志导出功能开发中')
},
/** 从订单详情处理派单 */
handleDispatchFromDetail() {
//
this.orderDetailVisible = false
//
this.handleDispatch(this.currentOrderDetail)
},
/** 从订单详情处理接单 */
handleAcceptFromDetail() {
this.$message.success('接单操作成功')
// API
},
/** 从订单详情处理开始服务 */
handleStartFromDetail() {
this.$message.success('开始服务操作成功')
// API
},
/** 从订单详情处理完成服务 */
handleCompleteFromDetail() {
this.$message.success('完成服务操作成功')
// API
},
/** 查看订单明细 */
handleOrderDetail(row) {
//
this.currentOrderDetail = {
id: row.id,
orderId: row.orderId,
status: row.status,
jsonStatus: row.jsonStatus,
workerId: row.workerId,
userName: row.uname || row.name,
userPhone: row.phone || row.userPhone,
productName: row.productName,
num: row.num,
appointmentTime: row.makeTime ? this.formatAppointmentTime(row.makeTime, row.makeHour) : '未设置',
appointmentAddress: row.address,
totalPrice: row.totalPrice,
payPrice: row.payPrice,
deduction: row.deduction || 0,
createdAt: this.formatCreatedTime(row.createdAt),
payTime: row.payTime ? this.formatCreatedTime(row.payTime) : '未设置',
mark: row.mark,
fileData: row.fileData,
//
logs: [
{
id: 1,
type: 'create',
title: '订单创建',
content: '用户创建订单',
createdAt: row.createdAt ? new Date(row.createdAt).toISOString() : new Date().toISOString(),
images: []
},
{
id: 2,
type: 'pay',
title: '支付成功',
content: '订单支付完成',
createdAt: row.payTime ? new Date(row.payTime).toISOString() : new Date().toISOString(),
images: []
}
]
}
//
this.orderDetailVisible = true
},
},
computed: {
imageListOnly() {
@ -5198,7 +5310,9 @@ export default {
justify-content: space-around;
align-items: center;
padding: 0 5px;
min-width: 150px;
min-width: 250px;
flex-wrap: wrap;
gap: 5px;
}
.button-group .el-button {
@ -5213,6 +5327,13 @@ export default {
justify-content: center;
}
.button-group .el-button:not([circle]) {
width: auto;
min-width: 60px;
border-radius: 4px;
padding: 6px 12px;
}
.button-group .el-button + .el-button {
margin-left: 5px;
}