diff --git a/backend/application/admin/lang/zh-cn/messages.php b/backend/application/admin/lang/zh-cn/messages.php
index f8cf33d..e2b6643 100644
--- a/backend/application/admin/lang/zh-cn/messages.php
+++ b/backend/application/admin/lang/zh-cn/messages.php
@@ -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' => '消息标题',
diff --git a/backend/application/api/controller/Message.php b/backend/application/api/controller/Message.php
index 52bc326..9dd2169 100644
--- a/backend/application/api/controller/Message.php
+++ b/backend/application/api/controller/Message.php
@@ -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) {
diff --git a/backend/application/common/model/Messages.php b/backend/application/common/model/Messages.php
index 0c3bfa7..f97ff8f 100644
--- a/backend/application/common/model/Messages.php
+++ b/backend/application/common/model/Messages.php
@@ -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')];
}
diff --git a/backend/application/common/service/EasyWeChatService.php b/backend/application/common/service/EasyWeChatService.php
index d84c8ac..7b2af73 100644
--- a/backend/application/common/service/EasyWeChatService.php
+++ b/backend/application/common/service/EasyWeChatService.php
@@ -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));
+ }
}
/**
diff --git a/backend/application/common/service/MessageService.php b/backend/application/common/service/MessageService.php
index 345133d..6b965d9 100644
--- a/backend/application/common/service/MessageService.php
+++ b/backend/application/common/service/MessageService.php
@@ -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
+ );
+ }
}
\ No newline at end of file
diff --git a/backend/application/common/service/SubscribeMessageService.php b/backend/application/common/service/SubscribeMessageService.php
index 667d508..f631e36 100644
--- a/backend/application/common/service/SubscribeMessageService.php
+++ b/backend/application/common/service/SubscribeMessageService.php
@@ -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' => ['申请人', '申请时间', '状态', '备注'],
],
];
diff --git a/frontend/miniapp/app.js b/frontend/miniapp/app.js
index 48d90b1..d0544b7 100644
--- a/frontend/miniapp/app.js
+++ b/frontend/miniapp/app.js
@@ -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();
+ }
},
});
diff --git a/frontend/miniapp/config/api.js b/frontend/miniapp/config/api.js
index 0b52cbe..46add54 100644
--- a/frontend/miniapp/config/api.js
+++ b/frontend/miniapp/config/api.js
@@ -107,5 +107,6 @@ module.exports = {
MessageList: ApiRootUrl + 'message/list', //消息列表
MessageDetail: ApiRootUrl + 'message/detail', //消息详情
MessageSetRead: ApiRootUrl + 'message/setRead', //设为已读
+ MessageUnreadCount: ApiRootUrl + 'message/unreadCount', //未读消息数(tabbar红点)
};
\ No newline at end of file
diff --git a/frontend/miniapp/custom-tab-bar/data.js b/frontend/miniapp/custom-tab-bar/data.js
index 9162680..3e844ff 100644
--- a/frontend/miniapp/custom-tab-bar/data.js
+++ b/frontend/miniapp/custom-tab-bar/data.js
@@ -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',
},
-];
\ No newline at end of file
+];
diff --git a/frontend/miniapp/custom-tab-bar/index.js b/frontend/miniapp/custom-tab-bar/index.js
index 90c58df..37fc7a5 100644
--- a/frontend/miniapp/custom-tab-bar/index.js
+++ b/frontend/miniapp/custom-tab-bar/index.js
@@ -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;
+ },
},
});
diff --git a/frontend/miniapp/custom-tab-bar/index.wxml b/frontend/miniapp/custom-tab-bar/index.wxml
index 187a519..6fab572 100644
--- a/frontend/miniapp/custom-tab-bar/index.wxml
+++ b/frontend/miniapp/custom-tab-bar/index.wxml
@@ -11,7 +11,11 @@
wx:key="index"
>
-
+
+
+
+
+
{{ item.text }}
diff --git a/frontend/miniapp/custom-tab-bar/index.wxss b/frontend/miniapp/custom-tab-bar/index.wxss
index c2fd45d..a65e9c6 100644
--- a/frontend/miniapp/custom-tab-bar/index.wxss
+++ b/frontend/miniapp/custom-tab-bar/index.wxss
@@ -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);
diff --git a/frontend/miniapp/pages/message/list/index.js b/frontend/miniapp/pages/message/list/index.js
index 0d79a2c..8e6edf1 100644
--- a/frontend/miniapp/pages/message/list/index.js
+++ b/frontend/miniapp/pages/message/list/index.js
@@ -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() { }
-});
\ No newline at end of file
+});
diff --git a/frontend/miniapp/pages/message/list/index.json b/frontend/miniapp/pages/message/list/index.json
index 2896fff..de8f570 100644
--- a/frontend/miniapp/pages/message/list/index.json
+++ b/frontend/miniapp/pages/message/list/index.json
@@ -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": "消息列表"
diff --git a/frontend/miniapp/pages/message/list/index.wxml b/frontend/miniapp/pages/message/list/index.wxml
index ee23846..a499b3d 100644
--- a/frontend/miniapp/pages/message/list/index.wxml
+++ b/frontend/miniapp/pages/message/list/index.wxml
@@ -10,11 +10,26 @@
-
+
+
+
+
+
+
-
+
+
+ {{item.title}}
+ {{item.message_type_text}}
+
{{_index.ellipsis(item.content, 22)}}
{{item.createtime_text}}
@@ -25,11 +40,12 @@
-
+
+ {{currentMessage.message_type_text}}
{{currentMessage.content}}
-
\ No newline at end of file
+
diff --git a/frontend/miniapp/pages/message/list/index.wxss b/frontend/miniapp/pages/message/list/index.wxss
index 4b73e6b..06c225a 100644
--- a/frontend/miniapp/pages/message/list/index.wxss
+++ b/frontend/miniapp/pages/message/list/index.wxss
@@ -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;
}
\ No newline at end of file
diff --git a/frontend/miniapp/services/message/fetchMessage.js b/frontend/miniapp/services/message/fetchMessage.js
index c24779b..f0bbec8 100644
--- a/frontend/miniapp/services/message/fetchMessage.js
+++ b/frontend/miniapp/services/message/fetchMessage.js
@@ -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');
}
\ No newline at end of file