From d0e155e2524d37c20d58a8b3b2d5afd4fdcee8bc Mon Sep 17 00:00:00 2001 From: Default User Date: Mon, 5 Jan 2026 13:37:43 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E8=B4=A7=E6=BA=90=E7=AE=A1?= =?UTF-8?q?=E7=90=86=E5=8A=9F=E8=83=BD=EF=BC=9A1.=20pageSize=E4=BB=8E100?= =?UTF-8?q?=E8=B0=83=E6=95=B4=E4=B8=BA20=EF=BC=9B2.=20=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=E6=97=A0=E9=99=90=E9=80=92=E5=BD=92=E9=97=AE=E9=A2=98=EF=BC=9B?= =?UTF-8?q?3.=20=E5=A4=84=E7=90=86=E5=B9=B6=E5=8F=91=E8=AF=B7=E6=B1=82?= =?UTF-8?q?=EF=BC=9B4.=20=E6=B7=BB=E5=8A=A0=E9=87=87=E8=B4=AD=E4=BB=B7?= =?UTF-8?q?=E6=98=BE=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app.json | 3 +- pages/buyer/index.js | 24 +- pages/goods/index.js | 342 +++++++++++++++++++++++ pages/goods/index.json | 7 + pages/goods/index.wxml | 133 +++++++++ pages/goods/index.wxss | 492 +++++++++++++++++++++++++++++++++ pages/index/index.js | 10 + pages/index/index.wxml | 4 + pages/message-list/index.js | 6 + pages/profile/index.js | 6 + pages/seller/index.js | 17 +- server-example/server-mysql.js | 90 ++++-- utils/api.js | 66 +++++ 13 files changed, 1170 insertions(+), 30 deletions(-) create mode 100644 pages/goods/index.js create mode 100644 pages/goods/index.json create mode 100644 pages/goods/index.wxml create mode 100644 pages/goods/index.wxss diff --git a/app.json b/app.json index e65e84a..0c9b330 100644 --- a/app.json +++ b/app.json @@ -15,7 +15,8 @@ "pages/chat-detail/index", "pages/message-list/index", "pages/customer-service/index", - "pages/cooperation/index" + "pages/cooperation/index", + "pages/goods/index" ], "subpackages": [ { diff --git a/pages/buyer/index.js b/pages/buyer/index.js index 3b6935b..b07a174 100644 --- a/pages/buyer/index.js +++ b/pages/buyer/index.js @@ -485,18 +485,22 @@ Page({ if (!users[userId]) { users[userId] = {}; } - users[userId].type = 'buyer'; - wx.setStorageSync('users', users); - // 更新标签 - let tags = wx.getStorageSync('tags'); - if (typeof tags !== 'object' || tags === null) { - tags = {}; + // 只有当当前类型不是 Colleague 时才修改为 buyer + if (users[userId].type !== 'Colleague') { + 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); } // ✅ 修改:重置分页状态并清空数据 diff --git a/pages/goods/index.js b/pages/goods/index.js new file mode 100644 index 0000000..0f92c8a --- /dev/null +++ b/pages/goods/index.js @@ -0,0 +1,342 @@ +// 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: 20, + 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() { + if (this.data.isLoading) return + + this.setData({ + isRefreshing: true, + currentPage: 1, + goodsList: [], + hasMore: true, + isLoading: false + }) + 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() { + if (this.data.isLoading) return + + this.setData({ + currentPage: 1, + goodsList: [], + hasMore: true + }) + this.loadGoodsList() + }, + + /** + * 清除搜索 + */ + clearSearch() { + this.setData({ + searchKeyword: '', + currentPage: 1, + goodsList: [], + hasMore: true + }) + this.loadGoodsList() + }, + + /** + * 筛选条件改变 + */ + onFilterChange(e) { + if (this.data.isLoading) return + + 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-05 17:30) + if (typeof dateString === 'string' && /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}(:\d{2})?$/.test(dateString)) { + // 直接返回格式化好的字符串,只保留到分钟 + return dateString.slice(0, 16) + } + + // 检查是否是ISO格式的字符串(如:2026-01-05T12:00:00.000Z) + if (typeof dateString === 'string' && /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?Z$/.test(dateString)) { + // 转换ISO格式为本地时间 + const date = new Date(dateString) + if (isNaN(date.getTime())) { + return '未知时间' + } + + 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}` + } + + // 对于其他格式的字符串,直接返回,不进行转换 + // 这是为了避免对后端返回的北京时间字符串进行错误的时区转换 + return dateString + }, + + /** + * 加载货源列表 + */ + 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和sellerNickName字段,不使用name字段 + // 详细日志,查看seller对象的完整结构和各个字段值 + console.log('seller对象完整结构:', JSON.stringify(item.seller)) + console.log('seller.nickName:', item.seller?.nickName) + console.log('seller.sellerNickName:', item.seller?.sellerNickName) + const creatorName = item.seller?.nickName || item.seller?.sellerNickName || '未知' + 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 + }) + + // 移除递归调用逻辑,避免无限递归风险 + // 用户可以通过正常的上拉加载获取更多数据 + } 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 + }) + } + } +}) \ No newline at end of file diff --git a/pages/goods/index.json b/pages/goods/index.json new file mode 100644 index 0000000..1f1e882 --- /dev/null +++ b/pages/goods/index.json @@ -0,0 +1,7 @@ +{ + "usingComponents": {}, + "navigationBarTitleText": "内部货源管理", + "navigationBarBackgroundColor": "#ffffff", + "navigationBarTextStyle": "black", + "backgroundColor": "#f5f5f5" +} \ No newline at end of file diff --git a/pages/goods/index.wxml b/pages/goods/index.wxml new file mode 100644 index 0000000..122c567 --- /dev/null +++ b/pages/goods/index.wxml @@ -0,0 +1,133 @@ + + + + 内部货源管理 + + + + + + + + + + + + + + + 全部 + + + 小品种 + + + 大贸易 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 预售 + 现货 + + + + {{item.productName}} + 库存:{{item.quantity || item.minOrder || '充足'}} + + {{item.specification || item.spec || '无'}} | {{item.yolk}} + + {{item.costprice ? '¥' + item.costprice : '无采购价'}} + {{item.region || ''}} + + {{item.description || ''}} + + {{item.creatorName || '未知'}} + {{item.formattedCreatedAt || item.created_at || '未知'}} + + + + + + + + + + 加载中... + + + + + 货源正在快马加鞭的赶来 + + + + + 暂无货源数据 + 下拉刷新试试 + + + + + \ No newline at end of file diff --git a/pages/goods/index.wxss b/pages/goods/index.wxss new file mode 100644 index 0000000..bc5d223 --- /dev/null +++ b/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; + } +} \ No newline at end of file diff --git a/pages/index/index.js b/pages/index/index.js index ad64925..9030faf 100644 --- a/pages/index/index.js +++ b/pages/index/index.js @@ -1479,6 +1479,16 @@ Page({ }) }, + // 跳转到货源管理页面 + navigateToGoods: function() { + this.setData({ + sidebarBtnHidden: true + }); + wx.navigateTo({ + url: '/pages/goods/index' + }) + }, + // 预览图片 previewImage: function(e) { // 阻止事件冒泡,避免触发商品点击事件 diff --git a/pages/index/index.wxml b/pages/index/index.wxml index 4fd756c..e3f30f4 100644 --- a/pages/index/index.wxml +++ b/pages/index/index.wxml @@ -100,6 +100,10 @@ 📢 招商合作 + + 📦 + 货源管理 + diff --git a/pages/message-list/index.js b/pages/message-list/index.js index 4214da6..c9fecf8 100644 --- a/pages/message-list/index.js +++ b/pages/message-list/index.js @@ -219,6 +219,12 @@ Page({ users[userId] = {} } + // 如果当前类型是 Colleague,直接返回,不允许从服务器同步覆盖 + if (users[userId].type === 'Colleague') { + console.log('当前用户是 Colleague 类型,不允许从服务器同步覆盖用户类型') + return + } + // 移除serverType中的customer(如果存在) let processedServerType = serverType.replace(/,?customer/g, '').replace(/^,|,$/g, '') diff --git a/pages/profile/index.js b/pages/profile/index.js index 73d8fc9..094bdf6 100644 --- a/pages/profile/index.js +++ b/pages/profile/index.js @@ -219,6 +219,12 @@ Page({ users[userId] = {} } + // 如果当前类型是 Colleague,直接返回,不允许从服务器同步覆盖 + if (users[userId].type === 'Colleague') { + console.log('当前用户是 Colleague 类型,不允许从服务器同步覆盖用户类型') + return + } + // 移除serverType中的customer(如果存在) let processedServerType = serverType.replace(/,?customer/g, '').replace(/^,|,$/g, '') diff --git a/pages/seller/index.js b/pages/seller/index.js index e7d3884..91e8690 100644 --- a/pages/seller/index.js +++ b/pages/seller/index.js @@ -2560,7 +2560,7 @@ Page({ finishSetUserType(type) { const userId = wx.getStorageSync('userId') - // 更新用户类型 + // 获取当前用户类型 let users = wx.getStorageSync('users') if (typeof users !== 'object' || users === null) { users = {} @@ -2568,6 +2568,21 @@ Page({ if (!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 wx.setStorageSync('users', users) diff --git a/server-example/server-mysql.js b/server-example/server-mysql.js index 5f7274b..fcd2091 100644 --- a/server-example/server-mysql.js +++ b/server-example/server-mysql.js @@ -839,6 +839,11 @@ Product.init({ type: DataTypes.STRING(50), allowNull: true, comment: '商品种类' + }, + description: { + type: DataTypes.TEXT, + allowNull: true, + comment: '货源描述' } }, { sequelize, @@ -1966,21 +1971,34 @@ app.post('/api/product/list', async (req, res) => { const { count, rows: products } = await Product.findAndCountAll({ where, include: [ - { - model: User, - as: 'seller', - attributes: ['userId', 'name', 'avatarUrl'] - } + { + model: User, + as: 'seller', + attributes: ['userId', 'name', 'nickName', 'avatarUrl'] + } + ], + attributes: [ + 'id', + 'productId', + 'sellerId', + 'productName', + 'price', + 'costprice', + 'quantity', + 'grossWeight', + 'yolk', + 'specification', + 'created_at', + 'updated_at', + 'imageUrls', + 'status', + 'region', + 'sourceType', + 'supplyStatus', + 'category', + 'producting', + 'description' ], - attributes: { - include: [ - 'region', // 【新增】确保返回地区字段 - 'sourceType', // 【新增】确保返回货源类型字段 - 'supplyStatus', // 【新增】确保返回供应状态字段 - 'category', // 【新增】确保返回商品种类字段 - 'producting' // 【新增】确保返回产品包装字段 - ] - }, order: [['created_at', 'DESC']], limit: pageSize, offset @@ -1996,16 +2014,16 @@ app.post('/api/product/list', async (req, res) => { const processedProducts = await Promise.all(products.map(async product => { const productJSON = product.toJSON(); - // 确保created_at字段存在并转换为ISO字符串格式 + // 确保created_at字段存在并转换为正确格式 if (!productJSON.created_at) { console.log('商品缺少created_at字段,使用默认值'); productJSON.created_at = getBeijingTimeISOString(); } else { - // 确保created_at是字符串格式 + // 确保created_at是字符串格式,不进行多余的时区转换 if (productJSON.created_at instanceof Date) { - productJSON.created_at = new Date(productJSON.created_at.getTime() + 8 * 60 * 60 * 1000).toISOString(); + productJSON.created_at = productJSON.created_at.toISOString(); } else if (typeof productJSON.created_at !== 'string') { - productJSON.created_at = new Date(new Date(productJSON.created_at).getTime() + 8 * 60 * 60 * 1000).toISOString(); + productJSON.created_at = new Date(productJSON.created_at).toISOString(); } } @@ -2072,12 +2090,48 @@ app.post('/api/product/list', async (req, res) => { productJSON.imageUrls = []; } + // 确保seller对象的nickName字段正确返回 + if (productJSON.seller) { + // 确保seller对象结构正确,处理nickName字段 + console.log('seller对象各字段值:'); + console.log('- seller.userId:', productJSON.seller.userId); + console.log('- seller.nickName:', productJSON.seller.nickName); + console.log('- seller.name:', productJSON.seller.name); + + // 按照用户要求,只使用users表里的nickName + const nickName = productJSON.seller.nickName || productJSON.seller.name || '未知'; + console.log('最终确定的nickName:', nickName); + + productJSON.seller = { + ...productJSON.seller, + // 只使用nickName字段 + nickName: nickName, + sellerNickName: nickName, // 确保sellerNickName字段存在 + sellerName: nickName + }; + } else { + // 如果没有seller对象,创建默认对象 + productJSON.seller = { + nickName: '未知', + sellerNickName: '未知', + sellerName: '未知' + }; + } + + // 确保created_at字段存在 + console.log('productJSON.created_at:', productJSON.created_at); + if (!productJSON.created_at) { + console.warn('商品缺少created_at字段,使用默认值'); + productJSON.created_at = getBeijingTimeISOString(); + } + // 记录第一个商品的转换信息用于调试 if (products.indexOf(product) === 0) { console.log('商品列表 - 第一个商品毛重字段处理:'); console.log('- 原始值:', grossWeightDetails.value, '类型:', grossWeightDetails.type); console.log('- 转换后的值:', productJSON.grossWeight, '类型:', typeof productJSON.grossWeight); console.log('- reservedCount值:', productJSON.reservedCount, '类型:', typeof productJSON.reservedCount); + console.log('- seller信息:', JSON.stringify(productJSON.seller)); } return productJSON; diff --git a/utils/api.js b/utils/api.js index 7ae9ff7..a2243d5 100644 --- a/utils/api.js +++ b/utils/api.js @@ -1017,6 +1017,72 @@ module.exports = { return { success: false, message: '聊天记录修复失败:' + err.message }; }); }, + + // 获取商品列表方法,用于goods页面 + 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') { + // 确保sellerNickName字段存在,同时支持nickName字段作为备选 + const sellerNickName = seller.sellerNickName || seller.nickName || '未知'; + + // 确保seller对象包含所有必要字段,特别是nickName字段 + seller = { + ...seller, + nickName: sellerNickName, // 确保nickName字段存在,与sellerNickName保持一致 + sellerNickName: sellerNickName, // 确保sellerNickName字段存在 + name: seller.name || sellerNickName // 不再移除name字段,保留原始信息 + }; + } else { + // 如果seller不是对象,创建默认对象,只包含nickName相关字段 + seller = { + nickName: '未知', + sellerNickName: '未知', + name: '未知' + }; + } + + return { + ...product, + // 确保name字段存在,优先使用productName,其次使用name + name: name, + // 确保seller对象结构正确 + seller: seller, + // 确保costprice字段存在,使用三元运算符处理,保留0值 + costprice: product.costprice !== undefined && product.costprice !== null ? product.costprice : '' + }; + }); + } + return data; + }); + }, // 上传带图片的商品 - 改进版,确保所有图片都被实际上传到服务器 uploadProductWithImages: function (productData, imageUrls) {