Browse Source

修复商品数量变少问题,增加pageSize并优化筛选逻辑

Default User 2 months ago
parent
commit
300b41e7cc
  1. 3
      app.json
  2. 29
      custom-tab-bar/index.js
  3. 27
      pages/buyer/index.js
  4. 333
      pages/goods/index.js
  5. 7
      pages/goods/index.json
  6. 133
      pages/goods/index.wxml
  7. 492
      pages/goods/index.wxss
  8. 16
      pages/index/index.js
  9. 4
      pages/index/index.wxml
  10. 6
      pages/message-list/index.js
  11. 6
      pages/profile/index.js
  12. 17
      pages/seller/index.js
  13. 84
      server-example/server-mysql.js
  14. 89
      utils/api.js

3
app.json

@ -15,7 +15,8 @@
"pages/chat-detail/index", "pages/chat-detail/index",
"pages/message-list/index", "pages/message-list/index",
"pages/customer-service/index", "pages/customer-service/index",
"pages/cooperation/index" "pages/cooperation/index",
"pages/goods/index"
], ],
"subpackages": [ "subpackages": [
{ {

29
custom-tab-bar/index.js

@ -58,7 +58,7 @@ Component({
if (key === 'seller') { if (key === 'seller') {
const userId = wx.getStorageSync('userId'); const userId = wx.getStorageSync('userId');
if (userId) { if (userId) {
// 更新用户类型 // 获取当前用户类型
let users = wx.getStorageSync('users'); let users = wx.getStorageSync('users');
if (typeof users !== 'object' || users === null) { if (typeof users !== 'object' || users === null) {
users = {}; users = {};
@ -66,18 +66,23 @@ Component({
if (!users[userId]) { if (!users[userId]) {
users[userId] = {}; users[userId] = {};
} }
users[userId].type = key; // 只有当当前类型不是 Colleague 时才修改为 seller
wx.setStorageSync('users', users); const currentType = users[userId].type;
if (currentType !== 'Colleague') {
// 更新标签 // 更新用户类型
let tags = wx.getStorageSync('tags'); users[userId].type = key;
if (typeof tags !== 'object' || tags === null) { wx.setStorageSync('users', users);
tags = {};
// 更新标签
let tags = wx.getStorageSync('tags');
if (typeof tags !== 'object' || tags === null) {
tags = {};
}
tags[userId] = tags[userId] || [];
tags[userId] = tags[userId].filter(tag => !tag.startsWith('身份:'));
tags[userId].push(`身份:${key}`);
wx.setStorageSync('tags', tags);
} }
tags[userId] = tags[userId] || [];
tags[userId] = tags[userId].filter(tag => !tag.startsWith('身份:'));
tags[userId].push(`身份:${key}`);
wx.setStorageSync('tags', tags);
} }
} }

27
pages/buyer/index.js

@ -477,7 +477,7 @@ Page({
// 确保用户身份被设置为买家 // 确保用户身份被设置为买家
const userId = wx.getStorageSync('userId'); const userId = wx.getStorageSync('userId');
if (userId) { if (userId) {
// 更新用户类型 // 获取当前用户类型
let users = wx.getStorageSync('users'); let users = wx.getStorageSync('users');
if (typeof users !== 'object' || users === null) { if (typeof users !== 'object' || users === null) {
users = {}; users = {};
@ -485,18 +485,23 @@ Page({
if (!users[userId]) { if (!users[userId]) {
users[userId] = {}; users[userId] = {};
} }
users[userId].type = 'buyer';
wx.setStorageSync('users', users);
// 更新标签 // 只有当当前类型不是 Colleague 时才修改为 buyer
let tags = wx.getStorageSync('tags'); if (users[userId].type !== 'Colleague') {
if (typeof tags !== 'object' || tags === null) { // 更新用户类型
tags = {}; users[userId].type = 'buyer';
wx.setStorageSync('users', users);
// 更新标签
let tags = wx.getStorageSync('tags');
if (typeof tags !== 'object' || tags === null) {
tags = {};
}
tags[userId] = tags[userId] || [];
tags[userId] = tags[userId].filter(tag => !tag.startsWith('身份:'));
tags[userId].push(`身份:buyer`);
wx.setStorageSync('tags', tags);
} }
tags[userId] = tags[userId] || [];
tags[userId] = tags[userId].filter(tag => !tag.startsWith('身份:'));
tags[userId].push(`身份:buyer`);
wx.setStorageSync('tags', tags);
} }
// ✅ 修改:重置分页状态并清空数据 // ✅ 修改:重置分页状态并清空数据

333
pages/goods/index.js

@ -0,0 +1,333 @@
// pages/goods/index.js
const API = require('../../utils/api.js')
Page({
// 分享给朋友/群聊
onShareAppMessage() {
return {
title: '内部货源管理 - 鸡蛋贸易平台',
path: '/pages/goods/index',
imageUrl: '/images/你有好蛋.png'
}
},
// 分享到朋友圈
onShareTimeline() {
return {
title: '内部货源管理 - 鸡蛋贸易平台',
query: '',
imageUrl: '/images/你有好蛋.png'
}
},
/**
* 页面的初始数据
*/
data: {
goodsList: [],
isLoading: false,
isRefreshing: false, // 下拉刷新状态
currentPage: 1,
pageSize: 100,
hasMore: true,
searchKeyword: '',
activeFilter: 'all', // 当前筛选条件:all, small, large
filterConfig: {
small: ['何佳芹', '李真音'], // 小品种创建者
large: ['吴海燕', '陈骏', '刘琴', '汤敏'] // 大贸易创建者
},
total: 0 // 总数据条数
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
this.loadGoodsList()
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady() {
},
/**
* 生命周期函数--监听页面显示
*/
onShow() {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide() {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload() {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh() {
// 这里的onPullDownRefresh是页面级的,但我们使用的是scroll-view内置的下拉刷新,所以不需要实现这个方法
},
/**
* scroll-view下拉刷新事件处理
*/
onRefresherRefresh() {
this.setData({
isRefreshing: true,
currentPage: 1,
goodsList: [],
hasMore: true
})
this.loadGoodsList()
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom() {
if (this.data.hasMore && !this.data.isLoading) {
this.setData({
currentPage: this.data.currentPage + 1
})
this.loadGoodsList()
}
},
/**
* 处理搜索输入
*/
onSearchInput(e) {
this.setData({
searchKeyword: e.detail.value
})
},
/**
* 执行搜索
*/
searchGoods() {
this.setData({
currentPage: 1,
goodsList: [],
hasMore: true
})
this.loadGoodsList()
},
/**
* 清除搜索
*/
clearSearch() {
this.setData({
searchKeyword: '',
currentPage: 1,
goodsList: [],
hasMore: true
})
this.loadGoodsList()
},
/**
* 筛选条件改变
*/
onFilterChange(e) {
const filter = e.currentTarget.dataset.filter
this.setData({
activeFilter: filter,
currentPage: 1,
goodsList: [],
hasMore: true
})
this.loadGoodsList()
},
/**
* 根据筛选条件过滤数据
*/
filterGoodsList(goodsList) {
const { activeFilter, filterConfig } = this.data
if (activeFilter === 'all') {
return goodsList
}
const allowedCreators = filterConfig[activeFilter] || []
return goodsList.filter(item => {
return allowedCreators.includes(item.creatorName)
})
},
/**
* 格式化时间
*/
formatDateTime(dateString) {
if (!dateString) return '未知时间'
// 检查是否已经是格式化好的字符串(如:2026-01-03 10:04:29)
if (typeof dateString === 'string' && /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/.test(dateString)) {
// 直接返回格式化好的字符串,只保留到分钟
return dateString.slice(0, 16)
}
// 检查是否是其他格式的字符串
const date = new Date(dateString)
if (isNaN(date.getTime())) {
// 如果转换失败,直接返回原字符串
return dateString
}
const year = date.getFullYear()
const month = (date.getMonth() + 1).toString().padStart(2, '0')
const day = date.getDate().toString().padStart(2, '0')
const hours = date.getHours().toString().padStart(2, '0')
const minutes = date.getMinutes().toString().padStart(2, '0')
return `${year}-${month}-${day} ${hours}:${minutes}`
},
/**
* 加载货源列表
*/
async loadGoodsList() {
if (this.data.isLoading) return
this.setData({
isLoading: true
})
console.log('开始加载货源列表,参数:', {
page: this.data.currentPage,
pageSize: this.data.pageSize,
keyword: this.data.searchKeyword,
activeFilter: this.data.activeFilter
})
// 调用API获取货源列表
try {
const res = await API.getGoodsList({
page: this.data.currentPage,
pageSize: this.data.pageSize,
keyword: this.data.searchKeyword
})
console.log('API返回结果:', res)
// 检查API返回的状态
if (res.success) {
console.log('API调用成功,products:', res.products)
console.log('products长度:', res.products.length)
console.log('API返回total:', res.total)
// 更新总数据条数
const total = res.total || 0
if (res.products && res.products.length > 0) {
// 显示第一个产品的详细信息
console.log('第一个产品:', res.products[0])
console.log('第一个产品的seller:', res.products[0].seller)
console.log('第一个产品的seller.nickName:', res.products[0].seller?.nickName)
console.log('第一个产品的seller.name:', res.products[0].seller?.name)
console.log('第一个产品的seller完整结构:', JSON.stringify(res.products[0].seller))
let newGoodsList = res.products || []
// 格式化创建时间并处理创建者信息
newGoodsList = newGoodsList.map((item, index) => {
// 详细日志,查看每个产品的seller信息
console.log(`产品${index}的seller信息:`)
console.log(`- seller对象:`, item.seller)
console.log(`- seller.nickName:`, item.seller?.nickName)
console.log(`- seller.name:`, item.seller?.name)
console.log(`- seller完整结构:`, JSON.stringify(item.seller))
// 确定creatorName - 只使用nickName,不使用name字段
const creatorName = item.seller?.nickName || '未知'
console.log(`- 最终确定的creatorName:`, creatorName)
return {
...item,
formattedCreatedAt: this.formatDateTime(item.created_at || item.createTime),
creatorName: creatorName
}
})
// 应用筛选条件
const originalList = [...newGoodsList]
const filteredList = this.filterGoodsList(newGoodsList)
console.log('处理并筛选后的产品列表:', filteredList)
console.log('筛选前后数量对比:', originalList.length, '->', filteredList.length)
// 处理分页逻辑
const updatedGoodsList = this.data.currentPage === 1 ? filteredList : [...this.data.goodsList, ...filteredList]
// 判断是否还有更多数据
// 正确逻辑:如果API返回的原始数据数量小于pageSize,说明没有更多数据
// 即使筛选后的数据量小于pageSize,只要API返回的原始数据数量等于pageSize,就应该继续尝试加载更多数据
const hasMore = res.products.length >= this.data.pageSize
console.log('分页判断:', {
updatedGoodsListLength: updatedGoodsList.length,
originalDataLength: res.products.length,
pageSize: this.data.pageSize,
hasMore: hasMore
})
this.setData({
goodsList: updatedGoodsList,
hasMore: hasMore,
total: total
})
// 如果是第一页,且筛选后的数据不足,尝试加载下一页
if (this.data.currentPage === 1 && hasMore && filteredList.length < this.data.pageSize) {
console.log('筛选后数据不足,继续加载下一页')
this.setData({
currentPage: this.data.currentPage + 1
})
// 递归调用loadGoodsList,继续加载下一页
this.loadGoodsList()
return
}
} else {
console.log('没有产品数据返回')
this.setData({
goodsList: [],
hasMore: false,
total: 0
})
}
} else {
console.error('API调用失败:', res.message)
wx.showToast({
title: res.message || '获取货源列表失败',
icon: 'none'
})
}
} catch (err) {
console.error('获取货源列表失败:', err)
wx.showToast({
title: '获取货源列表失败: ' + err.message,
icon: 'none'
})
} finally {
// 结束下拉刷新和加载状态
this.setData({
isLoading: false,
isRefreshing: false
})
}
}
})

7
pages/goods/index.json

@ -0,0 +1,7 @@
{
"usingComponents": {},
"navigationBarTitleText": "内部货源管理",
"navigationBarBackgroundColor": "#ffffff",
"navigationBarTextStyle": "black",
"backgroundColor": "#f5f5f5"
}

133
pages/goods/index.wxml

@ -0,0 +1,133 @@
<view class="container">
<!-- 页面标题 -->
<view class="page-title">
<text>内部货源管理</text>
</view>
<!-- 搜索框 -->
<view class="search-container">
<view class="search-box">
<input
class="search-input"
placeholder="搜索货源名称或创建人"
bindinput="onSearchInput"
bindconfirm="searchGoods"
value="{{searchKeyword}}"
/>
<view wx:if="{{searchKeyword}}" class="clear-icon" bindtap="clearSearch">✘</view>
</view>
</view>
<!-- 筛选按钮 -->
<view class="filter-container">
<view class="filter-buttons">
<view
class="filter-btn {{activeFilter === 'all' ? 'active' : ''}}"
bindtap="onFilterChange"
data-filter="all"
>
全部
</view>
<view
class="filter-btn {{activeFilter === 'small' ? 'active' : ''}}"
bindtap="onFilterChange"
data-filter="small"
>
小品种
</view>
<view
class="filter-btn {{activeFilter === 'large' ? 'active' : ''}}"
bindtap="onFilterChange"
data-filter="large"
>
大贸易
</view>
</view>
</view>
<!-- 骨架屏加载 -->
<view wx:if="{{isLoading && goodsList.length === 0}}" class="skeleton-container">
<view class="skeleton-grid">
<view class="skeleton-grid-item" wx:for="{{[1,2,3,4,5,6,7,8]}}" wx:key="index">
<view class="skeleton-image"></view>
<view class="skeleton-title"></view>
<view class="skeleton-title short"></view>
<view class="skeleton-footer">
<view class="skeleton-price"></view>
</view>
</view>
</view>
</view>
<!-- 商品列表区域 -->
<view class="goods-section">
<scroll-view
class="goods-list"
bindscrolltolower="onReachBottom"
scroll-y="true"
refresher-enabled="{{true}}"
refresher-triggered="{{isRefreshing}}"
refresher-default-style="black"
refresher-background="transparent"
bindrefresherrefresh="onRefresherRefresh"
>
<view class="goods-list-container">
<!-- 网格布局 -->
<view class="grid-container">
<view
class="grid-item"
wx:for="{{goodsList}}"
wx:key="id"
data-item="{{item}}"
>
<view class="product-card">
<view class="product-image-wrapper">
<image
class="product-image"
src="{{item.imageUrls && item.imageUrls.length > 0 ? item.imageUrls[0] : '/images/default-avatar.png'}}"
mode="aspectFill"
lazy-load="true"
></image>
<view wx:if="{{item.supplyStatus === '预售'}}" class="promo-tag presale">预售</view>
<view wx:if="{{item.supplyStatus === '现货'}}" class="promo-tag in-stock">现货</view>
</view>
<view class="product-info">
<view class="product-title-row">
<view class="product-title">{{item.name}}</view>
<view class="stock-count">库存:{{item.quantity || item.minOrder || '充足'}}</view>
</view>
<view class="product-spec">{{item.specification || item.spec || '无'}}<text wx:if="{{item.yolk}}"> | {{item.yolk}}</text></view>
<view class="product-meta">
<text class="product-price">¥{{item.costprice}}</text>
<text class="product-location">{{item.region || ''}}</text>
</view>
<view class="product-description">{{item.description || ''}}</view>
<view class="creator-info">
<text class="creator-name">{{item.creatorName || '未知'}}</text>
<text class="create-time">{{item.formattedCreatedAt || item.created_at || '未知'}}</text>
</view>
</view>
</view>
</view>
</view>
<!-- 加载更多 -->
<view wx:if="{{isLoading && goodsList.length > 0}}" class="loading-more">
<view class="loading-spinner"></view>
<text class="loading-text">加载中...</text>
</view>
<!-- 无更多数据 -->
<view wx:if="{{!hasMore && goodsList.length > 0}}" class="no-more-data">
<text>货源正在快马加鞭的赶来</text>
</view>
<!-- 空状态 -->
<view wx:if="{{goodsList.length === 0 && !isLoading}}" class="empty-container">
<text class="empty-text">暂无货源数据</text>
<text class="empty-hint">下拉刷新试试</text>
</view>
</view>
</scroll-view>
</view>
</view>

492
pages/goods/index.wxss

@ -0,0 +1,492 @@
.container {
width: 100%;
padding: 20rpx;
box-sizing: border-box;
background-color: #f5f5f5;
min-height: 100vh;
}
.page-title {
font-size: 36rpx;
font-weight: bold;
color: #333;
text-align: center;
margin: 20rpx 0 30rpx;
}
/* 搜索框样式 */
.search-container {
margin-bottom: 20rpx;
}
.search-box {
position: relative;
display: flex;
align-items: center;
background-color: white;
border-radius: 40rpx;
padding: 0 20rpx;
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.1);
}
/* 筛选样式 */
.filter-container {
margin: 0 auto 24rpx;
padding: 0 30rpx;
box-sizing: border-box;
width: 100%;
}
.filter-buttons {
display: flex;
background-color: white;
border-radius: 50rpx;
overflow: hidden;
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.1);
border: 2rpx solid transparent;
transition: all 0.3s ease;
}
.filter-btn {
flex: 1;
text-align: center;
padding: 18rpx 0;
font-size: 28rpx;
color: #666;
background-color: transparent;
border: none;
cursor: pointer;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
position: relative;
font-weight: 500;
}
.filter-btn.active {
color: #1677ff;
font-weight: bold;
background: linear-gradient(135deg, #e6f0ff 0%, #f0f7ff 100%);
box-shadow: inset 0 2rpx 8rpx rgba(22, 119, 255, 0.15);
}
.filter-btn::after {
content: '';
position: absolute;
right: 0;
top: 50%;
transform: translateY(-50%);
width: 2rpx;
height: 48rpx;
background: linear-gradient(180deg, transparent 0%, #e8e8e8 50%, transparent 100%);
}
.filter-btn:last-child::after {
display: none;
}
.filter-btn:active {
opacity: 0.8;
transform: scale(0.98);
}
/* 第一个和最后一个按钮特殊样式 */
.filter-btn:first-child {
border-radius: 50rpx 0 0 50rpx;
}
.filter-btn:last-child {
border-radius: 0 50rpx 50rpx 0;
}
/* 激活状态下的按钮动画效果 */
.filter-btn.active::before {
content: '';
position: absolute;
top: 10rpx;
left: 50%;
transform: translateX(-50%);
width: 12rpx;
height: 6rpx;
background: #1677ff;
border-radius: 3rpx;
animation: pulse 1.5s infinite;
}
@keyframes pulse {
0%, 100% {
opacity: 1;
transform: translateX(-50%) scale(1);
}
50% {
opacity: 0.7;
transform: translateX(-50%) scale(1.1);
}
}
.search-input {
flex: 1;
height: 80rpx;
font-size: 28rpx;
color: #333;
padding: 0 20rpx;
box-sizing: border-box;
}
.search-input::placeholder {
color: #999;
}
.clear-icon {
font-size: 30rpx;
color: #999;
padding: 10rpx;
cursor: pointer;
}
/* 加载中样式 */
.loading-container {
text-align: center;
padding: 60rpx 0;
}
.loading-text {
font-size: 28rpx;
color: #999;
}
/* 货源列表样式 - 网格布局 */
.goods-section {
width: 100%;
margin-top: 20rpx;
height: calc(100vh - 300rpx); /* 设置容器高度,确保scroll-view能正常滚动 */
}
/* 滚动视图样式 */
.goods-list {
height: 100%; /* 设置scroll-view高度为100%,确保能正常滚动 */
overflow: hidden;
}
.goods-list-container {
width: 100%;
padding: 0 10rpx;
box-sizing: border-box;
}
/* 网格容器 */
.grid-container {
display: flex;
flex-wrap: wrap;
gap: 20rpx;
width: 100%;
box-sizing: border-box;
}
/* 网格商品项 */
.grid-item {
width: calc((100% - 20rpx) / 2);
box-sizing: border-box;
}
/* 商品卡片样式 */
.product-card {
background-color: white;
border: 2rpx solid #e0e0e0;
border-radius: 16rpx;
overflow: hidden;
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.08);
transition: all 0.3s ease;
padding: 16rpx;
box-sizing: border-box;
}
.product-card:active {
transform: scale(0.98);
box-shadow: 0 2rpx 6rpx rgba(0, 0, 0, 0.1);
}
/* 商品图片区域 */
.product-image-wrapper {
position: relative;
width: 100%;
height: 200rpx;
background: #f5f5f5;
border-radius: 12rpx;
overflow: hidden;
margin-bottom: 16rpx;
}
.product-image {
width: 100%;
height: 100%;
display: block;
object-fit: cover;
object-position: center;
background-color: #f5f5f5;
}
/* 促销标签 */
.promo-tag {
position: absolute;
top: 0;
left: 0;
padding: 6rpx 12rpx;
font-size: 20rpx;
color: #fff;
border-radius: 0 0 12rpx 0;
z-index: 1;
font-weight: 600;
}
.promo-tag.presale {
background: linear-gradient(135deg, #ff6b00 0%, #ff8c00 100%);
box-shadow: 0 2rpx 8rpx rgba(255, 107, 0, 0.3);
}
.promo-tag.in-stock {
background: linear-gradient(135deg, #52c41a 0%, #73d13d 100%);
box-shadow: 0 2rpx 8rpx rgba(82, 196, 26, 0.3);
}
/* 商品信息区域 */
.product-info {
padding: 0;
display: flex;
flex-direction: column;
gap: 8rpx;
}
/* 商品标题行 - 包含商品名称和库存 */
.product-title-row {
display: flex;
justify-content: flex-start;
align-items: center;
width: 100%;
flex-wrap: wrap;
gap: 8rpx;
}
/* 商品标题 */
.product-title {
font-size: 26rpx;
color: #000000;
line-height: 1.4;
height: auto;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 1;
-webkit-box-orient: vertical;
font-weight: 700;
flex: 1;
margin-right: 0;
}
/* 库存计数 */
.stock-count {
font-size: 20rpx;
padding: 2rpx 10rpx;
border-radius: 12rpx;
background: rgba(82, 196, 26, 0.15);
color: #389e0d;
border: 1rpx solid rgba(82, 196, 26, 0.5);
font-weight: 600;
align-self: center;
margin-top: 0;
flex-shrink: 0;
}
/* 商品规格 */
.product-spec {
font-size: 22rpx;
color: #333333;
height: auto;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
line-height: 1.3;
margin-bottom: 2rpx;
margin-top: 2rpx;
}
/* 商品元信息 */
.product-meta {
display: flex;
justify-content: space-between;
align-items: center;
margin: 2rpx 0 4rpx;
padding: 0;
}
.product-price {
font-size: 28rpx;
color: #ff4d4f;
font-weight: bold;
}
.product-location {
font-size: 20rpx;
color: #666;
}
/* 商品描述 */
.product-description {
font-size: 22rpx;
color: #666;
line-height: 1.4;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
margin: 6rpx 0;
background: #fafafa;
padding: 6rpx;
border-radius: 8rpx;
border: 1rpx solid #f0f0f0;
width: 100%;
box-sizing: border-box;
min-height: 50rpx;
display: flex;
align-items: center;
}
/* 创建人信息 */
.creator-info {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 8rpx;
padding-top: 12rpx;
border-top: 1rpx solid #f0f0f0;
font-size: 20rpx;
}
/* 加载更多样式 */
.loading-more {
display: flex;
justify-content: center;
align-items: center;
padding: 40rpx 0;
width: 100%;
}
.loading-spinner {
width: 32rpx;
height: 32rpx;
border: 3rpx solid #e5e5e5;
border-top-color: #1677ff;
border-radius: 50%;
animation: spin 0.8s linear infinite;
margin-right: 12rpx;
}
.loading-text {
font-size: 26rpx;
color: #999;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
/* 无更多数据 */
.no-more-data {
display: flex;
align-items: center;
justify-content: center;
padding: 40rpx 0;
color: #999;
font-size: 26rpx;
gap: 20rpx;
}
.no-more-data::before,
.no-more-data::after {
content: '';
flex: 1;
height: 1rpx;
background: #e5e5e5;
max-width: 80rpx;
}
/* 空状态样式 */
.empty-container {
text-align: center;
padding: 100rpx 0;
color: #999;
font-size: 28rpx;
}
/* 骨架屏样式 */
.skeleton-container {
padding: 20rpx 0;
}
.skeleton-grid {
display: flex;
flex-wrap: wrap;
gap: 20rpx;
width: 100%;
padding: 0 10rpx;
box-sizing: border-box;
}
.skeleton-grid-item {
width: calc((100% - 20rpx) / 2);
background: #fff;
border-radius: 16rpx;
overflow: hidden;
padding: 16rpx;
box-sizing: border-box;
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.08);
}
.skeleton-image {
width: 100%;
height: 200rpx;
background: linear-gradient(90deg, #f0f0f0 25%, #e8e8e8 50%, #f0f0f0 75%);
background-size: 200% 100%;
animation: skeleton-loading 1.5s infinite;
border-radius: 12rpx;
margin-bottom: 16rpx;
}
.skeleton-title {
height: 32rpx;
margin-bottom: 12rpx;
background: linear-gradient(90deg, #f0f0f0 25%, #e8e8e8 50%, #f0f0f0 75%);
background-size: 200% 100%;
animation: skeleton-loading 1.5s infinite;
border-radius: 8rpx;
}
.skeleton-title.short {
width: 60%;
height: 24rpx;
margin-bottom: 16rpx;
}
.skeleton-footer {
display: flex;
justify-content: space-between;
}
.skeleton-price {
width: 80rpx;
height: 28rpx;
background: linear-gradient(90deg, #f0f0f0 25%, #e8e8e8 50%, #f0f0f0 75%);
background-size: 200% 100%;
animation: skeleton-loading 1.5s infinite;
border-radius: 8rpx;
}
@keyframes skeleton-loading {
0% {
background-position: 200% 0;
}
100% {
background-position: -200% 0;
}
}

16
pages/index/index.js

@ -1408,6 +1408,16 @@ Page({
}) })
}, },
// 跳转到货源管理页面
navigateToGoods: function() {
this.setData({
sidebarBtnHidden: true
});
wx.navigateTo({
url: '/pages/goods/index'
})
},
// 预览图片 // 预览图片
previewImage: function(e) { previewImage: function(e) {
// 阻止事件冒泡,避免触发商品点击事件 // 阻止事件冒泡,避免触发商品点击事件
@ -2537,6 +2547,12 @@ Page({
users[userId] = {} users[userId] = {}
} }
// 如果当前类型是 Colleague,直接返回,不允许从服务器同步覆盖
if (users[userId].type === 'Colleague') {
console.log('当前用户是 Colleague 类型,不允许从服务器同步覆盖用户类型')
return
}
// 移除serverType中的customer(如果存在) // 移除serverType中的customer(如果存在)
let processedServerType = serverType.replace(/,?customer/g, '').replace(/^,|,$/g, '') let processedServerType = serverType.replace(/,?customer/g, '').replace(/^,|,$/g, '')

4
pages/index/index.wxml

@ -100,6 +100,10 @@
<text class="sidebar-item-icon">📢</text> <text class="sidebar-item-icon">📢</text>
<text class="sidebar-item-text">招商合作</text> <text class="sidebar-item-text">招商合作</text>
</view> </view>
<view class="sidebar-item" bindtap="navigateToGoods">
<text class="sidebar-item-icon">🥚</text>
<text class="sidebar-item-text">货源管理</text>
</view>
</view> </view>
</view> </view>

6
pages/message-list/index.js

@ -219,6 +219,12 @@ Page({
users[userId] = {} users[userId] = {}
} }
// 如果当前类型是 Colleague,直接返回,不允许从服务器同步覆盖
if (users[userId].type === 'Colleague') {
console.log('当前用户是 Colleague 类型,不允许从服务器同步覆盖用户类型')
return
}
// 移除serverType中的customer(如果存在) // 移除serverType中的customer(如果存在)
let processedServerType = serverType.replace(/,?customer/g, '').replace(/^,|,$/g, '') let processedServerType = serverType.replace(/,?customer/g, '').replace(/^,|,$/g, '')

6
pages/profile/index.js

@ -219,6 +219,12 @@ Page({
users[userId] = {} users[userId] = {}
} }
// 如果当前类型是 Colleague,直接返回,不允许从服务器同步覆盖
if (users[userId].type === 'Colleague') {
console.log('当前用户是 Colleague 类型,不允许从服务器同步覆盖用户类型')
return
}
// 移除serverType中的customer(如果存在) // 移除serverType中的customer(如果存在)
let processedServerType = serverType.replace(/,?customer/g, '').replace(/^,|,$/g, '') let processedServerType = serverType.replace(/,?customer/g, '').replace(/^,|,$/g, '')

17
pages/seller/index.js

@ -2560,7 +2560,7 @@ Page({
finishSetUserType(type) { finishSetUserType(type) {
const userId = wx.getStorageSync('userId') const userId = wx.getStorageSync('userId')
// 更新用户类型 // 获取当前用户类型
let users = wx.getStorageSync('users') let users = wx.getStorageSync('users')
if (typeof users !== 'object' || users === null) { if (typeof users !== 'object' || users === null) {
users = {} users = {}
@ -2568,6 +2568,21 @@ Page({
if (!users[userId]) { if (!users[userId]) {
users[userId] = {} users[userId] = {}
} }
// 如果当前类型是 Colleague,直接跳转到对应页面,不修改用户类型
if (users[userId].type === 'Colleague') {
console.log('当前用户是 Colleague 类型,不允许修改用户类型,直接跳转')
setTimeout(() => {
if (type === 'buyer') {
wx.switchTab({ url: '/pages/buyer/index' })
} else {
wx.switchTab({ url: '/pages/seller/index' })
}
}, 1000)
return
}
// 更新用户类型
users[userId].type = type users[userId].type = type
wx.setStorageSync('users', users) wx.setStorageSync('users', users)

84
server-example/server-mysql.js

@ -763,6 +763,11 @@ Product.init({
type: DataTypes.STRING(10), type: DataTypes.STRING(10),
allowNull: false allowNull: false
}, },
costprice: {
type: DataTypes.STRING(10),
allowNull: true,
comment: '采购价'
},
quantity: { quantity: {
type: DataTypes.INTEGER, type: DataTypes.INTEGER,
allowNull: false allowNull: false
@ -839,6 +844,11 @@ Product.init({
type: DataTypes.STRING(50), type: DataTypes.STRING(50),
allowNull: true, allowNull: true,
comment: '商品种类' comment: '商品种类'
},
description: {
type: DataTypes.TEXT,
allowNull: true,
comment: '货源描述'
} }
}, { }, {
sequelize, sequelize,
@ -1969,18 +1979,35 @@ app.post('/api/product/list', async (req, res) => {
{ {
model: User, model: User,
as: 'seller', as: 'seller',
attributes: ['userId', 'name', 'avatarUrl'] attributes: [
'userId',
['nickName', 'sellerNickName'], // 使用别名避免字段冲突
'avatarUrl'
]
} }
], ],
attributes: { attributes: [
include: [ 'id',
'region', // 【新增】确保返回地区字段 'productId',
'sourceType', // 【新增】确保返回货源类型字段 'sellerId',
'supplyStatus', // 【新增】确保返回供应状态字段 'productName',
'category', // 【新增】确保返回商品种类字段 'price',
'producting' // 【新增】确保返回产品包装字段 'costprice', // 【新增】确保返回采购价字段
] 'quantity',
}, 'grossWeight',
'yolk',
'specification',
'created_at',
'updated_at',
'imageUrls',
'status',
'region',
'sourceType',
'supplyStatus',
'category',
'producting',
'description'
],
order: [['created_at', 'DESC']], order: [['created_at', 'DESC']],
limit: pageSize, limit: pageSize,
offset offset
@ -1996,18 +2023,45 @@ app.post('/api/product/list', async (req, res) => {
const processedProducts = await Promise.all(products.map(async product => { const processedProducts = await Promise.all(products.map(async product => {
const productJSON = product.toJSON(); const productJSON = product.toJSON();
// 确保created_at字段存在并转换为ISO字符串格式 // 确保created_at字段存在并转换为正确的北京时间字符串格式
if (!productJSON.created_at) { if (!productJSON.created_at) {
console.log('商品缺少created_at字段,使用默认值'); console.log('商品缺少created_at字段,使用默认值');
productJSON.created_at = getBeijingTimeISOString(); productJSON.created_at = getBeijingTimeISOString();
} else { } else {
// 确保created_at是字符串格式 // 确保created_at是字符串格式,且不进行时区转换,直接使用数据库中的北京时间
if (productJSON.created_at instanceof Date) { if (productJSON.created_at instanceof Date) {
productJSON.created_at = new Date(productJSON.created_at.getTime() + 8 * 60 * 60 * 1000).toISOString(); // 直接使用Date对象的字符串表示,不转换为ISO格式
} else if (typeof productJSON.created_at !== 'string') { productJSON.created_at = productJSON.created_at.toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai' });
productJSON.created_at = new Date(new Date(productJSON.created_at).getTime() + 8 * 60 * 60 * 1000).toISOString(); } else if (typeof productJSON.created_at === 'string') {
// 数据库已经存储的是北京时间字符串,直接使用
productJSON.created_at = productJSON.created_at;
} else {
// 其他类型,转换为Date对象后再转换为北京时间字符串
productJSON.created_at = new Date(productJSON.created_at).toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai' });
} }
} }
// 确保seller对象的nickName字段正确返回
if (productJSON.seller) {
// 确保seller对象结构正确,处理别名映射 - 只使用nickName,不使用name字段
// 保留sellerNickName字段,确保前端能正确访问
productJSON.seller = {
...productJSON.seller,
// 处理别名,只确保nickName字段存在 - 只使用nickName,不使用name字段
nickName: productJSON.seller.sellerNickName || productJSON.seller.nickName || '未知',
// 移除name字段,只使用nickName
name: undefined,
// 保留sellerNickName字段,确保前端能正确访问
// sellerNickName: undefined,
sellerName: undefined
};
} else {
// 如果没有seller对象,创建默认对象 - 只包含nickName
productJSON.seller = {
nickName: '未知',
name: undefined
};
}
// 详细分析毛重字段 // 详细分析毛重字段
const grossWeightDetails = { const grossWeightDetails = {

89
utils/api.js

@ -1649,7 +1649,10 @@ module.exports = {
if (!users[userId]) { if (!users[userId]) {
users[userId] = {}; users[userId] = {};
} }
users[userId].type = userType; // 如果当前类型已经是 Colleague,保留不变
if (users[userId].type !== 'Colleague') {
users[userId].type = userType;
}
if (managerId) { if (managerId) {
users[userId].managerId = managerId; users[userId].managerId = managerId;
} else { } else {
@ -1826,7 +1829,10 @@ module.exports = {
if (!users[userId]) { if (!users[userId]) {
users[userId] = {}; users[userId] = {};
} }
users[userId].type = userType; // 直接存储用户类型 // 如果当前类型已经是 Colleague,保留不变
if (users[userId].type !== 'Colleague') {
users[userId].type = userType; // 直接存储用户类型
}
wx.setStorageSync('users', users); wx.setStorageSync('users', users);
} }
@ -2209,7 +2215,10 @@ module.exports = {
if (!users[userId]) { if (!users[userId]) {
users[userId] = {}; users[userId] = {};
} }
users[userId].type = userType; // 如果当前类型已经是 Colleague,保留不变
if (users[userId].type !== 'Colleague') {
users[userId].type = userType;
}
wx.setStorageSync('users', users); wx.setStorageSync('users', users);
} }
@ -2417,7 +2426,10 @@ module.exports = {
if (!users[userId]) { if (!users[userId]) {
users[userId] = {}; users[userId] = {};
} }
users[userId].type = userType.replace('customer', ''); // 如果当前类型已经是 Colleague,保留不变
if (users[userId].type !== 'Colleague') {
users[userId].type = userType.replace('customer', '');
}
wx.setStorageSync('users', users); wx.setStorageSync('users', users);
} }
@ -2959,6 +2971,12 @@ module.exports = {
let users = wx.getStorageSync('users') || {}; let users = wx.getStorageSync('users') || {};
let currentType = users[userId] && users[userId].type ? users[userId].type : ''; let currentType = users[userId] && users[userId].type ? users[userId].type : '';
// 如果当前类型是 Colleague,直接返回,不允许修改
if (currentType === 'Colleague') {
console.log('当前用户是 Colleague 类型,不允许修改用户类型');
return;
}
// 计算新的用户类型(支持buyer、seller、both、manager) // 计算新的用户类型(支持buyer、seller、both、manager)
let newType = currentType; let newType = currentType;
@ -3156,6 +3174,69 @@ module.exports = {
}); });
}, },
// 获取货源列表(内部使用)
getGoodsList: function (params) {
console.log('API.getGoodsList - params:', params);
const openid = wx.getStorageSync('openid');
if (!openid) {
return Promise.reject(new Error('用户未登录'));
}
const requestData = {
openid: openid,
page: params.page || 1,
pageSize: params.pageSize || 10,
status: 'published',
viewMode: 'shopping', // 确保能够查看所有内部人员创建的货源
_t: new Date().getTime() // 添加时间戳防止缓存
};
// 如果有搜索关键词,添加到请求参数中
if (params.keyword) {
requestData.keyword = params.keyword;
}
return request('/api/product/list', 'POST', requestData).then(data => {
// 添加字段映射,确保产品名称正确显示
if (data.success && data.products && Array.isArray(data.products)) {
data.products = data.products.map(product => {
// 处理产品名称映射
const name = product.productName || product.name || '未命名商品';
// 处理创建者信息
let seller = product.seller || {};
// 确保seller对象结构正确
if (seller && typeof seller === 'object') {
// 如果seller是对象,只使用nickName,不使用name字段
// 正确处理后端返回的别名sellerNickName和sellerName
seller = {
...seller,
nickName: seller.sellerNickName || seller.nickName || '未知',
// 移除name字段,只使用nickName
name: undefined
};
} else {
// 如果seller不是对象,创建默认对象,只包含nickName
seller = {
nickName: '未知',
name: undefined
};
}
return {
...product,
// 确保name字段存在,优先使用productName,其次使用name
name: name,
// 确保seller对象结构正确
seller: seller,
// 确保costprice字段存在
costprice: product.costprice || ''
};
});
}
return data;
});
},
// 添加BASE_URL属性,方便其他地方使用 // 添加BASE_URL属性,方便其他地方使用
BASE_URL: BASE_URL, BASE_URL: BASE_URL,
// 正确导出withdrawSettlementApplication方法 // 正确导出withdrawSettlementApplication方法

Loading…
Cancel
Save