Xfy #19

Closed
xfy wants to merge 6 commits from Xfy into new
  1. 4
      app.json
  2. 122
      pages/goods-detail/goods-detail.js
  3. 25
      pages/goods-detail/goods-detail.wxml
  4. 79
      pages/order/detail/index.js
  5. 4
      pages/order/detail/index.json
  6. 106
      pages/order/detail/index.wxml
  7. 206
      pages/order/detail/index.wxss
  8. 283
      pages/order/index.js
  9. 116
      pages/order/index.wxml
  10. 372
      pages/order/index.wxss
  11. 16
      project.private.config.json
  12. 581
      server-example/server-mysql.js

4
app.json

@ -22,6 +22,7 @@
"pages/cooperation/index", "pages/cooperation/index",
"pages/goods/index", "pages/goods/index",
"pages/order/index", "pages/order/index",
"pages/order/detail/index",
"pages/company/company" "pages/company/company"
], ],
"subpackages": [ "subpackages": [
@ -50,7 +51,8 @@
"phone-test", "phone-test",
"clear-storage", "clear-storage",
"gross-weight-tester", "gross-weight-tester",
"fix-connection" "fix-connection",
"order-test"
], ],
"independent": false "independent": false
} }

122
pages/goods-detail/goods-detail.js

@ -26,63 +26,26 @@ function calculateDisplayPrice(price) {
return String(price); return String(price);
} }
// 格式化分享标题 - 按优先级: 地区、规格、数量、蛋黄颜色、蛋壳颜色 // 格式化分享标题 - 只显示货源描述
function formatShareTitle(goodsDetail) { function formatShareTitle(goodsDetail) {
console.log('===== formatShareTitle 开始 ====='); console.log('===== formatShareTitle 开始 =====');
console.log('goodsDetail:', JSON.stringify(goodsDetail, null, 2)); console.log('goodsDetail:', JSON.stringify(goodsDetail, null, 2));
const titleParts = []; // 优先使用货源描述
const description = (goodsDetail.description || '').trim();
// 1. 地区 (region) console.log('description:', description);
const region = (goodsDetail.region || '').trim();
console.log('region:', region);
if (region) {
titleParts.push(region);
}
// 2. 规格 (specification/spec)
const specification = (goodsDetail.specification || goodsDetail.spec || goodsDetail.specs || '').trim();
console.log('specification:', specification);
if (specification) {
titleParts.push(specification);
}
// 3. 数量 (quantity)
const quantity = (goodsDetail.quantity || '').trim();
console.log('quantity:', quantity);
if (quantity) {
titleParts.push(`${quantity}`);
}
// 4. 蛋黄颜色 (yolk)
const yolk = (goodsDetail.yolk || '').trim();
console.log('yolk:', yolk);
if (yolk) {
titleParts.push(yolk);
}
// 5. 蛋壳颜色 (category) if (description) {
const category = (goodsDetail.category || '').trim(); console.log('最终标题:', description);
console.log('category:', category); console.log('===== formatShareTitle 结束 =====');
if (category) { return description;
titleParts.push(category);
} }
// 过滤空值后组合标题 // 如果没有货源描述,返回默认标题
const validParts = titleParts.filter(part => part && part.length > 0); const defaultTitle = goodsDetail.name ? `优质鸡蛋 - ${goodsDetail.name}` : '优质鸡蛋货源';
const result = validParts.join(' '); console.log('最终标题:', defaultTitle);
console.log('titleParts:', titleParts);
console.log('validParts:', validParts);
console.log('最终标题:', result);
console.log('===== formatShareTitle 结束 ====='); console.log('===== formatShareTitle 结束 =====');
return defaultTitle;
if (validParts.length > 0) {
return result;
}
// 如果没有匹配的数据,返回默认标题
return goodsDetail.name ? `优质鸡蛋 - ${goodsDetail.name}` : '优质鸡蛋货源';
} }
// 构建商品分享消息内容 // 构建商品分享消息内容
@ -592,6 +555,67 @@ Page({
} }
}, },
// 添加到收藏时的分享信息设置
onAddToFavorites() {
const goodsDetail = this.data.goodsDetail || {};
const title = formatShareTitle(goodsDetail);
// 获取联系人、电话号码和地区信息(提取省份)
const contactName = goodsDetail.product_contact || '';
const contactPhone = goodsDetail.contact_phone || '';
const region = extractProvince(goodsDetail.region || '');
// 构建分享查询参数
const contactNameParam = encodeURIComponent(contactName);
const contactPhoneParam = encodeURIComponent(contactPhone);
const regionParam = encodeURIComponent(region);
let queryParams = [`productId=${goodsDetail.productId || goodsDetail.id}`];
// 添加联系人信息到查询参数
if (contactName) {
queryParams.push(`contactName=${contactNameParam}`);
}
if (contactPhone) {
queryParams.push(`contactPhone=${contactPhoneParam}`);
}
if (region) {
queryParams.push(`region=${regionParam}`);
}
const contactQuery = queryParams.join('&');
// 确定分享图片
let imageUrl = '/images/你有好蛋.png';
const mediaItems = goodsDetail.mediaItems || [];
const hasVideo = mediaItems.some(item => item.type === 'video');
const hasImage = mediaItems.some(item => item.type === 'image');
if (hasVideo && !hasImage) {
// 纯视频商品:使用提取的封面或默认图片
if (this.data.videoCoverUrl) {
imageUrl = this.data.videoCoverUrl;
console.log('使用提取的视频封面收藏分享:', imageUrl);
} else {
console.log('视频封面尚未提取完成,使用默认图片');
// 尝试立即提取视频封面
const videoItems = mediaItems.filter(item => item.type === 'video');
if (videoItems.length > 0) {
this.extractVideoFirstFrame(videoItems[0].url);
}
}
} else {
imageUrl = getShareImageUrl(goodsDetail);
}
return {
title: title,
imageUrl: imageUrl,
query: contactQuery
}
},
// 视频帧提取功能 // 视频帧提取功能
extractVideoFirstFrame(videoUrl) { extractVideoFirstFrame(videoUrl) {
console.log('开始提取视频帧:', videoUrl); console.log('开始提取视频帧:', videoUrl);

25
pages/goods-detail/goods-detail.wxml

@ -72,18 +72,23 @@
<!-- 商品基本信息 --> <!-- 商品基本信息 -->
<view class="goods-info"> <view class="goods-info">
<view style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 10rpx;"> <view style="display: flex; flex-direction: column; margin-bottom: 10rpx;">
<view style="display: flex; align-items: center; flex: 1;"> <view style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 8rpx;">
<view wx:if="{{goodsDetail.status === 'sold_out'}}" style="display: inline-block; margin-right: 10rpx; font-size: 18rpx; color: #fff; background: linear-gradient(135deg, #8c8c8c 0%, #a6a6a6 100%); padding: 4rpx 10rpx; border-radius: 15rpx; vertical-align: middle; backdrop-filter: blur(10rpx); border: 1rpx solid rgba(255, 255, 255, 0.3); box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.15), inset 0 1rpx 0 rgba(255, 255, 255, 0.5); text-shadow: 0 1rpx 2rpx rgba(0, 0, 0, 0.2); font-weight: bold; margin-top: -20rpx;">售空</view> <view style="display: flex; align-items: center; flex: 1;">
<view wx:elif="{{goodsDetail.supplyStatus}}" style="display: inline-block; margin-right: 10rpx; font-size: 18rpx; color: #fff; background: rgba(218, 165, 32, 0.8); padding: 4rpx 10rpx; border-radius: 15rpx; vertical-align: middle; backdrop-filter: blur(10rpx); border: 1rpx solid rgba(255, 255, 255, 0.3); box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.15), inset 0 1rpx 0 rgba(255, 255, 255, 0.5); text-shadow: 0 1rpx 2rpx rgba(0, 0, 0, 0.2); font-weight: bold; margin-top: -20rpx;">{{goodsDetail.supplyStatus}}</view> <view wx:if="{{goodsDetail.status === 'sold_out'}}" style="display: inline-block; margin-right: 10rpx; font-size: 18rpx; color: #fff; background: linear-gradient(135deg, #8c8c8c 0%, #a6a6a6 100%); padding: 4rpx 10rpx; border-radius: 15rpx; vertical-align: middle; backdrop-filter: blur(10rpx); border: 1rpx solid rgba(255, 255, 255, 0.3); box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.15), inset 0 1rpx 0 rgba(255, 255, 255, 0.5); text-shadow: 0 1rpx 2rpx rgba(0, 0, 0, 0.2); font-weight: bold; margin-top: -20rpx;">售空</view>
<text class="goods-name">{{goodsDetail.name}}</text> <view wx:elif="{{goodsDetail.supplyStatus}}" style="display: inline-block; margin-right: 10rpx; font-size: 18rpx; color: #fff; background: rgba(218, 165, 32, 0.8); padding: 4rpx 10rpx; border-radius: 15rpx; vertical-align: middle; backdrop-filter: blur(10rpx); border: 1rpx solid rgba(255, 255, 255, 0.3); box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.15), inset 0 1rpx 0 rgba(255, 255, 255, 0.5); text-shadow: 0 1rpx 2rpx rgba(0, 0, 0, 0.2); font-weight: bold; margin-top: -20rpx;">{{goodsDetail.supplyStatus}}</view>
<span style="vertical-align: middle; font-size: 20rpx; color: white; background: linear-gradient(135deg, #4a90e2 0%, #2b66f0 50%, #1a4bbd 100%); padding: 4rpx 8rpx; clip-path: polygon(50% 0%, 70% 10%, 100% 30%, 100% 70%, 70% 90%, 50% 100%, 30% 90%, 0% 70%, 0% 30%, 30% 10%); margin-left: 8rpx; box-shadow: 0 2rpx 4rpx rgba(0, 0, 0, 0.3), inset 0 1rpx 2rpx rgba(255, 255, 255, 0.5); text-shadow: 0 1rpx 2rpx rgba(0, 0, 0, 0.5); font-weight: bold; margin-top: -20rpx;">V</span> <text class="goods-name">{{goodsDetail.name}}</text>
</view> <span style="vertical-align: middle; font-size: 20rpx; color: white; background: linear-gradient(135deg, #4a90e2 0%, #2b66f0 50%, #1a4bbd 100%); padding: 4rpx 8rpx; clip-path: polygon(50% 0%, 70% 10%, 100% 30%, 100% 70%, 70% 90%, 50% 100%, 30% 90%, 0% 70%, 0% 30%, 30% 10%); margin-left: 8rpx; box-shadow: 0 2rpx 4rpx rgba(0, 0, 0, 0.3), inset 0 1rpx 2rpx rgba(255, 255, 255, 0.5); text-shadow: 0 1rpx 2rpx rgba(0, 0, 0, 0.5); font-weight: bold; margin-top: -20rpx;">V</span>
<view style="display: flex; align-items: flex-start;"> </view>
<view class="source-type-badge" style="display: flex; align-items: center; justify-content: center; margin-right: 10rpx;"> <view style="display: flex; align-items: flex-start;">
<text style="color: {{goodsDetail.sourceTypeColor}}; font-weight: bold;">{{goodsDetail.sourceType || '暂无'}}</text> <view class="source-type-badge" style="display: flex; align-items: center; justify-content: center; margin-right: 10rpx;">
<text style="color: {{goodsDetail.sourceTypeColor}}; font-weight: bold;">{{goodsDetail.sourceType || '暂无'}}</text>
</view>
</view> </view>
</view> </view>
<view class="source-description" style="padding: 8rpx 16rpx; background: #f5f5f5; border-radius: 8rpx; font-size: 28rpx; color: #333; margin-top: 4rpx;">
{{goodsDetail.description || '暂无描述'}}
</view>
</view> </view>
<view class="goods-price" style="position: relative; display: flex; align-items: center; justify-content: space-between;"> <view class="goods-price" style="position: relative; display: flex; align-items: center; justify-content: space-between;">
<view style="display: flex; align-items: center;"> <view style="display: flex; align-items: center;">

79
pages/order/detail/index.js

@ -0,0 +1,79 @@
// pages/order/detail/index.js
Page({
// 分享给朋友/群聊
onShareAppMessage() {
return {
title: '鸡蛋贸易平台 - 订单详情',
path: '/pages/order/detail/index',
imageUrl: '/images/你有好蛋.png'
}
},
// 分享到朋友圈
onShareTimeline() {
return {
title: '鸡蛋贸易平台 - 订单详情',
query: '',
imageUrl: '/images/你有好蛋.png'
}
},
data: {
orderId: '',
orderDetail: null,
loading: false,
error: ''
},
onLoad(options) {
if (options.orderId) {
this.setData({ orderId: options.orderId });
this.loadOrderDetail();
}
},
// 加载订单详情
loadOrderDetail() {
const orderId = this.data.orderId;
if (!orderId) {
this.setData({ error: '订单ID不能为空' });
return;
}
this.setData({ loading: true, error: '' });
wx.request({
url: `http://192.168.1.30:3003/api/orders/detail/${orderId}`,
method: 'GET',
header: {
'Content-Type': 'application/json'
},
success: (res) => {
console.log('获取订单详情成功:', res.data);
if (res.data.success) {
this.setData({
orderDetail: res.data.data,
loading: false
});
} else {
this.setData({
error: res.data.message || '获取订单详情失败',
loading: false
});
}
},
fail: (err) => {
console.error('获取订单详情失败:', err);
this.setData({
error: '网络请求失败,请稍后重试',
loading: false
});
}
});
},
// 返回上一页
goBack() {
wx.navigateBack();
}
})

4
pages/order/detail/index.json

@ -0,0 +1,4 @@
{
"navigationBarTitleText": "订单详情",
"usingComponents": {}
}

106
pages/order/detail/index.wxml

@ -0,0 +1,106 @@
<view class="order-detail-page">
<!-- 加载中 -->
<view wx:if="{{loading}}" class="loading">
<text>加载中...</text>
</view>
<!-- 错误提示 -->
<view wx:elif="{{error}}" class="error">
<text>{{error}}</text>
<button bindtap="loadOrderDetail">重新加载</button>
</view>
<!-- 订单详情 -->
<view wx:elif="{{orderDetail}}" class="order-content">
<!-- 订单头部 -->
<view class="order-header">
<view class="order-no">订单号: {{orderDetail.sales_no}}</view>
<view class="order-date">{{orderDetail.order_date}}</view>
</view>
<!-- 客户信息 -->
<view class="section">
<view class="section-title">客户信息</view>
<view class="customer-info">
<view class="info-item">
<view class="label">公司名称:</view>
<view class="value">{{orderDetail.customer_company}}</view>
</view>
<view class="info-item">
<view class="label">联系人:</view>
<view class="value">{{orderDetail.contact_person}}</view>
</view>
<view class="info-item">
<view class="label">联系电话:</view>
<view class="value">{{orderDetail.phone}}</view>
</view>
<view class="info-item">
<view class="label">联系地址:</view>
<view class="value">{{orderDetail.address}}</view>
</view>
</view>
</view>
<!-- 订单摘要 -->
<view class="section">
<view class="section-title">订单摘要</view>
<view class="order-summary">
<view class="info-item">
<view class="label">总金额:</view>
<view class="value total-amount">¥{{orderDetail.total_amount}}</view>
</view>
<view class="info-item">
<view class="label">总件数:</view>
<view class="value">{{orderDetail.total_pieces}}</view>
</view>
<view class="info-item">
<view class="label">总斤数:</view>
<view class="value">{{orderDetail.total_weight}}</view>
</view>
<view class="info-item">
<view class="label">支付状态:</view>
<view class="value payment-status">{{orderDetail.payment_status}}</view>
</view>
<view class="info-item">
<view class="label">订单状态:</view>
<view class="value order-status">{{orderDetail.order_status}}</view>
</view>
</view>
</view>
<!-- 产品详情 -->
<view class="section">
<view class="section-title">产品详情</view>
<view class="products-list">
<view wx:for="{{orderDetail.subItems}}" wx:key="sub_id" class="product-item">
<view class="product-header">
<view class="product-name">{{item.product_name}}</view>
</view>
<view class="product-details">
<view class="info-item">
<view class="label">销售件数:</view>
<view class="value">{{item.sales_pieces}}</view>
</view>
<view class="info-item">
<view class="label">销售斤数:</view>
<view class="value">{{item.sales_weight}}</view>
</view>
<view class="info-item">
<view class="label">单价:</view>
<view class="value">¥{{item.unit_price}}</view>
</view>
<view class="info-item">
<view class="label">小计:</view>
<view class="value">¥{{item.sales_amount}}</view>
</view>
</view>
</view>
</view>
</view>
</view>
<!-- 无数据提示 -->
<view wx:else class="no-data">
<text>暂无订单详情</text>
</view>
</view>

206
pages/order/detail/index.wxss

@ -0,0 +1,206 @@
/* pages/order/detail/index.wxss */
.order-detail-page {
min-height: 100vh;
background-color: #f5f5f5;
padding: 20rpx;
}
/* 加载中 */
.loading {
display: flex;
justify-content: center;
align-items: center;
padding: 120rpx 0;
background-color: #fff;
border-radius: 12rpx;
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
font-size: 32rpx;
color: #666;
}
/* 错误提示 */
.error {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding: 120rpx 40rpx;
background-color: #fff;
border-radius: 12rpx;
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
font-size: 32rpx;
color: #ff4d4f;
text-align: center;
}
.error button {
margin-top: 40rpx;
background-color: #1677ff;
color: #fff;
border: none;
padding: 20rpx 80rpx;
border-radius: 60rpx;
font-size: 28rpx;
font-weight: bold;
}
/* 订单内容 */
.order-content {
background-color: #fff;
border-radius: 12rpx;
padding: 30rpx;
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
}
/* 订单头部 */
.order-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 30rpx;
padding-bottom: 30rpx;
border-bottom: 1rpx solid #f0f0f0;
}
.order-no {
font-size: 28rpx;
color: #333;
font-weight: bold;
}
.order-date {
font-size: 26rpx;
color: #999;
}
/* 区块样式 */
.section {
margin-bottom: 30rpx;
}
.section-title {
font-size: 30rpx;
color: #333;
font-weight: bold;
margin-bottom: 20rpx;
padding-left: 20rpx;
border-left: 6rpx solid #1677ff;
}
/* 客户信息 */
.customer-info {
background-color: #f9f9f9;
border-radius: 8rpx;
padding: 20rpx;
}
/* 订单摘要 */
.order-summary {
background-color: #f9f9f9;
border-radius: 8rpx;
padding: 20rpx;
}
/* 信息项 */
.info-item {
display: flex;
margin-bottom: 16rpx;
align-items: flex-start;
}
.info-item:last-child {
margin-bottom: 0;
}
.label {
width: 140rpx;
font-size: 26rpx;
color: #666;
font-weight: bold;
flex-shrink: 0;
}
.value {
flex: 1;
font-size: 26rpx;
color: #333;
word-break: break-all;
line-height: 1.4;
}
/* 总金额样式 */
.total-amount {
font-size: 28rpx;
color: #ff4d4f;
font-weight: bold;
}
/* 状态样式 */
.payment-status {
color: #52c41a;
font-weight: bold;
}
.order-status {
color: #1677ff;
font-weight: bold;
}
/* 产品列表 */
.products-list {
background-color: #f9f9f9;
border-radius: 8rpx;
padding: 20rpx;
}
/* 产品项 */
.product-item {
background-color: #fff;
border-radius: 8rpx;
padding: 20rpx;
margin-bottom: 16rpx;
box-shadow: 0 1rpx 4rpx rgba(0, 0, 0, 0.05);
}
.product-item:last-child {
margin-bottom: 0;
}
/* 产品头部 */
.product-header {
margin-bottom: 16rpx;
padding-bottom: 16rpx;
border-bottom: 1rpx solid #f0f0f0;
}
.product-name {
font-size: 28rpx;
color: #333;
font-weight: bold;
}
/* 产品详情 */
.product-details {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 16rpx;
}
@media (max-width: 375px) {
.product-details {
grid-template-columns: 1fr;
}
}
/* 无数据提示 */
.no-data {
display: flex;
justify-content: center;
align-items: center;
padding: 120rpx 0;
background-color: #fff;
border-radius: 12rpx;
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
font-size: 32rpx;
color: #999;
}

283
pages/order/index.js

@ -1,4 +1,6 @@
// pages/order/index.js // pages/order/index.js
const API = require('../../utils/api.js');
Page({ Page({
// 分享给朋友/群聊 // 分享给朋友/群聊
onShareAppMessage() { onShareAppMessage() {
@ -20,38 +22,293 @@ Page({
data: { data: {
userInfo: {}, userInfo: {},
orders: [] orders: [],
loading: false,
error: '',
// 订单状态标签
activeTab: 'all',
// 时间筛选相关
dateRange: {
start: '',
end: ''
},
// 分页相关
page: 1,
pageSize: 10,
hasMore: true,
loadingMore: false,
// 统计信息
statistics: {
totalOrders: 0,
totalAmount: 0,
totalPieces: 0,
totalWeight: 0,
unpaidAmount: 0,
paidAmount: 0
}
}, },
onLoad() { onLoad() {
// 页面加载时的初始化逻辑 // 页面加载时的初始化逻辑
this.loadUserInfo(); this.loadUserInfo(() => {
this.loadOrders();
});
}, },
onShow() { onShow() {
// 页面显示时的逻辑 // 页面显示时的逻辑
this.loadUserInfo(); this.loadUserInfo(() => {
// 更新自定义tabBar状态 // 更新自定义tabBar状态
if (typeof this.getTabBar === 'function' && this.getTabBar()) { if (typeof this.getTabBar === 'function' && this.getTabBar()) {
this.getTabBar().setData({ this.getTabBar().setData({
selected: 4 // 保持与个人中心相同的选中状态 selected: 4 // 保持与个人中心相同的选中状态
}); });
} }
});
}, },
// 加载用户信息 // 加载用户信息
loadUserInfo() { loadUserInfo(callback) {
const app = getApp(); const app = getApp();
let userInfo = {};
if (app.globalData.userInfo) { if (app.globalData.userInfo) {
this.setData({ userInfo: app.globalData.userInfo }); userInfo = app.globalData.userInfo;
} else { } else {
const localUserInfo = wx.getStorageSync('userInfo') || {}; userInfo = wx.getStorageSync('userInfo') || {};
this.setData({ userInfo: localUserInfo });
} }
// 同步更新用户信息,确保loadOrders能获取到最新数据
this.data.userInfo = userInfo;
// 同时异步更新UI
this.setData({ userInfo: userInfo }, () => {
if (callback) callback();
});
},
// 加载订单数据
loadOrders(isLoadMore = false, activeTab = this.data.activeTab) {
const userInfo = this.data.userInfo;
const phoneNumber = userInfo.phoneNumber;
if (!phoneNumber) {
this.setData({
error: '请先登录并绑定电话号码',
orders: []
});
return;
}
if (isLoadMore) {
this.setData({ loadingMore: true });
} else {
this.setData({ loading: true, error: '' });
}
const page = isLoadMore ? this.data.page + 1 : 1;
const pageSize = this.data.pageSize;
// 根据标签确定付款状态
let paymentStatus = '';
if (activeTab === 'unpaid') {
paymentStatus = '未收款';
} else if (activeTab === 'completed') {
paymentStatus = '全款';
}
console.log('loadOrders - activeTab:', activeTab, 'paymentStatus:', paymentStatus, 'page:', page);
// 并行请求订单列表和统计数据
const orderRequest = API.request('/api/orders/list', 'POST', {
phoneNumber: phoneNumber,
startDate: this.data.dateRange.start,
endDate: this.data.dateRange.end,
paymentStatus: paymentStatus,
page: page,
pageSize: pageSize
}).then(res => {
return { data: res };
}).catch(err => {
throw err;
});
// 只有在非加载更多时才请求统计数据(加载更多时统计数据不变)
const statisticsRequest = isLoadMore ? Promise.resolve(null) : API.request('/api/orders/statistics', 'POST', {
phoneNumber: phoneNumber,
startDate: this.data.dateRange.start,
endDate: this.data.dateRange.end,
paymentStatus: paymentStatus
}).then(res => {
return { data: res };
}).catch(err => {
throw err;
});
// 处理所有请求
Promise.all([orderRequest, statisticsRequest])
.then(([orderRes, statisticsRes]) => {
console.log('获取订单列表成功:', orderRes.data);
if (orderRes.data.success) {
const newOrders = orderRes.data.data.orders || [];
const totalOrders = isLoadMore ? [...this.data.orders, ...newOrders] : newOrders;
const hasMore = newOrders.length === pageSize;
// 处理统计数据
let statistics = this.data.statistics;
if (statisticsRes && statisticsRes.data && statisticsRes.data.success) {
statistics = statisticsRes.data.data;
console.log('获取统计数据成功:', statistics);
}
this.setData({
orders: totalOrders,
page: page,
hasMore: hasMore,
loading: false,
loadingMore: false,
statistics: statistics
});
} else {
this.setData({
error: orderRes.data.message || '获取订单失败',
loading: false,
loadingMore: false
});
}
})
.catch((err) => {
console.error('请求失败:', err);
this.setData({
error: err.message || '网络请求失败,请稍后重试',
loading: false,
loadingMore: false
});
});
}, },
// 返回上一页 // 返回上一页
goBack() { goBack() {
wx.navigateBack(); wx.navigateBack();
},
// 查看订单详情
viewOrderDetail(e) {
const orderId = e.currentTarget.dataset.orderId;
wx.navigateTo({
url: `/pages/order/detail/index?orderId=${orderId}`
});
},
// 去首页购物
goToHome() {
wx.switchTab({
url: '/pages/index/index'
});
},
// 选择开始日期
bindStartDateChange(e) {
const startDate = e.detail.value;
this.setData({
'dateRange.start': startDate
});
},
// 选择结束日期
bindEndDateChange(e) {
const endDate = e.detail.value;
this.setData({
'dateRange.end': endDate
});
},
// 重置日期筛选
resetDateFilter() {
this.setData({
'dateRange.start': '',
'dateRange.end': ''
});
this.loadOrders();
},
// 应用日期筛选
applyDateFilter() {
this.loadOrders();
},
// 切换订单状态标签
switchTab(e) {
const tab = e.currentTarget.dataset.tab;
console.log('switchTab - tab:', tab);
this.setData({
activeTab: tab,
page: 1,
orders: []
}, () => {
console.log('switchTab - after setData, activeTab:', this.data.activeTab);
this.loadOrders(false, tab);
});
},
// 计算统计信息
calculateStatistics() {
const orders = this.data.orders;
if (!orders || orders.length === 0) {
this.setData({
statistics: {
totalOrders: 0,
totalAmount: 0,
totalPieces: 0,
unpaidAmount: 0,
paidAmount: 0
}
});
return;
}
let totalOrders = orders.length;
let totalAmount = 0;
let totalPieces = 0;
let unpaidAmount = 0;
let paidAmount = 0;
orders.forEach(order => {
// 累加总金额和总件数
if (order.total_amount) {
totalAmount += parseFloat(order.total_amount) || 0;
}
if (order.total_pieces) {
totalPieces += parseInt(order.total_pieces) || 0;
}
// 区分未付款和已付款
if (order.payment_status === '未收款') {
if (order.total_amount) {
unpaidAmount += parseFloat(order.total_amount) || 0;
}
} else if (order.payment_status === '全款') {
if (order.total_amount) {
paidAmount += parseFloat(order.total_amount) || 0;
}
}
});
this.setData({
statistics: {
totalOrders,
totalAmount: Math.round(totalAmount * 100) / 100,
totalPieces,
unpaidAmount: Math.round(unpaidAmount * 100) / 100,
paidAmount: Math.round(paidAmount * 100) / 100
}
});
},
// 滚动到底部的预加载逻辑
onReachBottom() {
if (!this.data.loadingMore && this.data.hasMore) {
this.loadOrders(true, this.data.activeTab);
}
} }
}) })

116
pages/order/index.wxml

@ -1,21 +1,123 @@
<view class="order-page"> <view class="order-page">
<!-- 订单状态标签栏 --> <!-- 订单状态标签栏 -->
<view class="order-tabs"> <view class="order-tabs">
<view class="tab-item active">全部</view> <view class="tab-item {{activeTab === 'all' ? 'active' : ''}}" bindtap="switchTab" data-tab="all">全部</view>
<view class="tab-item">待付款</view> <view class="tab-item {{activeTab === 'unpaid' ? 'active' : ''}}" bindtap="switchTab" data-tab="unpaid">待付款</view>
<view class="tab-item">待发货</view> <view class="tab-item {{activeTab === 'completed' ? 'active' : ''}}" bindtap="switchTab" data-tab="completed">已完成</view>
<view class="tab-item">待收货</view> </view>
<view class="tab-item">已完成</view>
<!-- 时间筛选组件 -->
<view class="date-filter">
<view class="date-filter-row">
<view class="date-picker">
<view class="date-label">开始日期:</view>
<picker mode="date" value="{{dateRange.start}}" bindchange="bindStartDateChange">
<view class="picker">
{{dateRange.start || '请选择'}}
</view>
</picker>
</view>
<view class="date-picker">
<view class="date-label">结束日期:</view>
<picker mode="date" value="{{dateRange.end}}" bindchange="bindEndDateChange">
<view class="picker">
{{dateRange.end || '请选择'}}
</view>
</picker>
</view>
</view>
<view class="filter-actions">
<button class="reset-btn" bindtap="resetDateFilter">重置</button>
<button class="apply-btn" bindtap="applyDateFilter">应用筛选</button>
</view>
</view>
<!-- 统计信息 -->
<view class="statistics-card">
<view class="statistics-header">
<text class="statistics-title">订单统计</text>
</view>
<view class="statistics-body">
<view class="stat-item">
<view class="stat-label">总订单数</view>
<view class="stat-value">{{statistics.totalOrders}}</view>
</view>
<view class="stat-item">
<view class="stat-label">总件数</view>
<view class="stat-value">{{statistics.totalPieces}}</view>
</view>
<view class="stat-item">
<view class="stat-label">总斤数</view>
<view class="stat-value">{{statistics.totalWeight}}</view>
</view>
<view class="stat-item">
<view class="stat-label">已消费</view>
<view class="stat-value paid">¥{{statistics.paidAmount}}</view>
</view>
</view>
</view> </view>
<!-- 订单列表 --> <!-- 订单列表 -->
<view class="order-list"> <view class="order-list">
<!-- 暂无订单提示 --> <!-- 加载中 -->
<view class="no-orders"> <view wx:if="{{loading}}" class="loading">
<text>加载中...</text>
</view>
<!-- 错误提示 -->
<view wx:elif="{{error}}" class="error">
<text>{{error}}</text>
</view>
<!-- 无订单提示 -->
<view wx:elif="{{orders.length === 0}}" class="no-orders">
<view class="no-orders-icon">📦</view> <view class="no-orders-icon">📦</view>
<view class="no-orders-title">空空如也~</view> <view class="no-orders-title">空空如也~</view>
<view class="no-orders-subtitle">您还没有任何订单,快去选购吧</view> <view class="no-orders-subtitle">您还没有任何订单,快去选购吧</view>
<button class="go-shopping-btn" bindtap="goToHome">去首页购物</button> <button class="go-shopping-btn" bindtap="goToHome">去首页购物</button>
</view> </view>
<!-- 订单列表 -->
<view wx:else class="orders">
<view wx:for="{{orders}}" wx:key="dataid" class="order-card" bindtap="viewOrderDetail" data-order-id="{{item.dataid}}">
<!-- 订单头部 -->
<view class="order-header">
<view class="order-no">订单号: {{item.sales_no}}</view>
<view class="order-date">{{item.order_date}}</view>
</view>
<!-- 订单主体 -->
<view class="order-body">
<view class="customer-info">
<view class="customer-company">{{item.customer_company}}</view>
<view class="contact-person">{{item.contact_person}} {{item.phone}}</view>
</view>
<view class="order-summary">
<view class="total-amount">总金额: ¥{{item.total_amount}}</view>
<view class="total-pieces">总件数: {{item.total_pieces}}</view>
<view class="total-weight">总斤数: {{item.total_weight}}</view>
</view>
</view>
<!-- 订单底部 -->
<view class="order-footer">
<view class="status-container">
<view class="payment-status">{{item.payment_status}}</view>
<view class="order-status">{{item.order_status}}</view>
</view>
<view class="arrow-icon">→</view>
</view>
</view>
<!-- 分页加载状态 -->
<view class="load-more">
<view wx:if="{{loadingMore}}" class="loading-more">
<text>加载中...</text>
</view>
<view wx:elif="{{!hasMore && orders.length > 0}}" class="no-more">
<text>没有更多数据了</text>
</view>
</view>
</view>
</view> </view>
</view> </view>

372
pages/order/index.wxss

@ -9,23 +9,25 @@
display: flex; display: flex;
background-color: #fff; background-color: #fff;
border-bottom: 1rpx solid #e0e0e0; border-bottom: 1rpx solid #e0e0e0;
overflow-x: auto;
white-space: nowrap;
} }
.tab-item { .tab-item {
flex: 1; flex: 1;
min-width: 160rpx;
text-align: center; text-align: center;
padding: 24rpx 0; padding: 20rpx 0;
font-size: 32rpx; font-size: 28rpx;
color: #666; color: #666;
position: relative; position: relative;
transition: all 0.2s ease;
}
.tab-item:hover {
color: #1677ff;
} }
.tab-item.active { .tab-item.active {
color: #1677ff; color: #1677ff;
font-weight: bold; font-weight: 500;
} }
.tab-item.active::after { .tab-item.active::after {
@ -34,10 +36,186 @@
bottom: 0; bottom: 0;
left: 50%; left: 50%;
transform: translateX(-50%); transform: translateX(-50%);
width: 60rpx; width: 50rpx;
height: 6rpx; height: 5rpx;
background-color: #1677ff; background-color: #1677ff;
border-radius: 3rpx; border-radius: 3rpx;
transition: all 0.2s ease;
}
/* 时间筛选组件 */
.date-filter {
background-color: #fff;
padding: 16rpx;
margin: 8rpx;
border-radius: 10rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.04);
}
.date-filter-row {
display: flex;
justify-content: space-between;
margin-bottom: 16rpx;
gap: 12rpx;
}
.date-picker {
flex: 1;
display: flex;
flex-direction: column;
}
.date-label {
font-size: 22rpx;
color: #666;
margin-bottom: 6rpx;
font-weight: 500;
}
.picker {
border: 1rpx solid #e8e8e8;
border-radius: 6rpx;
padding: 14rpx;
font-size: 22rpx;
color: #333;
background-color: #f9f9f9;
transition: all 0.2s ease;
}
.picker:hover {
border-color: #1677ff;
background-color: #f0f7ff;
}
.filter-actions {
display: flex;
justify-content: flex-end;
gap: 12rpx;
padding-top: 12rpx;
border-top: 1rpx solid #f5f5f5;
}
.reset-btn {
padding: 12rpx 32rpx;
border-radius: 24rpx;
font-size: 22rpx;
border: 1rpx solid #d9d9d9;
color: #666;
background-color: #f9f9f9;
transition: all 0.2s ease;
}
.reset-btn:hover {
border-color: #999;
background-color: #f0f0f0;
}
.apply-btn {
padding: 12rpx 32rpx;
border-radius: 24rpx;
font-size: 22rpx;
border: 1rpx solid #1677ff;
color: #1677ff;
background-color: #fff;
font-weight: 500;
transition: all 0.2s ease;
}
.apply-btn:hover {
background-color: #1677ff;
color: #fff;
}
/* 统计信息卡片 */
.statistics-card {
background-color: #fff;
margin: 10rpx;
border-radius: 12rpx;
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
overflow: hidden;
}
.statistics-header {
background-color: #1677ff;
color: #fff;
padding: 20rpx;
font-size: 28rpx;
font-weight: bold;
}
.statistics-body {
padding: 20rpx;
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 20rpx;
}
.stat-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 15rpx;
background-color: #f9f9f9;
border-radius: 8rpx;
}
.stat-label {
font-size: 24rpx;
color: #666;
font-weight: 500;
}
.stat-value {
font-size: 28rpx;
font-weight: bold;
color: #333;
}
.stat-value.unpaid {
color: #ff4d4f;
}
.stat-value.paid {
color: #52c41a;
}
/* 响应式设计 */
@media (max-width: 375px) {
.date-filter-row {
flex-direction: column;
}
.date-picker {
flex-direction: row;
align-items: center;
}
.date-label {
margin-bottom: 0;
margin-right: 10rpx;
min-width: 120rpx;
}
.picker {
flex: 1;
}
.statistics-body {
grid-template-columns: 1fr;
}
.stat-item {
padding: 12rpx;
}
.stat-label {
font-size: 22rpx;
}
.stat-value {
font-size: 26rpx;
}
} }
/* 订单列表 */ /* 订单列表 */
@ -45,6 +223,33 @@
padding: 20rpx; padding: 20rpx;
} }
/* 加载中 */
.loading {
display: flex;
justify-content: center;
align-items: center;
padding: 120rpx 0;
background-color: #fff;
border-radius: 12rpx;
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
font-size: 32rpx;
color: #666;
}
/* 错误提示 */
.error {
display: flex;
justify-content: center;
align-items: center;
padding: 120rpx 40rpx;
background-color: #fff;
border-radius: 12rpx;
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
font-size: 32rpx;
color: #ff4d4f;
text-align: center;
}
/* 暂无订单提示 */ /* 暂无订单提示 */
.no-orders { .no-orders {
display: flex; display: flex;
@ -85,13 +290,19 @@
font-weight: bold; font-weight: bold;
} }
/* 订单卡片样式(预留) */ /* 订单卡片样式 */
.order-card { .order-card {
background-color: #fff; background-color: #fff;
border-radius: 12rpx; border-radius: 12rpx;
padding: 30rpx; padding: 30rpx;
margin-bottom: 20rpx; margin-bottom: 20rpx;
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05); box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.order-card:active {
transform: translateY(2rpx);
box-shadow: 0 1rpx 5rpx rgba(0, 0, 0, 0.05);
} }
.order-header { .order-header {
@ -103,60 +314,75 @@
border-bottom: 1rpx solid #f0f0f0; border-bottom: 1rpx solid #f0f0f0;
} }
.order-info { .order-no {
font-size: 28rpx; font-size: 28rpx;
color: #666; color: #333;
font-weight: bold;
} }
.order-status { .order-date {
font-size: 28rpx; font-size: 26rpx;
color: #1677ff; color: #999;
font-weight: bold;
} }
.order-goods { .order-body {
margin-bottom: 20rpx; margin-bottom: 20rpx;
} }
.goods-item { .customer-info {
display: flex;
margin-bottom: 20rpx; margin-bottom: 20rpx;
} }
.goods-image { .customer-company {
width: 120rpx; font-size: 30rpx;
height: 120rpx; color: #333;
border-radius: 8rpx; font-weight: bold;
margin-right: 20rpx; margin-bottom: 10rpx;
} }
.goods-info { .contact-person {
flex: 1; font-size: 26rpx;
color: #666;
}
.order-summary {
display: flex; display: flex;
flex-direction: column; flex-wrap: wrap;
justify-content: space-between; gap: 30rpx;
padding: 20rpx;
background-color: #f9f9f9;
border-radius: 8rpx;
} }
.goods-name { .total-amount {
font-size: 32rpx; font-size: 28rpx;
color: #333; color: #333;
margin-bottom: 10rpx; font-weight: bold;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
} }
.goods-spec { .total-amount::before {
font-size: 26rpx; content: "💰 ";
color: #999; font-size: 24rpx;
margin-bottom: 10rpx;
} }
.goods-price { .total-pieces {
font-size: 32rpx; font-size: 28rpx;
color: #ff4d4f; color: #666;
font-weight: bold; }
.total-pieces::before {
content: "📦 ";
font-size: 24rpx;
}
.total-weight {
font-size: 28rpx;
color: #666;
}
.total-weight::before {
content: "⚖️ ";
font-size: 24rpx;
} }
.order-footer { .order-footer {
@ -167,31 +393,61 @@
border-top: 1rpx solid #f0f0f0; border-top: 1rpx solid #f0f0f0;
} }
.total-price { .status-container {
font-size: 32rpx; display: flex;
color: #333; gap: 20rpx;
align-items: center;
} }
.total-price .price { .payment-status {
color: #ff4d4f; font-size: 26rpx;
color: #52c41a;
font-weight: bold;
padding: 6rpx 16rpx;
background-color: #f6ffed;
border-radius: 20rpx;
border: 1rpx solid #b7eb8f;
}
.order-status {
font-size: 26rpx;
color: #1677ff;
font-weight: bold;
padding: 6rpx 16rpx;
background-color: #e6f7ff;
border-radius: 20rpx;
border: 1rpx solid #91d5ff;
}
.arrow-icon {
font-size: 32rpx;
color: #999;
font-weight: bold; font-weight: bold;
} }
.order-actions { /* 分页加载相关样式 */
.load-more {
padding: 30rpx 0;
text-align: center;
}
.loading-more {
display: flex; display: flex;
justify-content: center;
align-items: center;
padding: 20rpx 0;
} }
.action-btn { .loading-more text {
padding: 12rpx 30rpx;
border-radius: 40rpx;
font-size: 28rpx; font-size: 28rpx;
margin-left: 20rpx; color: #666;
border: 2rpx solid #1677ff;
color: #1677ff;
background-color: #fff;
} }
.action-btn.primary { .no-more {
background-color: #1677ff; padding: 20rpx 0;
color: #fff; }
.no-more text {
font-size: 26rpx;
color: #999;
} }

16
project.private.config.json

@ -1,6 +1,6 @@
{ {
"libVersion": "3.10.3", "libVersion": "3.10.3",
"projectname": "xcx22", "projectname": "Mini-Program",
"setting": { "setting": {
"urlCheck": false, "urlCheck": false,
"coverView": true, "coverView": true,
@ -19,5 +19,17 @@
"ignoreDevUnusedFiles": true, "ignoreDevUnusedFiles": true,
"bigPackageSizeSupport": false "bigPackageSizeSupport": false
}, },
"condition": {} "condition": {
"miniprogram": {
"list": [
{
"name": "pages/test-tools/order-test",
"pathName": "pages/test-tools/order-test",
"query": "",
"scene": null,
"launchMode": "default"
}
]
}
}
} }

581
server-example/server-mysql.js

@ -399,6 +399,29 @@ const userLoginSequelize = new Sequelize(
} }
); );
// 3. trade_library数据源连接(新添加)
const tradeLibrarySequelize = new Sequelize(
'trade_library',
dbConfig.user,
dbConfig.password,
{
host: dbConfig.host,
port: dbConfig.port,
dialect: 'mysql',
pool: {
max: 10,
min: 0,
acquire: 30000,
idle: 10000
},
logging: console.log,
define: {
timestamps: false
},
timezone: '+08:00' // 设置时区为UTC+8
}
);
// 为保持兼容性,保留默认sequelize引用(指向wechat_app) // 为保持兼容性,保留默认sequelize引用(指向wechat_app)
const sequelize = wechatAppSequelize; const sequelize = wechatAppSequelize;
@ -1296,8 +1319,239 @@ Cover.init({
timestamps: false timestamps: false
}); });
// 简道云销售订单主表模型(新添加)
class JdSalesMain extends Model { }
JdSalesMain.init({
dataid: {
type: DataTypes.STRING(50),
primaryKey: true,
allowNull: false,
comment: '主表自增主键(简道云插件关联用)'
},
sales_no: {
type: DataTypes.STRING(50),
allowNull: false,
unique: true,
comment: '销售单号(业务唯一编号)'
},
order_date: {
type: DataTypes.DATEONLY,
allowNull: false,
comment: '下单日期'
},
customer_company: {
type: DataTypes.STRING(200),
allowNull: false,
comment: '客户公司'
},
contact_person: {
type: DataTypes.STRING(100),
allowNull: false,
comment: '联系人'
},
phone: {
type: DataTypes.STRING(20),
allowNull: true,
comment: '联系电话(允许带区号等特殊字符,用字符串存储)'
},
salesman: {
type: DataTypes.STRING(100),
allowNull: true,
comment: '销售员'
},
merchandiser: {
type: DataTypes.STRING(100),
allowNull: true,
comment: '跟单员'
},
address: {
type: DataTypes.STRING(500),
allowNull: true,
comment: '地址'
},
total_pieces: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: 0,
comment: '总件数'
},
total_weight: {
type: DataTypes.DECIMAL(10, 3),
allowNull: false,
defaultValue: 0.000,
comment: '总斤数(保留3位小数)'
},
total_amount: {
type: DataTypes.DECIMAL(12, 2),
allowNull: false,
defaultValue: 0.00,
comment: '总金额(保留2位小数)'
},
vehicle_scale: {
type: DataTypes.STRING(100),
allowNull: true,
comment: '车辆规模'
},
payment_voucher: {
type: DataTypes.STRING(255),
allowNull: true,
comment: '支付凭证(存储照片路径)'
},
invoice: {
type: DataTypes.STRING(255),
allowNull: true,
comment: '发票(存储照片路径)'
},
payment_status: {
type: DataTypes.STRING(50),
allowNull: true,
comment: '付款状态(如:未付款/部分付款/已付清)'
},
order_status: {
type: DataTypes.STRING(50),
allowNull: true
},
chepai: {
type: DataTypes.STRING(255),
allowNull: true,
comment: '车牌'
}
}, {
sequelize: tradeLibrarySequelize,
modelName: 'JdSalesMain',
tableName: 'jd_sales_main',
timestamps: false,
indexes: [
{
unique: true,
fields: ['sales_no'],
name: 'idx_sales_no',
comment: '销售单号唯一索引(避免重复)'
}
]
});
// 简道云销售订单明细表模型(新添加)
class JdSalesSub extends Model { }
JdSalesSub.init({
sub_id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true,
comment: '子表自增主键'
},
dataid: {
type: DataTypes.STRING(50),
allowNull: false,
comment: '关联主表dataid'
},
product_name: {
type: DataTypes.STRING(200),
allowNull: false,
comment: '产品名称'
},
batch_no: {
type: DataTypes.STRING(100),
allowNull: true,
comment: '批次号'
},
unit: {
type: DataTypes.STRING(50),
allowNull: true,
comment: '单位(件,斤)'
},
yolk: {
type: DataTypes.STRING(50),
allowNull: true,
comment: '蛋黄(描述信息)'
},
specification: {
type: DataTypes.STRING(100),
allowNull: true,
comment: '规格'
},
sales_pieces: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: 0,
comment: '销售件数'
},
sales_weight: {
type: DataTypes.DECIMAL(10, 3),
allowNull: false,
defaultValue: 0.000,
comment: '销售斤数(保留3位小数)'
},
unit_price: {
type: DataTypes.DECIMAL(10, 2),
allowNull: false,
defaultValue: 0.00,
comment: '单价(保留2位小数)'
},
purchase_price: {
type: DataTypes.DECIMAL(10, 2),
allowNull: true,
comment: '采购价(保留2位小数)'
},
sales_amount: {
type: DataTypes.DECIMAL(12, 2),
allowNull: false,
defaultValue: 0.00,
comment: '销售金额'
},
discount_amount: {
type: DataTypes.DECIMAL(10, 2),
allowNull: true,
defaultValue: 0.00,
comment: '优惠金额'
},
rebate_amount: {
type: DataTypes.DECIMAL(10, 2),
allowNull: true,
defaultValue: 0.00,
comment: '折扣金额'
},
purchase_amount: {
type: DataTypes.DECIMAL(12, 2),
allowNull: true,
comment: '采购金额'
},
discount_reason: {
type: DataTypes.STRING(500),
allowNull: true,
comment: '优惠原因'
}
}, {
sequelize: tradeLibrarySequelize,
modelName: 'JdSalesSub',
tableName: 'jd_sales_sub',
timestamps: false,
indexes: [
{
fields: ['dataid'],
name: 'idx_main_id',
comment: '关联主表索引(加速查询)'
}
]
});
// 定义模型之间的关联关系 // 定义模型之间的关联关系
// 简道云销售订单主表和明细表的关联关系
JdSalesMain.hasMany(JdSalesSub, {
foreignKey: 'dataid',
sourceKey: 'dataid',
as: 'subItems',
onDelete: 'CASCADE',
onUpdate: 'CASCADE'
});
JdSalesSub.belongsTo(JdSalesMain, {
foreignKey: 'dataid',
targetKey: 'dataid',
as: 'mainOrder'
});
// 用户和商品的一对多关系 (卖家发布商品) // 用户和商品的一对多关系 (卖家发布商品)
User.hasMany(Product, { User.hasMany(Product, {
foreignKey: 'sellerId', // 外键字段名 foreignKey: 'sellerId', // 外键字段名
@ -9871,13 +10125,340 @@ updateProductContacts().then(() => {
}); });
}); });
// 订单查询API端点 - 根据登录用户的电话号码查询订单信息
app.post('/api/orders/list', async (req, res) => {
try {
const { phoneNumber, startDate, endDate, paymentStatus, page = 1, pageSize = 10 } = req.body;
console.log('获取订单列表请求:', { phoneNumber, startDate, endDate, paymentStatus, page, pageSize });
console.log('请求体完整内容:', req.body);
if (!phoneNumber) {
return res.status(400).json({
success: false,
code: 400,
message: '用户电话号码不能为空'
});
}
// 构建查询条件
const whereCondition = {
phone: phoneNumber
};
// 添加时间范围过滤
if (startDate) {
whereCondition.order_date = {
...whereCondition.order_date,
[Op.gte]: startDate
};
console.log('添加开始日期过滤:', startDate);
}
if (endDate) {
whereCondition.order_date = {
...whereCondition.order_date,
[Op.lte]: endDate
};
console.log('添加结束日期过滤:', endDate);
}
// 添加付款状态过滤
if (paymentStatus) {
whereCondition.payment_status = paymentStatus;
console.log('添加付款状态过滤:', paymentStatus);
}
console.log('最终查询条件:', whereCondition);
// 计算分页偏移量
const offset = (page - 1) * pageSize;
console.log('分页参数:', { page, pageSize, offset });
// 查询trade_library数据库中的订单主表,根据电话号码和时间范围过滤
const orders = await JdSalesMain.findAll({
where: whereCondition,
include: [
{
model: JdSalesSub,
as: 'subItems',
attributes: [
'sub_id',
'product_name',
'batch_no',
'unit',
'yolk',
'specification',
'sales_pieces',
'sales_weight',
'unit_price',
'sales_amount',
'discount_amount',
'rebate_amount',
'discount_reason'
]
}
],
order: [['order_date', 'DESC']],
limit: pageSize,
offset: offset
});
console.log('查询到的订单数量:', orders.length);
// 格式化响应数据
const formattedOrders = orders.map(order => {
const orderData = order.toJSON();
// 确保order_date是字符串格式
if (orderData.order_date instanceof Date) {
orderData.order_date = orderData.order_date.toISOString().split('T')[0];
}
// 确保数值字段正确格式化
orderData.total_pieces = parseInt(orderData.total_pieces) || 0;
orderData.total_weight = parseFloat(orderData.total_weight) || 0;
orderData.total_amount = parseFloat(orderData.total_amount) || 0;
// 格式化子项数据
if (orderData.subItems) {
orderData.subItems = orderData.subItems.map(subItem => ({
...subItem,
sales_pieces: parseInt(subItem.sales_pieces) || 0,
sales_weight: parseFloat(subItem.sales_weight) || 0,
unit_price: parseFloat(subItem.unit_price) || 0,
sales_amount: parseFloat(subItem.sales_amount) || 0,
discount_amount: parseFloat(subItem.discount_amount) || 0,
rebate_amount: parseFloat(subItem.rebate_amount) || 0
}));
}
return orderData;
});
res.status(200).json({
success: true,
code: 200,
message: '获取订单列表成功',
data: {
orders: formattedOrders,
totalCount: formattedOrders.length
}
});
} catch (error) {
console.error('获取订单列表失败:', error);
res.status(500).json({
success: false,
code: 500,
message: '获取订单列表失败: ' + error.message
});
}
});
// 订单详情API端点 - 根据订单ID查询订单详情
app.get('/api/orders/detail/:dataid', async (req, res) => {
try {
const { dataid } = req.params;
console.log('获取订单详情请求:', { dataid });
if (!dataid) {
return res.status(400).json({
success: false,
code: 400,
message: '订单ID不能为空'
});
}
// 查询订单详情
const order = await JdSalesMain.findOne({
where: {
dataid
},
include: [
{
model: JdSalesSub,
as: 'subItems',
attributes: [
'sub_id',
'product_name',
'batch_no',
'unit',
'yolk',
'specification',
'sales_pieces',
'sales_weight',
'unit_price',
'sales_amount',
'discount_amount',
'rebate_amount',
'discount_reason'
]
}
]
});
if (!order) {
return res.status(404).json({
success: false,
code: 404,
message: '订单不存在'
});
}
// 格式化响应数据
const orderData = order.toJSON();
// 确保order_date是字符串格式
if (orderData.order_date instanceof Date) {
orderData.order_date = orderData.order_date.toISOString().split('T')[0];
}
// 确保数值字段正确格式化
orderData.total_pieces = parseInt(orderData.total_pieces) || 0;
orderData.total_weight = parseFloat(orderData.total_weight) || 0;
orderData.total_amount = parseFloat(orderData.total_amount) || 0;
// 格式化子项数据
if (orderData.subItems) {
orderData.subItems = orderData.subItems.map(subItem => ({
...subItem,
sales_pieces: parseInt(subItem.sales_pieces) || 0,
sales_weight: parseFloat(subItem.sales_weight) || 0,
unit_price: parseFloat(subItem.unit_price) || 0,
sales_amount: parseFloat(subItem.sales_amount) || 0,
discount_amount: parseFloat(subItem.discount_amount) || 0,
rebate_amount: parseFloat(subItem.rebate_amount) || 0
}));
}
res.status(200).json({
success: true,
code: 200,
message: '获取订单详情成功',
data: orderData
});
} catch (error) {
console.error('获取订单详情失败:', error);
res.status(500).json({
success: false,
code: 500,
message: '获取订单详情失败: ' + error.message
});
}
});
// 订单统计API端点 - 获取完整的订单统计数据(不分页)
app.post('/api/orders/statistics', async (req, res) => {
try {
const { phoneNumber, startDate, endDate, paymentStatus } = req.body;
console.log('获取订单统计请求:', { phoneNumber, startDate, endDate, paymentStatus });
if (!phoneNumber) {
return res.status(400).json({
success: false,
code: 400,
message: '用户电话号码不能为空'
});
}
// 构建查询条件
const whereCondition = {
phone: phoneNumber
};
// 添加时间范围过滤
if (startDate) {
whereCondition.order_date = {
...whereCondition.order_date,
[Op.gte]: startDate
};
}
if (endDate) {
whereCondition.order_date = {
...whereCondition.order_date,
[Op.lte]: endDate
};
}
// 添加付款状态过滤
if (paymentStatus) {
whereCondition.payment_status = paymentStatus;
}
console.log('统计查询条件:', whereCondition);
// 查询所有符合条件的订单(不分页)
const allOrders = await JdSalesMain.findAll({
where: whereCondition,
order: [['order_date', 'DESC']]
});
console.log('统计查询到的订单数量:', allOrders.length);
// 计算统计信息
let totalOrders = allOrders.length;
let totalAmount = 0;
let totalPieces = 0;
let totalWeight = 0;
let unpaidAmount = 0;
let paidAmount = 0;
allOrders.forEach(order => {
const orderData = order.toJSON();
const amount = parseFloat(orderData.total_amount) || 0;
const pieces = parseInt(orderData.total_pieces) || 0;
const weight = parseFloat(orderData.total_weight) || 0;
totalAmount += amount;
totalPieces += pieces;
totalWeight += weight;
if (orderData.payment_status === '未收款') {
unpaidAmount += amount;
} else if (orderData.payment_status === '全款') {
paidAmount += amount;
}
});
const statistics = {
totalOrders,
totalAmount: Math.round(totalAmount * 100) / 100,
totalPieces,
totalWeight: Math.round(totalWeight * 1000) / 1000,
unpaidAmount: Math.round(unpaidAmount * 100) / 100,
paidAmount: Math.round(paidAmount * 100) / 100
};
console.log('计算的完整统计信息:', statistics);
res.status(200).json({
success: true,
code: 200,
message: '获取订单统计成功',
data: statistics
});
} catch (error) {
console.error('获取订单统计失败:', error);
res.status(500).json({
success: false,
code: 500,
message: '获取订单统计失败: ' + error.message
});
}
});
// 导出模型和Express应用供其他模块使用 // 导出模型和Express应用供其他模块使用
module.exports = { module.exports = {
User, User,
Product, Product,
CartItem, CartItem,
Resources, Resources,
JdSalesMain,
JdSalesSub,
sequelize, sequelize,
tradeLibrarySequelize,
createUserAssociations, createUserAssociations,
app, app,
PORT PORT

Loading…
Cancel
Save