Browse Source

过滤掉type为Colleague的用户在内部信息块中的显示

Xfy
Default User 1 week ago
parent
commit
cd135141db
  1. 120
      pages/goods-detail/goods-detail.js
  2. 39
      pages/goods-detail/goods-detail.wxml
  3. 254
      pages/goods-detail/goods-detail.wxss
  4. 116
      server-example/server-mysql.js

120
pages/goods-detail/goods-detail.js

@ -338,6 +338,28 @@ function formatDateTime(dateString) {
return dateString;
}
// 格式化北京时间的函数
function formatBeijingTime(dateString) {
if (!dateString) return '未知时间';
// 尝试解析日期字符串
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}`;
}
// 处理净重、件数、规格和价格数据,将逗号分隔的字符串转换为一一对应的数组
function processWeightAndQuantityData(weightSpecString, quantityString, specString, priceString, specStatusString, specPriceChanges = {}) {
console.log('===== 处理净重、件数、规格和价格数据 =====');
@ -1358,7 +1380,12 @@ Page({
// 当前用户信息
currentUserPhone: '', // 当前用户的手机号,用于判断评论是否属于当前用户
// 导航锁状态
navigating: false // 是否正在导航中,防止多次点击导致多次跳转
navigating: false, // 是否正在导航中,防止多次点击导致多次跳转
// 内部信息相关数据
isInternalUser: false, // 是否为内部人员
viewerPhone: '', // 浏览人电话号码
followUpRecords: [], // 跟进记录
viewerInfo: [] // 浏览用户信息列表
},
// 导航锁机制,防止多次点击导致多次跳转
@ -1449,11 +1476,69 @@ Page({
currentUserPhone = wx.getStorageSync('phoneNumber');
}
// 设置当前用户的电话号码
this.setData({
currentUserPhone: currentUserPhone
});
console.log('当前用户手机号已设置:', currentUserPhone);
// 4. 检查是否为内部人员(与 index 页面逻辑一致)
if (currentUserPhone) {
const appInstance = getApp();
appInstance.getPersonnelInfo(currentUserPhone).then(res => {
const isInternalUser = res.success && res.data && res.data.length > 0;
// 5. 模拟浏览人电话号码和跟进记录(实际项目中应该从API获取)
const viewerPhone = '13800138000';
const followUpRecords = [
{ time: '2024-01-15 10:30', content: '客户询问商品价格和库存情况' },
{ time: '2024-01-14 14:20', content: '客户表示对商品感兴趣,需要进一步了解' },
{ time: '2024-01-13 09:15', content: '首次联系客户,介绍商品信息' }
];
// 设置当前用户的电话号码和内部人员状态
this.setData({
currentUserPhone: currentUserPhone,
isInternalUser: isInternalUser,
viewerPhone: viewerPhone,
followUpRecords: followUpRecords
});
console.log('当前用户手机号已设置:', currentUserPhone);
console.log('是否为内部人员:', isInternalUser);
}).catch(err => {
console.error('检查personnel表失败:', err);
// 5. 模拟浏览人电话号码和跟进记录(实际项目中应该从API获取)
const viewerPhone = '13800138000';
const followUpRecords = [
{ time: '2024-01-15 10:30', content: '客户询问商品价格和库存情况' },
{ time: '2024-01-14 14:20', content: '客户表示对商品感兴趣,需要进一步了解' },
{ time: '2024-01-13 09:15', content: '首次联系客户,介绍商品信息' }
];
// 设置当前用户的电话号码和内部人员状态
this.setData({
currentUserPhone: currentUserPhone,
isInternalUser: false,
viewerPhone: viewerPhone,
followUpRecords: followUpRecords
});
console.log('当前用户手机号已设置:', currentUserPhone);
console.log('是否为内部人员:', false);
});
} else {
// 5. 模拟浏览人电话号码和跟进记录(实际项目中应该从API获取)
const viewerPhone = '13800138000';
const followUpRecords = [
{ time: '2024-01-15 10:30', content: '客户询问商品价格和库存情况' },
{ time: '2024-01-14 14:20', content: '客户表示对商品感兴趣,需要进一步了解' },
{ time: '2024-01-13 09:15', content: '首次联系客户,介绍商品信息' }
];
// 设置当前用户的电话号码和内部人员状态
this.setData({
currentUserPhone: currentUserPhone,
isInternalUser: false,
viewerPhone: viewerPhone,
followUpRecords: followUpRecords
});
console.log('当前用户手机号已设置:', currentUserPhone);
console.log('是否为内部人员:', false);
}
// 只有在用户未登录且URL参数明确要求登录时才显示登录弹窗
if (!isLoggedIn && needLoginFromUrl) {
@ -2155,11 +2240,30 @@ Page({
displayRegion = extractProvince(displayRegion);
}
this.setData({
// 提取浏览用户信息和跟进记录
const viewerInfo = product.viewerInfo || [];
// 处理浏览用户信息,确保格式正确,并过滤掉type为Colleague的用户
const processedViewerInfo = viewerInfo
.filter(viewer => viewer.type !== 'Colleague') // 过滤掉Colleague类型的用户
.map(viewer => ({
userId: viewer.userId,
phoneNumber: viewer.phoneNumber,
nickName: viewer.nickName,
type: viewer.type,
followupRecords: (viewer.followupRecords || []).map(record => ({
...record,
// 确保时间显示为北京时间
createdAt: record.createdAt ? formatBeijingTime(record.createdAt) : '未知时间'
}))
}));
this.setData({
goodsDetail: formattedGoods,
displayRegion: displayRegion,
isFavorite: preloadedFavoriteStatus, // 优先使用预加载数据中的收藏状态
videoCoverUrl: null // 重置封面URL
videoCoverUrl: null, // 重置封面URL
viewerInfo: processedViewerInfo // 设置浏览用户信息
});
// 商品加载完成后检查并提取视频封面

39
pages/goods-detail/goods-detail.wxml

@ -194,6 +194,45 @@
</view>
</view>
</view>
<!-- 内部人员查看 - 浏览人信息和跟进记录 -->
<view class="internal-info" wx:if="{{isInternalUser && viewerInfo && viewerInfo.length > 0}}">
<view class="internal-header">
<text class="internal-title">内部信息</text>
</view>
<view class="internal-content">
<block wx:for="{{viewerInfo}}" wx:key="userId">
<!-- 浏览人信息 -->
<view class="viewer-item">
<view class="viewer-header">
<text class="viewer-name">{{item.nickName || '未知用户'}}</text>
<text class="viewer-phone">{{item.phoneNumber || '暂无电话'}}</text>
</view>
<!-- 跟进记录 -->
<view class="follow-up-records">
<block wx:if="{{item.followupRecords && item.followupRecords.length > 0}}">
<view wx:for="{{item.followupRecords}}" wx:key="index" class="follow-up-item">
<view class="follow-up-time">{{item.createdAt || '未知时间'}}</view>
<view class="follow-up-content">{{item.content || '无内容'}}</view>
</view>
</block>
<view wx:else class="no-records">暂无跟进记录</view>
</view>
</view>
</block>
</view>
</view>
<!-- 内部人员查看 - 无浏览记录 -->
<view class="internal-info" wx:if="{{isInternalUser && (!viewerInfo || viewerInfo.length === 0)}}">
<view class="internal-header">
<text class="internal-title">内部信息</text>
</view>
<view class="internal-content">
<view class="no-records">暂无浏览记录</view>
</view>
</view>
<!-- 空白页 -->
<view class="blank-space"></view>
</view>

254
pages/goods-detail/goods-detail.wxss

@ -872,6 +872,133 @@ video.slider-media .wx-video-volume-icon {
}
}
/* 内部信息样式 */
.internal-info {
margin: 8px 16px;
padding: 16px;
border-radius: 10px;
background: #f0f5ff;
border: 1px solid #d6e4ff;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
animation: fadeIn 0.4s ease-out;
animation-delay: 0.3s;
}
.internal-header {
margin-bottom: 12px;
padding-bottom: 8px;
border-bottom: 1px solid #d6e4ff;
}
.internal-title {
font-size: 16px;
font-weight: 600;
color: #1890ff;
display: flex;
align-items: center;
}
.internal-title::before {
content: '🔒';
margin-right: 8px;
font-size: 14px;
}
.internal-content {
display: flex;
flex-direction: column;
gap: 12px;
}
.internal-item {
display: flex;
flex-direction: column;
gap: 6px;
}
.internal-label {
font-size: 13px;
color: #595959;
font-weight: 500;
}
.internal-value {
font-size: 14px;
color: #262626;
font-weight: 600;
}
/* 跟进记录样式 */
.follow-up-records {
background: #ffffff;
border-radius: 6px;
padding: 12px;
border: 1px solid #e6f7ff;
}
.follow-up-item {
margin-bottom: 10px;
padding-bottom: 10px;
border-bottom: 1px solid #f0f0f0;
}
.follow-up-item:last-child {
margin-bottom: 0;
padding-bottom: 0;
border-bottom: none;
}
.follow-up-time {
font-size: 12px;
color: #8c8c8c;
margin-bottom: 4px;
}
.follow-up-content {
font-size: 13px;
color: #595959;
line-height: 1.4;
}
.no-records {
font-size: 13px;
color: #8c8c8c;
text-align: center;
padding: 20px 0;
}
/* 浏览人信息样式 */
.viewer-item {
margin-bottom: 16px;
padding-bottom: 16px;
border-bottom: 1px solid #e6f7ff;
}
.viewer-item:last-child {
margin-bottom: 0;
padding-bottom: 0;
border-bottom: none;
}
.viewer-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
}
.viewer-name {
font-size: 14px;
font-weight: 600;
color: #262626;
}
.viewer-phone {
font-size: 14px;
color: #1890ff;
font-weight: 500;
}
.compare-modal-header {
display: flex;
justify-content: space-between;
@ -2047,4 +2174,131 @@ video.slider-media .wx-video-volume-icon {
to {
transform: translateY(0);
}
}
/* 内部信息样式 */
.internal-info {
margin: 8px 16px;
padding: 16px;
border-radius: 10px;
background: #f0f5ff;
border: 1px solid #d6e4ff;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
animation: fadeIn 0.4s ease-out;
animation-delay: 0.3s;
}
.internal-header {
margin-bottom: 12px;
padding-bottom: 8px;
border-bottom: 1px solid #d6e4ff;
}
.internal-title {
font-size: 16px;
font-weight: 600;
color: #1890ff;
display: flex;
align-items: center;
}
.internal-title::before {
content: '🔒';
margin-right: 8px;
font-size: 14px;
}
.internal-content {
display: flex;
flex-direction: column;
gap: 12px;
}
.internal-item {
display: flex;
flex-direction: column;
gap: 6px;
}
.internal-label {
font-size: 13px;
color: #595959;
font-weight: 500;
}
.internal-value {
font-size: 14px;
color: #262626;
font-weight: 600;
}
/* 跟进记录样式 */
.follow-up-records {
background: #ffffff;
border-radius: 6px;
padding: 12px;
border: 1px solid #e6f7ff;
}
.follow-up-item {
margin-bottom: 10px;
padding-bottom: 10px;
border-bottom: 1px solid #f0f0f0;
}
.follow-up-item:last-child {
margin-bottom: 0;
padding-bottom: 0;
border-bottom: none;
}
.follow-up-time {
font-size: 12px;
color: #8c8c8c;
margin-bottom: 4px;
}
.follow-up-content {
font-size: 13px;
color: #595959;
line-height: 1.4;
}
.no-records {
font-size: 13px;
color: #8c8c8c;
text-align: center;
padding: 20px 0;
}
/* 浏览人信息样式 */
.viewer-item {
margin-bottom: 16px;
padding-bottom: 16px;
border-bottom: 1px solid #e6f7ff;
}
.viewer-item:last-child {
margin-bottom: 0;
padding-bottom: 0;
border-bottom: none;
}
.viewer-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
}
.viewer-name {
font-size: 14px;
font-weight: 600;
color: #262626;
}
.viewer-phone {
font-size: 14px;
color: #1890ff;
font-weight: 500;
}

116
server-example/server-mysql.js

@ -2568,6 +2568,69 @@ EggbarLike.init({
timestamps: false
});
// 定义用户浏览商品记录模型
class UserProductView extends Model { }
UserProductView.init({
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true
},
user_id: {
type: DataTypes.STRING(50),
allowNull: false,
comment: '用户ID'
},
product_id: {
type: DataTypes.STRING(100),
allowNull: false,
comment: '产品ID'
},
created_at: {
type: DataTypes.DATE,
defaultValue: Sequelize.NOW
}
}, {
sequelize,
modelName: 'UserProductView',
tableName: 'user_product_views',
timestamps: false
});
// 定义客户历史跟进记录模型
class UserFollowupHistory extends Model { }
UserFollowupHistory.init({
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true
},
user_id: {
type: DataTypes.STRING(100),
allowNull: false,
comment: '关联的用户ID,对应users表的userId'
},
followup_content: {
type: DataTypes.TEXT,
allowNull: true,
comment: '跟进内容'
},
phone_number: {
type: DataTypes.STRING(20),
allowNull: false,
comment: '客户电话号码'
},
created_at: {
type: DataTypes.DATE,
defaultValue: Sequelize.NOW
}
}, {
sequelize,
modelName: 'UserFollowupHistory',
tableName: 'user_followup_history',
timestamps: false
});
// 定义模型之间的关联关系
// 用户和商品的一对多关系 (卖家发布商品)
@ -5922,6 +5985,55 @@ app.post('/api/products/detail', async (req, res) => {
}
});
// 查询浏览该商品的用户记录
const userViews = await UserProductView.findAll({
where: {
product_id: productId
},
attributes: ['user_id', 'created_at']
});
// 提取浏览用户的user_id列表
const userIds = userViews.map(view => view.user_id);
// 查询这些用户的电话号码和跟进记录
let viewerInfo = [];
if (userIds.length > 0) {
// 查询用户信息,包括followup和type字段
const users = await User.findAll({
where: {
userId: {
[Sequelize.Op.in]: userIds
}
},
attributes: ['userId', 'phoneNumber', 'nickName', 'followup', 'type']
});
// 构建浏览用户信息
userIds.forEach(userId => {
const user = users.find(u => u.userId === userId);
if (user) {
// 构建跟进记录,使用users表中的followup字段
const followupRecords = [];
if (user.followup) {
followupRecords.push({
content: user.followup,
phoneNumber: user.phoneNumber,
createdAt: new Date() // 使用当前时间
});
}
viewerInfo.push({
userId: userId,
phoneNumber: user.phoneNumber,
nickName: user.nickName,
type: user.type,
followupRecords: followupRecords
});
}
});
}
// 对返回的商品数据进行处理
let updatedProduct = { ...product.toJSON() };
@ -5997,8 +6109,12 @@ app.post('/api/products/detail', async (req, res) => {
// 设置收藏人数 - 从favorites表统计得到
updatedProduct.reservedCount = favoriteCount;
// 添加浏览用户信息
updatedProduct.viewerInfo = viewerInfo;
console.log('商品详情 - 最终返回的毛重值:', updatedProduct.grossWeight, '类型:', typeof updatedProduct.grossWeight);
console.log('商品详情 - 返回的收藏人数:', updatedProduct.reservedCount, '类型:', typeof updatedProduct.reservedCount);
console.log('商品详情 - 浏览用户数量:', viewerInfo.length);
console.log('商品详情 - producting字段:', updatedProduct.producting, '类型:', typeof updatedProduct.producting);
console.log('商品详情 - description字段:', updatedProduct.description, '类型:', typeof updatedProduct.description);

Loading…
Cancel
Save