消息模块完善
This commit is contained in:
@@ -4,7 +4,8 @@ return [
|
||||
'Id' => '主键ID',
|
||||
'Message_type' => '消息类型',
|
||||
'Message_type 0' => '系统消息',
|
||||
'Message_type 1' => '聊天消息',
|
||||
'Message_type 1' => '公告消息',
|
||||
'Message_type 2' => '需求消息',
|
||||
'From_user_id' => '发消息用户ID',
|
||||
'To_user_id' => '收消息用户ID',
|
||||
'Title' => '消息标题',
|
||||
|
||||
@@ -19,12 +19,32 @@ class Message extends Api
|
||||
$user_id = $this->auth->id ?? -1;
|
||||
$page = $this->request->get('page', 1);
|
||||
$page_size = $this->request->get('pageSize', 10);
|
||||
//消息类型筛选:0=系统消息,1=公告消息,2=需求消息,不传=全部
|
||||
$message_type = $this->request->get('message_type', '');
|
||||
$where = [
|
||||
'to_user_id' => $user_id
|
||||
];
|
||||
if ($message_type !== '' && $message_type !== null) {
|
||||
$where['message_type'] = $message_type;
|
||||
}
|
||||
//总未读数(兼容旧字段)+ 分类未读数(前端tab角标用)
|
||||
$unreadCount = MessageService::where($where + ['is_read' => CommonService::STATUS_OFF])->count();
|
||||
$unreadList = MessageService::where(['to_user_id' => $user_id, 'is_read' => CommonService::STATUS_OFF])
|
||||
->field('message_type, count(*) as total')
|
||||
->group('message_type')
|
||||
->select()->toArray();
|
||||
$unreadMap = [
|
||||
MessageService::TYPE_SYSTEM => 0,
|
||||
MessageService::TYPE_NOTICE => 0,
|
||||
MessageService::TYPE_DEMAND => 0,
|
||||
];
|
||||
foreach ($unreadList as $item) {
|
||||
$unreadMap[$item['message_type']] = (int)$item['total'];
|
||||
}
|
||||
|
||||
$list = MessageService::getList($where, $page_size, 'id desc')->toArray();
|
||||
$list['unread_count'] = $unreadCount;
|
||||
$list['unread_map'] = $unreadMap;
|
||||
|
||||
$this->success('', $list);
|
||||
} catch (\Exception $e) {
|
||||
@@ -36,6 +56,27 @@ class Message extends Api
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 未读消息数(供底部tabbar红点等轻量场景)
|
||||
* @return void
|
||||
*/
|
||||
public function unreadCount()
|
||||
{
|
||||
try {
|
||||
$user_id = $this->auth->id ?? -1;
|
||||
$count = MessageService::where([
|
||||
'to_user_id' => $user_id,
|
||||
'is_read' => CommonService::STATUS_OFF
|
||||
])->count();
|
||||
$this->success('', ['unread_count' => (int)$count]);
|
||||
} catch (\Exception $e) {
|
||||
if ($e instanceof HttpResponseException) {
|
||||
throw $e;
|
||||
}
|
||||
$this->error($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 详情
|
||||
* @return void
|
||||
@@ -50,6 +91,7 @@ class Message extends Api
|
||||
}
|
||||
|
||||
/**
|
||||
* 设为已读
|
||||
* @return void
|
||||
*/
|
||||
public function setRead()
|
||||
@@ -58,9 +100,14 @@ class Message extends Api
|
||||
$user_id = $this->auth->id ?? -1;
|
||||
$id = $this->request->post('id', 0);
|
||||
$isAll = $this->request->post('isAll', 0);
|
||||
//isAll 时可携带 message_type,仅将该类型全部设为已读
|
||||
$message_type = $this->request->post('message_type', '');
|
||||
$where = ['id' => $id, 'to_user_id' => $user_id];
|
||||
if ($isAll) {
|
||||
unset($where['id']);
|
||||
if ($message_type !== '' && $message_type !== null) {
|
||||
$where['message_type'] = $message_type;
|
||||
}
|
||||
}
|
||||
$res = MessageService::where($where)->update(['is_read' => CommonService::STATUS_ON]);
|
||||
if ($res === false) {
|
||||
|
||||
@@ -38,7 +38,7 @@ class Messages extends Model
|
||||
|
||||
public function getMessageTypeList()
|
||||
{
|
||||
return ['0' => __('Message_type 0'), '1' => __('Message_type 1')];
|
||||
return ['0' => __('Message_type 0'), '1' => __('Message_type 1'), '2' => __('Message_type 2')];
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -112,6 +112,7 @@ class EasyWeChatService
|
||||
|
||||
/**
|
||||
* 发送小程序订阅消息
|
||||
* 只要走了订阅消息,就同步发送一条站内消息(失败不影响订阅消息主流程)
|
||||
* @param $user_id
|
||||
* @param $template_id
|
||||
* @param array $msgData
|
||||
@@ -123,6 +124,17 @@ class EasyWeChatService
|
||||
$response = $this->app->getClient()->postJson('/cgi-bin/message/subscribe/send', $data);
|
||||
$res = $response->toArray(false);
|
||||
Log::info('发送订阅消息-res' . json_encode(['data' => $data, 'res' => $res], JSON_UNESCAPED_UNICODE));
|
||||
|
||||
//同步写站内消息(extra_json.page 直接复用订阅消息的跳转页面)
|
||||
try {
|
||||
MessageService::createBySubscribe($user_id, $template_id, $msgData, $data['page'] ?? '');
|
||||
} catch (\Exception $e) {
|
||||
Log::error('订阅消息同步站内消息失败:' . json_encode([
|
||||
'user_id' => $user_id,
|
||||
'template_id' => $template_id,
|
||||
'error' => $e->getMessage(),
|
||||
], JSON_UNESCAPED_UNICODE));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -13,6 +13,11 @@ class MessageService
|
||||
/** @var Messages */
|
||||
public static $Model = Messages::class;
|
||||
|
||||
// 消息类型
|
||||
const TYPE_SYSTEM = '0'; //系统消息:账户、审核、安全类
|
||||
const TYPE_NOTICE = '1'; //公告消息:平台运营公告
|
||||
const TYPE_DEMAND = '2'; //需求消息:需求/工单/合同/订单等业务流程
|
||||
|
||||
|
||||
/**
|
||||
* @param $to_user_id
|
||||
@@ -23,10 +28,10 @@ class MessageService
|
||||
* @return mixed
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function createData($to_user_id, $title, $content, $extra_json = [], $from_user_id = 0)
|
||||
public static function createData($to_user_id, $title, $content, $extra_json = [], $from_user_id = 0, $message_type = self::TYPE_SYSTEM)
|
||||
{
|
||||
$data = [
|
||||
'message_type' => CommonService::NO,
|
||||
'message_type' => $message_type,
|
||||
'from_user_id' => $from_user_id,
|
||||
'to_user_id' => $to_user_id,
|
||||
'title' => $title,
|
||||
@@ -43,4 +48,48 @@ class MessageService
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送订阅消息时同步写入站内消息
|
||||
* 数据来源:MSG_MAP 中模板的 name / message_type / valueNames / remark / page
|
||||
* @param int $to_user_id 收件用户
|
||||
* @param string $template_id 订阅消息模板ID
|
||||
* @param array $msgData sendMsg 的原始参数 ['values'=>[], 'param'=>[], 'page'=>'']
|
||||
* @param string $page 打包后的跳转页面(含query),如 pages/demand/list/index?opType=my&demandId=1
|
||||
* @return mixed
|
||||
*/
|
||||
public static function createBySubscribe($to_user_id, $template_id, array $msgData = [], $page = '')
|
||||
{
|
||||
$template = SubscribeMessageService::MSG_MAP[$template_id] ?? [];
|
||||
if (!$template) {
|
||||
//未定义的模板不写站内消息
|
||||
return false;
|
||||
}
|
||||
|
||||
//按模板字段名组装可读内容:工单号:xxx;服务项目:xxx;...;备注:xxx
|
||||
$values = $msgData['values'] ?? [];
|
||||
$valueNames = $template['valueNames'] ?? [];
|
||||
$lines = [];
|
||||
foreach ($valueNames as $key => $name) {
|
||||
if (isset($values[$key]) && $values[$key] !== '') {
|
||||
$lines[] = $name . ':' . $values[$key];
|
||||
}
|
||||
}
|
||||
if (!empty($template['remark'])) {
|
||||
$lines[] = '备注:' . $template['remark'];
|
||||
}
|
||||
$content = implode("\n", $lines);
|
||||
|
||||
return self::createData(
|
||||
$to_user_id,
|
||||
$template['name'] ?? '系统通知',
|
||||
$content,
|
||||
[
|
||||
'page' => $page ?: ($template['page'] ?? ''),
|
||||
'template_id' => $template_id,
|
||||
],
|
||||
0, //系统发出
|
||||
$template['message_type'] ?? self::TYPE_SYSTEM
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,8 @@ class SubscribeMessageService
|
||||
|
||||
const MSG_MAP = [
|
||||
self::MSG_TEMP_DEMAND_RECEIVE => [
|
||||
'name' => '接单成功通知', //站内消息标题
|
||||
'message_type' => MessageService::TYPE_DEMAND, //站内消息类型
|
||||
'page' => self::PAGE_DEMAND_LIST . '?opType=myQuote', //我的报价需求列表
|
||||
'remark' => '恭喜中标,请到我投标的需求确认接单!',
|
||||
/*
|
||||
@@ -32,9 +34,12 @@ class SubscribeMessageService
|
||||
任务名称:共享精英团队logo设计
|
||||
备注:接单成功,请尽快安排排产
|
||||
*/
|
||||
'fields' => ['character_string1', 'thing9', 'thing10', 'thing4']
|
||||
'fields' => ['character_string1', 'thing9', 'thing10', 'thing4'],
|
||||
'valueNames' => ['工单号', '服务项目', '任务名称', '备注'], //与fields一一对应,用于站内消息内容组装
|
||||
],
|
||||
self::MSG_TEMP_DEMAND_MATCH_SUCCESS => [
|
||||
'name' => '工单匹配成功通知',
|
||||
'message_type' => MessageService::TYPE_DEMAND,
|
||||
'page' => self::PAGE_DEMAND_LIST . '?opType=my', //我发布的需求列表
|
||||
'remark' => '匹配工程师成功,请到需求详情查看沟通',
|
||||
/*
|
||||
@@ -43,8 +48,11 @@ class SubscribeMessageService
|
||||
温馨提醒:您的工单已成功匹配,请您电话保持畅通。
|
||||
*/
|
||||
'fields' => ['thing1', 'phrase2', 'thing5'],
|
||||
'valueNames' => ['匹配服务方', '服务类别', '温馨提醒'],
|
||||
],
|
||||
self::MSG_TEMP_DEMAND_SETTLE => [
|
||||
'name' => '工单结算通知',
|
||||
'message_type' => MessageService::TYPE_DEMAND,
|
||||
'page' => self::PAGE_DEMAND_LIST . '?opType=myQuote', //我报价的需求列表
|
||||
'remark' => '该工单已结算完成',
|
||||
/*
|
||||
@@ -53,8 +61,11 @@ class SubscribeMessageService
|
||||
结算说明:订单已收货结算完成
|
||||
*/
|
||||
'fields' => ['character_string4', 'amount1', 'thing2'],
|
||||
'valueNames' => ['订单编号', '结算金额', '结算说明'],
|
||||
],
|
||||
self::MSG_TEMP_WORK_START => [
|
||||
'name' => '项目开工通知',
|
||||
'message_type' => MessageService::TYPE_DEMAND,
|
||||
'page' => self::PAGE_DEMAND_LIST . '?opType=myQuote', //我报价的需求列表
|
||||
'remark' => '客户已支付预付款,可以开始工作',
|
||||
/*
|
||||
@@ -63,8 +74,11 @@ class SubscribeMessageService
|
||||
备注:请在上下班时间安排对工人进行考勤
|
||||
*/
|
||||
'fields' => ['thing1', 'time2', 'thing4'],
|
||||
'valueNames' => ['项目名称', '工作日期', '备注'],
|
||||
],
|
||||
self::MSG_TEMP_WORK_ACCEPT => [
|
||||
'name' => '工单验收通知',
|
||||
'message_type' => MessageService::TYPE_DEMAND,
|
||||
'page' => self::PAGE_DEMAND_LIST . '?opType=my', //我发布的需求列表
|
||||
'remark' => '需求开发工作已完成,请前去验收成果',
|
||||
/*
|
||||
@@ -74,8 +88,11 @@ class SubscribeMessageService
|
||||
温馨提醒:登录平台确保验收,给予工程师中肯评价吧~
|
||||
*/
|
||||
'fields' => ['character_string1', 'thing8', 'thing2', 'thing5'],
|
||||
'valueNames' => ['工单编号', '工单标题', '验收内容', '温馨提醒'],
|
||||
],
|
||||
self::MSG_TEMP_WORK_ACCEPT_RESULT => [
|
||||
'name' => '验收结果通知',
|
||||
'message_type' => MessageService::TYPE_DEMAND,
|
||||
'page' => self::PAGE_DEMAND_LIST . '?opType=myQuote', //我报价的需求列表
|
||||
'remark' => '客户已验收并支付尾款,可以发送源码',
|
||||
'remarkFail' => '客户验收未通过,请根据要求修改',
|
||||
@@ -86,8 +103,11 @@ class SubscribeMessageService
|
||||
原因:不符合需求描述的内容
|
||||
*/
|
||||
'fields' => ['thing1', 'thing3', 'thing5', 'thing4'],
|
||||
'valueNames' => ['验收阶段', '验收结果', '备注', '原因'],
|
||||
],
|
||||
self::MSG_TEMP_CONTRACT_PAY => [
|
||||
'name' => '合同付款通知',
|
||||
'message_type' => MessageService::TYPE_DEMAND,
|
||||
'page' => self::PAGE_ORDER_LIST, //我的订单列表
|
||||
'remark' => '请到我的订单完成合同尾款支付',
|
||||
/*
|
||||
@@ -98,8 +118,11 @@ class SubscribeMessageService
|
||||
备注:您有以上合同需要付款,请及时发起流程
|
||||
*/
|
||||
'fields' => ['thing1', 'character_string2', 'amount5', 'time4', 'thing3'],
|
||||
'valueNames' => ['项目名称', '合同编号', '结算金额', '结算周期', '备注'],
|
||||
],
|
||||
self::MSG_TEMP_CONTRACT_SIGN => [
|
||||
'name' => '合同签约通知',
|
||||
'message_type' => MessageService::TYPE_DEMAND,
|
||||
'page' => self::PAGE_DEMAND_LIST . '?opType=my', //我发布的需求列表
|
||||
'remark' => '需求已确认,请前去完成合同签约',
|
||||
/*
|
||||
@@ -109,8 +132,11 @@ class SubscribeMessageService
|
||||
备注:【签约成功】请及时缴纳首期款办理入住
|
||||
*/
|
||||
'fields' => ['thing1', 'thing2', 'amount12', 'thing5'],
|
||||
'valueNames' => ['合同编号', '项目名称', '签约金额', '备注'],
|
||||
],
|
||||
self::MSG_TEMP_ORDER_SHIPPED => [
|
||||
'name' => '订单发货通知',
|
||||
'message_type' => MessageService::TYPE_DEMAND,
|
||||
'page' => self::PAGE_ORDER_LIST, //订单列表
|
||||
'remark' => '发货成功,请到订单详情查看激活码',
|
||||
/*
|
||||
@@ -120,8 +146,11 @@ class SubscribeMessageService
|
||||
备注:快递配送中,请留意电话或者短信
|
||||
*/
|
||||
'fields' => ['character_string2', 'thing1', 'phrase19', 'thing6'],
|
||||
'valueNames' => ['订单号', '商品名称', '订单状态', '备注'],
|
||||
],
|
||||
self::MSG_TEMP_CONFIRM_RECEIPT => [
|
||||
'name' => '确认收货通知',
|
||||
'message_type' => MessageService::TYPE_DEMAND,
|
||||
'page' => self::PAGE_DEMAND_DETAIL, //需求详情(发送时携带需求id)
|
||||
'remark' => '开发成果已交付,请查看交付内容并确认收货',
|
||||
/*
|
||||
@@ -131,8 +160,11 @@ class SubscribeMessageService
|
||||
备注:开发成果已交付,请查看交付内容并确认收货
|
||||
*/
|
||||
'fields' => ['character_string1', 'character_string2', 'amount3', 'thing5'],
|
||||
'valueNames' => ['产品名称', '订单号', '付款金额', '备注'],
|
||||
],
|
||||
self::MSG_TEMP_DEVELOPER_JOIN => [
|
||||
'name' => '入驻审核通知',
|
||||
'message_type' => MessageService::TYPE_SYSTEM, //入驻审核属系统消息
|
||||
'page' => self::PAGE_HOME, //首页
|
||||
'remark' => '开发者入驻成功,前去接单大厅接单',
|
||||
/*
|
||||
@@ -142,6 +174,7 @@ class SubscribeMessageService
|
||||
备注:备注说明
|
||||
*/
|
||||
'fields' => ['thing2', 'time3', 'phrase4', 'thing5'],
|
||||
'valueNames' => ['申请人', '申请时间', '状态', '备注'],
|
||||
],
|
||||
];
|
||||
|
||||
|
||||
@@ -25,8 +25,43 @@ App({
|
||||
});
|
||||
},
|
||||
});
|
||||
//启动未读消息轮询(tabbar红点)
|
||||
this.startUnreadPolling();
|
||||
},
|
||||
onShow: function () {
|
||||
updateManager();
|
||||
//从后台切回前台,立即刷新未读红点
|
||||
this.refreshUnread();
|
||||
},
|
||||
onHide: function () {},
|
||||
globalData: {
|
||||
unread: 0, //未读消息数(tabbar红点共享缓存)
|
||||
},
|
||||
|
||||
/**
|
||||
* 未读消息轮询:小程序前台每10s刷新一次底部tabbar红点
|
||||
* (切后台后JS执行会被系统挂起,定时器自然暂停,回前台由onShow立即补一次)
|
||||
*/
|
||||
startUnreadPolling: function () {
|
||||
if (this.unreadTimer) {
|
||||
return;
|
||||
}
|
||||
this.unreadTimer = setInterval(() => {
|
||||
this.refreshUnread();
|
||||
}, 10000);
|
||||
},
|
||||
|
||||
/**
|
||||
* 刷新当前 tab 页 tabbar 实例的红点(非tab页无tabbar实例,自动跳过)
|
||||
*/
|
||||
refreshUnread: function () {
|
||||
if (!wx.getStorageSync('token')) {
|
||||
return;
|
||||
}
|
||||
const page = getCurrentPages().pop();
|
||||
const tabBar = page && typeof page.getTabBar === 'function' ? page.getTabBar() : null;
|
||||
if (tabBar && typeof tabBar.refreshUnread === 'function') {
|
||||
tabBar.refreshUnread();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -107,5 +107,6 @@ module.exports = {
|
||||
MessageList: ApiRootUrl + 'message/list', //消息列表
|
||||
MessageDetail: ApiRootUrl + 'message/detail', //消息详情
|
||||
MessageSetRead: ApiRootUrl + 'message/setRead', //设为已读
|
||||
MessageUnreadCount: ApiRootUrl + 'message/unreadCount', //未读消息数(tabbar红点)
|
||||
|
||||
};
|
||||
@@ -1,21 +1,25 @@
|
||||
export default [{
|
||||
key: 'home',
|
||||
icon: 'home',
|
||||
text: '首页',
|
||||
url: 'pages/home/home',
|
||||
},
|
||||
{
|
||||
key: 'goods',
|
||||
icon: 'sort',
|
||||
text: '软件',
|
||||
url: 'pages/goods/list/index',
|
||||
},
|
||||
{
|
||||
key: 'message',
|
||||
icon: 'comment',
|
||||
text: '消息',
|
||||
url: 'pages/message/list/index',
|
||||
},
|
||||
{
|
||||
key: 'usercenter',
|
||||
icon: 'person',
|
||||
text: '个人中心',
|
||||
url: 'pages/usercenter/index',
|
||||
},
|
||||
];
|
||||
];
|
||||
|
||||
@@ -1,8 +1,28 @@
|
||||
import TabMenu from './data';
|
||||
import { fetchMessageUnreadCount } from '../services/message/fetchMessage';
|
||||
Component({
|
||||
data: {
|
||||
active: 0,
|
||||
list: TabMenu,
|
||||
unread: 0, //未读消息数,>0 时消息tab显示红点
|
||||
},
|
||||
|
||||
lifetimes: {
|
||||
attached() {
|
||||
//先用 globalData 缓存渲染,避免切换 tab 时红点闪没,再拉取最新
|
||||
const app = getApp();
|
||||
if (app && Number(app.globalData?.unread) > 0) {
|
||||
this.setData({ unread: Number(app.globalData.unread) });
|
||||
}
|
||||
this.refreshUnread();
|
||||
},
|
||||
},
|
||||
|
||||
//所在 tab 页面显示时刷新(切换 tab、从其他页面返回均会触发)
|
||||
pageLifetimes: {
|
||||
show() {
|
||||
this.refreshUnread();
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
@@ -25,5 +45,42 @@ Component({
|
||||
);
|
||||
this.setData({ active });
|
||||
},
|
||||
|
||||
/**
|
||||
* 从服务端拉取未读消息数
|
||||
* 未登录/请求失败时保持 0(不显示红点),不影响 tabbar 正常使用
|
||||
* 5s 内重复触发只发一次请求(app轮询/页面onShow/tabbar show 多处触发)
|
||||
*/
|
||||
refreshUnread(force = false) {
|
||||
if (!wx.getStorageSync('token')) {
|
||||
this.setData({ unread: 0 });
|
||||
const app = getApp();
|
||||
if (app) app.globalData.unread = 0;
|
||||
return;
|
||||
}
|
||||
const now = Date.now();
|
||||
if (!force && now - (this._lastFetchAt || 0) < 5000) {
|
||||
return;
|
||||
}
|
||||
this._lastFetchAt = now;
|
||||
fetchMessageUnreadCount().then((res) => {
|
||||
if (res.code == 1) {
|
||||
const unread = Number(res.data?.unread_count) || 0;
|
||||
this.setData({ unread });
|
||||
const app = getApp();
|
||||
if (app) app.globalData.unread = unread;
|
||||
}
|
||||
}).catch(() => {});
|
||||
},
|
||||
|
||||
/**
|
||||
* 供消息页主动同步未读数(读消息/全部已读后红点实时消失)
|
||||
*/
|
||||
setUnread(count) {
|
||||
const unread = Number(count) || 0;
|
||||
this.setData({ unread });
|
||||
const app = getApp();
|
||||
if (app) app.globalData.unread = unread;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -11,7 +11,11 @@
|
||||
wx:key="index"
|
||||
>
|
||||
<view class="custom-tab-bar-wrapper">
|
||||
<t-icon prefix="wr" name="{{item.icon}}" size="48rpx" />
|
||||
<view class="tab-icon-wrap">
|
||||
<t-icon prefix="wr" name="{{item.icon}}" size="48rpx" />
|
||||
<!-- 消息tab未读红点 -->
|
||||
<view wx:if="{{item.key === 'message' && unread > 0}}" class="unread-dot"></view>
|
||||
</view>
|
||||
<view class="text">{{ item.text }}</view>
|
||||
</view>
|
||||
</t-tab-bar-item>
|
||||
|
||||
@@ -11,6 +11,24 @@
|
||||
margin-top: 4rpx;
|
||||
}
|
||||
|
||||
/* tab 图标容器:红点定位基准 */
|
||||
.tab-icon-wrap {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* 消息tab未读红点 */
|
||||
.unread-dot {
|
||||
position: absolute;
|
||||
top: -2rpx;
|
||||
right: -14rpx;
|
||||
width: 16rpx;
|
||||
height: 16rpx;
|
||||
border-radius: 50%;
|
||||
background: #fa5151;
|
||||
border: 2rpx solid #ffffff;
|
||||
box-sizing: content-box;
|
||||
}
|
||||
|
||||
/* 底部导航整体:顶部细分隔线 + 安全区自适应 */
|
||||
.custom-tab-bar {
|
||||
background: rgba(255, 255, 255, 0.96);
|
||||
|
||||
@@ -1,7 +1,21 @@
|
||||
// pages/message/index.js
|
||||
import { fetchMessageList, messageSetRead } from '../../../services/message/fetchMessage';
|
||||
|
||||
// 消息类型 tab:与后端 message_type 对齐(''=全部)
|
||||
const TYPE_TABS = [
|
||||
{ key: '', label: '全部' },
|
||||
{ key: '0', label: '系统消息' },
|
||||
{ key: '1', label: '公告消息' },
|
||||
{ key: '2', label: '需求消息' },
|
||||
];
|
||||
|
||||
Page({
|
||||
data: {
|
||||
tabs: TYPE_TABS,
|
||||
currentType: '', //当前筛选的消息类型:''=全部
|
||||
currentTabIndex: 0,
|
||||
unreadMap: { '0': 0, '1': 0, '2': 0 }, //分类未读数
|
||||
totalUnread: 0, //总未读数
|
||||
list: [],
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
@@ -18,51 +32,99 @@ Page({
|
||||
},
|
||||
|
||||
loadList(refresh = false) {
|
||||
let { pageSize, list } = this.data;
|
||||
let { pageSize, list, currentType } = this.data;
|
||||
const page = refresh ? 1 : this.data.page;
|
||||
|
||||
let params = {
|
||||
page: page,
|
||||
message_type: 0,
|
||||
pageSize: pageSize,
|
||||
};
|
||||
//全部时不传 message_type,由后端返回全类型
|
||||
if (currentType !== '') {
|
||||
params.message_type = currentType;
|
||||
}
|
||||
|
||||
fetchMessageList(params).then((res) => {
|
||||
console.log(res)
|
||||
let newList = res.data.data;
|
||||
let hasMore = res.data.current_page < res.data.last_page;
|
||||
let unreadMap = res.data.unread_map || { '0': 0, '1': 0, '2': 0 };
|
||||
let totalUnread = Number(unreadMap['0'] || 0) + Number(unreadMap['1'] || 0) + Number(unreadMap['2'] || 0);
|
||||
this.setData({
|
||||
list: refresh ? newList : list.concat(newList),
|
||||
page: page + 1,
|
||||
unreadCount: res.data.unread_count,
|
||||
unreadMap: unreadMap,
|
||||
totalUnread: totalUnread,
|
||||
hasMore: hasMore,
|
||||
loading: false
|
||||
});
|
||||
//同步底部tabbar红点
|
||||
this.syncTabBarUnread(totalUnread);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 同步底部tabbar消息红点
|
||||
* @param {number} count 总未读数
|
||||
*/
|
||||
syncTabBarUnread(count) {
|
||||
const tabBar = typeof this.getTabBar === 'function' && this.getTabBar();
|
||||
if (tabBar && typeof tabBar.setUnread === 'function') {
|
||||
tabBar.setUnread(count);
|
||||
}
|
||||
},
|
||||
|
||||
//切换消息类型tab
|
||||
onTabsChange(e) {
|
||||
const index = Number(e.detail.value);
|
||||
const tab = this.data.tabs[index] || this.data.tabs[0];
|
||||
this.setData({
|
||||
currentTabIndex: index,
|
||||
currentType: tab.key
|
||||
});
|
||||
this.loadList(true);
|
||||
},
|
||||
|
||||
showDialog(e) {
|
||||
const id = e.currentTarget.dataset.id;
|
||||
const item = this.data.list.find(msg => msg.id == id);
|
||||
const jumpPage = item?.extra_json?.page || '';
|
||||
this.setData({
|
||||
showTextAndTitle: true,
|
||||
currentMessage: item || {}
|
||||
currentMessage: item || {},
|
||||
//携带跳转页面时提供「前往查看」
|
||||
confirmBtn: jumpPage ? '前往查看' : '关闭'
|
||||
});
|
||||
messageSetRead({ id: id }).then((res) => {
|
||||
console.log(res)
|
||||
// 把查找到的列表中的元素的is_read字段设为1
|
||||
if (item) {
|
||||
const idx = this.data.list.findIndex(msg => msg.id == id);
|
||||
if (idx !== -1) {
|
||||
const key = `list[${idx}].is_read`;
|
||||
this.setData({ [key]: 1 });
|
||||
}
|
||||
messageSetRead({ id: id }).then(() => {
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
const idx = this.data.list.findIndex(msg => msg.id == id);
|
||||
if (idx === -1) {
|
||||
return;
|
||||
}
|
||||
//原来未读的消息:本地未读数-1并同步tabbar红点
|
||||
if (!this.data.list[idx].is_read) {
|
||||
const typeKey = String(item.message_type);
|
||||
const unreadMap = { ...this.data.unreadMap };
|
||||
unreadMap[typeKey] = Math.max(Number(unreadMap[typeKey] || 0) - 1, 0);
|
||||
const totalUnread = Math.max(this.data.totalUnread - 1, 0);
|
||||
this.setData({ unreadMap, totalUnread });
|
||||
this.syncTabBarUnread(totalUnread);
|
||||
}
|
||||
this.setData({ [`list[${idx}].is_read`]: 1 });
|
||||
});
|
||||
},
|
||||
|
||||
closeDialog() {
|
||||
//弹窗确认:有跳转页面则前往,否则仅关闭
|
||||
onDialogConfirm() {
|
||||
const jumpPage = this.data.currentMessage?.extra_json?.page || '';
|
||||
this.setData({ showTextAndTitle: false });
|
||||
if (jumpPage) {
|
||||
wx.navigateTo({
|
||||
url: '/' + String(jumpPage).replace(/^\//, '')
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
onPullDownRefresh() {
|
||||
@@ -76,8 +138,7 @@ Page({
|
||||
},
|
||||
|
||||
onClearUnreadTap() {
|
||||
messageSetRead({ isAll: 1 }).then((res) => {
|
||||
console.log(res)
|
||||
messageSetRead({ isAll: 1 }).then(() => {
|
||||
wx.showToast({ title: '全部已读', icon: 'success' });
|
||||
this.loadList(true);
|
||||
});
|
||||
@@ -95,4 +156,4 @@ Page({
|
||||
},
|
||||
|
||||
onShareAppMessage() { }
|
||||
});
|
||||
});
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
"t-badge": "tdesign-miniprogram/badge/badge",
|
||||
"t-dialog": "tdesign-miniprogram/dialog/dialog",
|
||||
"t-empty": "tdesign-miniprogram/empty/empty",
|
||||
"t-tag": "tdesign-miniprogram/tag/tag",
|
||||
"t-divider": "tdesign-miniprogram/divider/divider"
|
||||
},
|
||||
"navigationBarTitleText": "消息列表"
|
||||
|
||||
@@ -10,11 +10,26 @@
|
||||
<t-icon bindtap="onClearUnreadTap" name="clear" size="40rpx" />
|
||||
</t-col>
|
||||
</t-row>
|
||||
<t-divider />
|
||||
|
||||
<!-- 消息类型分类 -->
|
||||
<t-tabs value="{{currentTabIndex}}" bind:change="onTabsChange" swipeable="{{false}}" t-class="message-tabs">
|
||||
<t-tab-panel
|
||||
wx:for="{{tabs}}"
|
||||
wx:key="key"
|
||||
label="{{item.label}}"
|
||||
value="{{index}}"
|
||||
badge-props="{{ { count: item.key === '' ? totalUnread : unreadMap[item.key] } }}"
|
||||
/>
|
||||
</t-tabs>
|
||||
|
||||
<view class="message-list">
|
||||
<view class="message-item" wx:for="{{list}}" wx:for-item="item" wx:key="id">
|
||||
<t-cell hover bordered title="{{item.title}}" data-key="showTextAndTitle" bind:tap="showDialog" data-id="{{item.id}}">
|
||||
<t-cell hover bordered data-key="showTextAndTitle" bind:tap="showDialog" data-id="{{item.id}}">
|
||||
<t-avatar class="avatar-example" slot="image" image="{{item.image}}" badge-props="{{ {dot: !item.is_read, offset: [0, 4] } }}" />
|
||||
<view slot="title" class="msg-title">
|
||||
<text class="msg-title-text">{{item.title}}</text>
|
||||
<t-tag size="small" variant="light" class="msg-type-tag">{{item.message_type_text}}</t-tag>
|
||||
</view>
|
||||
<view slot="description">{{_index.ellipsis(item.content, 22)}}</view>
|
||||
<text slot="note">{{item.createtime_text}}</text>
|
||||
</t-cell>
|
||||
@@ -25,11 +40,12 @@
|
||||
</view>
|
||||
<view class="safe-bottom" />
|
||||
</view>
|
||||
<t-dialog visible="{{showTextAndTitle}}" title="{{currentMessage.title}}" confirm-btn="{{ confirmBtn }}" bind:confirm="closeDialog">
|
||||
<t-dialog visible="{{showTextAndTitle}}" title="{{currentMessage.title}}" confirm-btn="{{ confirmBtn }}" bind:confirm="onDialogConfirm">
|
||||
<!-- 适配skyline,增加type="list" -->
|
||||
<scroll-view slot="content" type="list" scroll-y class="long-content">
|
||||
<view class="content-container">
|
||||
<view class="content-type">{{currentMessage.message_type_text}}</view>
|
||||
{{currentMessage.content}}
|
||||
</view>
|
||||
</scroll-view>
|
||||
</t-dialog>
|
||||
</t-dialog>
|
||||
|
||||
@@ -43,4 +43,33 @@
|
||||
.empty-container {
|
||||
min-height: 100vh;
|
||||
padding-top: 50%;
|
||||
}
|
||||
|
||||
/* 消息标题行:标题 + 类型标签 */
|
||||
.msg-title {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.msg-title-text {
|
||||
margin-right: 12rpx;
|
||||
}
|
||||
|
||||
.msg-type-tag {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* 弹窗内容:类型标识 + 正文换行 */
|
||||
.content-container {
|
||||
white-space: pre-line;
|
||||
word-break: break-all;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.content-type {
|
||||
font-size: 22rpx;
|
||||
color: var(--text-secondary, #999);
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
@@ -11,4 +11,9 @@ export function fetchMessageDetail(id) {
|
||||
/** 获取提现申请 */
|
||||
export function messageSetRead(param) {
|
||||
return wx.$util.request(wx.$api.MessageSetRead, param, 'POST');
|
||||
}
|
||||
|
||||
/** 获取未读消息数(tabbar红点) */
|
||||
export function fetchMessageUnreadCount() {
|
||||
return wx.$util.request(wx.$api.MessageUnreadCount, {}, 'GET');
|
||||
}
|
||||
Reference in New Issue
Block a user