|
|
@@ -212,6 +212,7 @@ export class PrintTriggerService {
|
|
|
state: number;
|
|
|
payState: number;
|
|
|
createdAt: Date;
|
|
|
+ remark: string | null;
|
|
|
} | null> {
|
|
|
try {
|
|
|
const order = await this.orderRepository.findOne({
|
|
|
@@ -242,7 +243,8 @@ export class PrintTriggerService {
|
|
|
recevierName: order.recevierName,
|
|
|
state: order.state,
|
|
|
payState: order.payState,
|
|
|
- createdAt: order.createdAt
|
|
|
+ createdAt: order.createdAt,
|
|
|
+ remark: order.remark
|
|
|
};
|
|
|
} catch (error) {
|
|
|
console.error(`[租户${tenantId}] 获取完整订单信息失败,订单ID: ${orderId}:`, error);
|
|
|
@@ -301,6 +303,9 @@ export class PrintTriggerService {
|
|
|
订单状态: {orderStatus}
|
|
|
支付状态: {payStatus}
|
|
|
<BR>
|
|
|
+<B>订单备注</B>
|
|
|
+{remark}
|
|
|
+<BR>
|
|
|
<C>感谢您的惠顾!</C>
|
|
|
<BR>
|
|
|
<QR>{orderNo}</QR>
|
|
|
@@ -330,6 +335,9 @@ export class PrintTriggerService {
|
|
|
订单状态: {orderStatus}
|
|
|
支付状态: {payStatus}
|
|
|
<BR>
|
|
|
+<B>订单备注</B>
|
|
|
+{remark}
|
|
|
+<BR>
|
|
|
<C>感谢您的惠顾!</C>
|
|
|
<BR>
|
|
|
<QR>{orderNo}</QR>
|
|
|
@@ -360,6 +368,7 @@ export class PrintTriggerService {
|
|
|
state: number;
|
|
|
payState: number;
|
|
|
createdAt: Date;
|
|
|
+ remark: string | null;
|
|
|
}
|
|
|
): Promise<string> {
|
|
|
const {
|
|
|
@@ -373,9 +382,44 @@ export class PrintTriggerService {
|
|
|
recevierName,
|
|
|
state,
|
|
|
payState,
|
|
|
- createdAt
|
|
|
+ createdAt,
|
|
|
+ remark
|
|
|
} = orderInfo;
|
|
|
|
|
|
+ // 字符串长度限制函数(按中文字符计算)
|
|
|
+ const limitStringLength = (str: string, maxLength: number): string => {
|
|
|
+ if (!str || str.length <= maxLength) {
|
|
|
+ return str;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 计算字符长度(中文字符算1个长度)
|
|
|
+ let length = 0;
|
|
|
+ let result = '';
|
|
|
+
|
|
|
+ for (const char of str) {
|
|
|
+ // 中文字符范围判断
|
|
|
+ const charCode = char.charCodeAt(0);
|
|
|
+ if (charCode >= 0x4E00 && charCode <= 0x9FFF) {
|
|
|
+ length += 1; // 中文字符
|
|
|
+ } else {
|
|
|
+ length += 0.5; // 英文字符和数字算半个长度
|
|
|
+ }
|
|
|
+
|
|
|
+ if (length <= maxLength) {
|
|
|
+ result += char;
|
|
|
+ } else {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 如果被截断,添加省略号
|
|
|
+ if (result.length < str.length) {
|
|
|
+ result += '...';
|
|
|
+ }
|
|
|
+
|
|
|
+ return result;
|
|
|
+ };
|
|
|
+
|
|
|
try {
|
|
|
// 1. 获取打印模板
|
|
|
const template = await this.getPrintTemplate(tenantId);
|
|
|
@@ -411,26 +455,32 @@ export class PrintTriggerService {
|
|
|
|
|
|
const variables = {
|
|
|
orderNo,
|
|
|
- orderTime: new Date(createdAt).toLocaleString('zh-CN'),
|
|
|
+ orderTime: new Date(createdAt).toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai' }),
|
|
|
receiverName: recevierName || '客户',
|
|
|
receiverPhone: receiverMobile || '未提供',
|
|
|
phone: receiverMobile || '未提供', // 兼容变量
|
|
|
address: address || '未提供地址',
|
|
|
- goodsList: items.map(item => {
|
|
|
- const itemName = item.name || '未命名商品';
|
|
|
- const itemPrice = typeof item.price === 'number' ? item.price : parseFloat(item.price as any) || 0;
|
|
|
- const itemQuantity = typeof item.quantity === 'number' ? item.quantity : parseInt(item.quantity as any, 10) || 0;
|
|
|
- const itemTotal = itemPrice * itemQuantity;
|
|
|
- return `${itemName} × ${itemQuantity} = ¥${itemTotal.toFixed(2)}`;
|
|
|
- }).join('\n') || '暂无商品信息',
|
|
|
- totalAmount: `¥${safeAmount.toFixed(2)}`,
|
|
|
- freightAmount: `¥${safeFreightAmount.toFixed(2)}`,
|
|
|
- payAmount: `¥${safePayAmount.toFixed(2)}`,
|
|
|
+ goodsList: (() => {
|
|
|
+ const goodsLines = items.map(item => {
|
|
|
+ const itemName = item.name || '未命名商品';
|
|
|
+ const itemPrice = typeof item.price === 'number' ? item.price : parseFloat(item.price as any) || 0;
|
|
|
+ const itemQuantity = typeof item.quantity === 'number' ? item.quantity : parseInt(item.quantity as any, 10) || 0;
|
|
|
+ const itemTotal = itemPrice * itemQuantity;
|
|
|
+ return `${itemName} ${itemPrice} × ${itemQuantity} = ${itemTotal.toFixed(2)}`;
|
|
|
+ }).join('\n') || '暂无商品信息';
|
|
|
+
|
|
|
+ // 限制商品列表总长度在500字以内
|
|
|
+ return limitStringLength(goodsLines, 2000);
|
|
|
+ })(),
|
|
|
+ totalAmount: `${safeAmount.toFixed(2)}`,
|
|
|
+ freightAmount: `${safeFreightAmount.toFixed(2)}`,
|
|
|
+ payAmount: `${safePayAmount.toFixed(2)}`,
|
|
|
orderStatus: getOrderStatusLabel(state),
|
|
|
- payStatus: getPayStatusLabel(payState)
|
|
|
+ payStatus: getPayStatusLabel(payState),
|
|
|
+ remark: remark || '无备注'
|
|
|
};
|
|
|
|
|
|
- // 3. 替换模板变量
|
|
|
+ // 替换模板变量
|
|
|
let content = template;
|
|
|
for (const [key, value] of Object.entries(variables)) {
|
|
|
content = content.replace(new RegExp(`{${key}}`, 'g'), value);
|
|
|
@@ -445,7 +495,7 @@ export class PrintTriggerService {
|
|
|
'<CB>订单小票</CB><BR>',
|
|
|
'------------------------<BR>',
|
|
|
`<B>订单号:</B>${orderNo}<BR>`,
|
|
|
- `<B>下单时间:</B>${new Date(createdAt).toLocaleString('zh-CN')}<BR>`,
|
|
|
+ `<B>下单时间:</B>${new Date(createdAt).toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai' })}<BR>`,
|
|
|
`<B>收货人:</B>${recevierName || '客户'}<BR>`,
|
|
|
`<B>联系电话:</B>${receiverMobile || '未提供'}<BR>`,
|
|
|
`<B>地址:</B>${address || '未提供地址'}<BR>`,
|
|
|
@@ -454,15 +504,27 @@ export class PrintTriggerService {
|
|
|
];
|
|
|
|
|
|
// 添加商品明细
|
|
|
+ const goodsDetails: string[] = [];
|
|
|
items.forEach(item => {
|
|
|
const itemPrice = typeof item.price === 'number' ? item.price : parseFloat(item.price as any) || 0;
|
|
|
const itemQuantity = typeof item.quantity === 'number' ? item.quantity : parseInt(item.quantity as any, 10) || 0;
|
|
|
const itemTotal = itemPrice * itemQuantity;
|
|
|
const itemName = item.name || '未命名商品';
|
|
|
- lines.push(`${itemName} x${itemQuantity}<BR>`);
|
|
|
- lines.push(` ¥${itemPrice.toFixed(2)} x ${itemQuantity} = ¥${itemTotal.toFixed(2)}<BR>`);
|
|
|
+ goodsDetails.push(`${itemName} x${itemQuantity}<BR>`);
|
|
|
+ goodsDetails.push(` ¥${itemPrice.toFixed(2)} x ${itemQuantity} = ¥${itemTotal.toFixed(2)}<BR>`);
|
|
|
});
|
|
|
|
|
|
+ // 限制商品明细总长度在100字以内
|
|
|
+ const goodsDetailsStr = goodsDetails.join('');
|
|
|
+ const limitedGoodsDetails = limitStringLength(goodsDetailsStr, 2000);
|
|
|
+
|
|
|
+ // 将限制后的商品明细添加到lines中
|
|
|
+ if (limitedGoodsDetails) {
|
|
|
+ lines.push(limitedGoodsDetails);
|
|
|
+ } else {
|
|
|
+ lines.push('暂无商品信息<BR>');
|
|
|
+ }
|
|
|
+
|
|
|
// 添加总计
|
|
|
const safeAmount = typeof amount === 'number' ? amount : parseFloat(amount as any) || 0;
|
|
|
const safePayAmount = typeof payAmount === 'number' ? payAmount : parseFloat(payAmount as any) || 0;
|
|
|
@@ -473,6 +535,10 @@ export class PrintTriggerService {
|
|
|
lines.push(`<B>运费:</B>¥${safeFreightAmount.toFixed(2)}<BR>`);
|
|
|
lines.push(`<B>实付金额:</B>¥${safePayAmount.toFixed(2)}<BR>`);
|
|
|
lines.push('------------------------<BR>');
|
|
|
+ // 添加备注信息
|
|
|
+ const displayRemark = remark || '无备注';
|
|
|
+ lines.push(`<B>订单备注:</B>${displayRemark}<BR>`);
|
|
|
+ lines.push('------------------------<BR>');
|
|
|
lines.push('<B>感谢您的惠顾!</B><BR>');
|
|
|
lines.push('<QR>https://example.com/order/' + orderNo + '</QR><BR>');
|
|
|
|