message_list.php 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704
  1. <?php
  2. require_once 'conn.php';
  3. checkLogin();
  4. // 获取当前登录员工ID
  5. $employee_id = $_SESSION['employee_id'];
  6. // 筛选参数
  7. $message_type = isset($_GET['message_type']) ? intval($_GET['message_type']) : 0;
  8. $is_read = isset($_GET['is_read']) ? intval($_GET['is_read']) : -1;
  9. $priority = isset($_GET['priority']) ? intval($_GET['priority']) : -1;
  10. $urlStr = '';
  11. // 搜索参数
  12. $keys = $_GET['Keys'] ?? '';
  13. $keyscode = mysqli_real_escape_string($conn, $keys);
  14. // 页码参数
  15. $page = $_GET['Page'] ?? 1;
  16. $ord = $_GET['Ord'] ?? '';
  17. $ordStr = !empty($ord) ? "$ord," : "";
  18. // 构建SQL查询
  19. $sql = "SELECT m.*, mr.is_read, mr.read_time
  20. FROM messages m
  21. LEFT JOIN message_recipients mr ON m.id = mr.message_id AND mr.employee_id = $employee_id
  22. WHERE (m.target_type = 2 OR (mr.employee_id = $employee_id)) AND (mr.is_deleted = 0 OR mr.is_deleted IS NULL)";
  23. // 添加筛选条件
  24. if ($message_type > 0) {
  25. $sql .= " AND m.message_type = $message_type";
  26. $urlStr .= "&message_type=" . urlencode($message_type);
  27. }
  28. if ($is_read >= 0) {
  29. $sql .= " AND mr.is_read = $is_read";
  30. $urlStr .= "&is_read=" . urlencode($is_read);
  31. }
  32. if ($priority >= 0) {
  33. $sql .= " AND m.priority = $priority";
  34. $urlStr .= "&priority=" . urlencode($priority);
  35. }
  36. // 关键词搜索
  37. if (!empty($keyscode)) {
  38. $sql .= " AND (m.title LIKE '%$keyscode%' OR m.content LIKE '%$keyscode%')";
  39. $urlStr .= "&Keys=" . urlencode($keys);
  40. }
  41. // 排序 - 未读优先,然后按时间倒序
  42. $sql .= " ORDER BY CASE WHEN mr.is_read = 0 OR mr.is_read IS NULL THEN 0 ELSE 1 END, m.created_at DESC";
  43. ?>
  44. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  45. <html xmlns="http://www.w3.org/1999/xhtml">
  46. <head>
  47. <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  48. <title>消息列表</title>
  49. <link rel="stylesheet" href="css/common.css" type="text/css" />
  50. <link rel="stylesheet" href="css/alert.css" type="text/css" />
  51. <script src="js/jquery-1.7.2.min.js"></script>
  52. <script src="js/js.js"></script>
  53. <style>
  54. body {
  55. margin: 0;
  56. padding: 20px;
  57. background: #fff;
  58. }
  59. #man_zone {
  60. margin-left: 0;
  61. }
  62. /* 表格布局修复 */
  63. .table2 {
  64. width: 100%;
  65. }
  66. .theader, .tline {
  67. display: flex;
  68. flex-direction: row;
  69. align-items: center;
  70. width: 100%;
  71. border-bottom: 1px solid #ddd;
  72. }
  73. .theader {
  74. background-color: #f2f2f2;
  75. font-weight: bold;
  76. height: 40px;
  77. }
  78. .tline {
  79. height: 45px;
  80. }
  81. .tline:hover {
  82. background-color: #f5f5f5;
  83. }
  84. .col2 { width: 5%; text-align: center; }
  85. .col3 { width: 40%; }
  86. .col4 { width: 10%; }
  87. .col5 { width: 10%; }
  88. .col6 { width: 15%; }
  89. .col7 { width: 20%; }
  90. .theader > div, .tline > div {
  91. padding: 0 5px;
  92. overflow: hidden;
  93. text-overflow: ellipsis;
  94. white-space: nowrap;
  95. }
  96. /* 消息状态标签 */
  97. .badge {
  98. display: inline-block;
  99. padding: 3px 6px;
  100. border-radius: 3px;
  101. font-size: 12px;
  102. color: white;
  103. box-sizing: border-box;
  104. height: 22px;
  105. line-height: 16px;
  106. vertical-align: middle;
  107. }
  108. .badge-primary {
  109. background-color: #4285f4;
  110. }
  111. .badge-secondary {
  112. background-color: #888;
  113. }
  114. .badge-warning {
  115. background-color: #f4b400;
  116. color: #333;
  117. }
  118. .badge-danger {
  119. background-color: #db4437;
  120. }
  121. /* 按钮样式 */
  122. .btn {
  123. display: inline-block;
  124. padding: 3px 8px;
  125. margin: 0 2px;
  126. border-radius: 3px;
  127. cursor: pointer;
  128. text-decoration: none;
  129. font-size: 12px;
  130. box-sizing: border-box;
  131. height: 32px;
  132. line-height: 26px;
  133. vertical-align: middle;
  134. }
  135. .btn-info {
  136. background-color: #4285f4;
  137. color: white;
  138. }
  139. .btn-success {
  140. background-color: #0f9d58;
  141. color: white;
  142. }
  143. .btn-danger {
  144. background-color: #db4437;
  145. color: white;
  146. }
  147. .btn:hover {
  148. /* opacity: 0.8; */
  149. color: #fff;
  150. /* background-color: #999; */
  151. }
  152. /* 未读消息样式 */
  153. .unread {
  154. font-weight: bold;
  155. }
  156. /* 优先级颜色 */
  157. .priority-1 {
  158. color: #f4b400;
  159. }
  160. .priority-2 {
  161. color: #db4437;
  162. font-weight: bold;
  163. }
  164. /* 筛选控件样式 */
  165. .filter-select {
  166. padding: 3px;
  167. border: 1px solid #ccc;
  168. border-radius: 3px;
  169. margin: 3px 5px 3px 0;
  170. width: 100px;
  171. height: 32px;
  172. box-sizing: border-box;
  173. vertical-align: middle;
  174. }
  175. /* 筛选条件区域样式 */
  176. .filter-row {
  177. display: flex;
  178. flex-wrap: nowrap;
  179. align-items: center;
  180. margin-bottom: 8px;
  181. overflow-x: auto;
  182. padding-bottom: 5px;
  183. }
  184. .filter-item {
  185. display: flex;
  186. align-items: center;
  187. margin-right: 10px;
  188. white-space: nowrap;
  189. }
  190. .filter-item label {
  191. margin-right: 5px;
  192. white-space: nowrap;
  193. }
  194. /* 搜索框样式 */
  195. .search-input {
  196. padding: 3px 8px;
  197. border: 1px solid #ccc;
  198. border-radius: 3px;
  199. width: 120px;
  200. margin-right: 5px;
  201. height: 32px;
  202. box-sizing: border-box;
  203. vertical-align: middle;
  204. font-size: 12px;
  205. }
  206. .search-btn {
  207. padding: 3px 10px;
  208. background-color: #4285f4;
  209. color: white;
  210. border: none;
  211. border-radius: 3px;
  212. cursor: pointer;
  213. height: 32px;
  214. box-sizing: border-box;
  215. vertical-align: middle;
  216. font-size: 12px;
  217. line-height: 1;
  218. }
  219. .search-btn:hover {
  220. background-color: #999;
  221. }
  222. /* 使按钮容器垂直居中 */
  223. .col7 {
  224. display: flex;
  225. align-items: center;
  226. justify-content: flex-start;
  227. width: 20%;
  228. text-align: left;
  229. }
  230. /* 状态标签容器垂直居中 */
  231. .col2 {
  232. display: flex;
  233. align-items: center;
  234. justify-content: center;
  235. width: 5%;
  236. text-align: center;
  237. }
  238. /* 滑动面板样式 */
  239. .msg-slidepanel {
  240. cursor: pointer;
  241. }
  242. .msg-slidepanel.open {
  243. font-weight: bold;
  244. color: #3366cc;
  245. }
  246. .notepanel {
  247. display: none;
  248. background: #f9f9f9;
  249. padding: 10px;
  250. border: 1px solid #eee;
  251. margin-bottom: 10px;
  252. text-align: left;
  253. }
  254. .notepanel .noteItem {
  255. font-weight: bold;
  256. margin-bottom: 5px;
  257. text-align: left;
  258. padding-top:0px;
  259. }
  260. .notepanel .noteContent {
  261. margin: 10px 0;
  262. line-height: 1.5;
  263. white-space: pre-line;
  264. text-align: left;
  265. padding-top:0px;
  266. }
  267. .notepanel .noteInfo {
  268. display: flex;
  269. flex-wrap: wrap;
  270. gap: 15px;
  271. margin-bottom: 10px;
  272. text-align: left;
  273. }
  274. .notepanel .message-header-row {
  275. display: flex;
  276. align-items: center;
  277. margin-bottom: 10px;
  278. }
  279. .notepanel .message-header-row .noteItem {
  280. margin-right: 15px;
  281. margin-bottom: 0;
  282. min-width: 80px;
  283. }
  284. </style>
  285. </head>
  286. <body>
  287. <div id="man_zone">
  288. <div class="fastSelect clear">
  289. <H1>筛选条件</H1>
  290. <div class="filter-row">
  291. <div class="filter-item">
  292. <label>消息类型:</label>
  293. <select name="message_type" class="filter-select filterSearch">
  294. <option value="0" <?= $message_type == 0 ? 'selected' : '' ?>>所有类型</option>
  295. <option value="1" <?= $message_type == 1 ? 'selected' : '' ?>>系统消息</option>
  296. <option value="2" <?= $message_type == 2 ? 'selected' : '' ?>>客户相关</option>
  297. <option value="3" <?= $message_type == 3 ? 'selected' : '' ?>>订单相关</option>
  298. <option value="4" <?= $message_type == 4 ? 'selected' : '' ?>>任务提醒</option>
  299. <option value="5" <?= $message_type == 5 ? 'selected' : '' ?>>其他</option>
  300. </select>
  301. </div>
  302. <div class="filter-item">
  303. <label>状态:</label>
  304. <select name="is_read" class="filter-select filterSearch">
  305. <option value="-1" <?= $is_read == -1 ? 'selected' : '' ?>>全部状态</option>
  306. <option value="0" <?= $is_read == 0 ? 'selected' : '' ?>>未读</option>
  307. <option value="1" <?= $is_read == 1 ? 'selected' : '' ?>>已读</option>
  308. </select>
  309. </div>
  310. <div class="filter-item">
  311. <label>优先级:</label>
  312. <select name="priority" class="filter-select filterSearch">
  313. <option value="-1" <?= $priority == -1 ? 'selected' : '' ?>>所有优先级</option>
  314. <option value="0" <?= $priority == 0 ? 'selected' : '' ?>>普通</option>
  315. <option value="1" <?= $priority == 1 ? 'selected' : '' ?>>重要</option>
  316. <option value="2" <?= $priority == 2 ? 'selected' : '' ?>>紧急</option>
  317. </select>
  318. </div>
  319. <div class="filter-item">
  320. <label>搜索:</label>
  321. <input type="text" id="keys" class="search-input" placeholder="标题或内容" value="<?= htmlspecialcharsFix($keys) ?>" />
  322. <input type="button" class="search-btn" value="搜索" onClick="doSearch()" />
  323. </div>
  324. </div>
  325. </div>
  326. <div class="table2 em<?= $_SESSION['employee_id'] ?>">
  327. <div class="theader">
  328. <div class="col2">状态</div>
  329. <div class="col3">标题</div>
  330. <div class="col4">类型</div>
  331. <div class="col5">优先级</div>
  332. <div class="col6">时间</div>
  333. <div class="col7">操作</div>
  334. </div>
  335. <?php
  336. // 设置每页显示记录数
  337. $pageSize = 20;
  338. // 获取总记录数
  339. $countSql = "SELECT COUNT(*) AS total
  340. FROM messages m
  341. LEFT JOIN message_recipients mr ON m.id = mr.message_id AND mr.employee_id = $employee_id
  342. WHERE (m.target_type = 2 OR (mr.employee_id = $employee_id)) AND (mr.is_deleted = 0 OR mr.is_deleted IS NULL)";
  343. // 添加筛选条件
  344. if ($message_type > 0) {
  345. $countSql .= " AND m.message_type = $message_type";
  346. }
  347. if ($is_read >= 0) {
  348. $countSql .= " AND mr.is_read = $is_read";
  349. }
  350. if ($priority >= 0) {
  351. $countSql .= " AND m.priority = $priority";
  352. }
  353. // 添加关键词搜索条件
  354. if (!empty($keyscode)) {
  355. $countSql .= " AND (m.title LIKE '%$keyscode%' OR m.content LIKE '%$keyscode%')";
  356. }
  357. $countResult = mysqli_query($conn, $countSql);
  358. $countRow = mysqli_fetch_assoc($countResult);
  359. $totalRecords = $countRow['total'];
  360. // 计算总页数
  361. $totalPages = ceil($totalRecords / $pageSize);
  362. if ($totalPages < 1) $totalPages = 1;
  363. // 验证当前页码
  364. $page = (int)$page;
  365. if ($page < 1) $page = 1;
  366. if ($page > $totalPages) $page = $totalPages;
  367. // 计算起始记录
  368. $offset = ($page - 1) * $pageSize;
  369. // 添加分页条件
  370. $sql .= " LIMIT $offset, $pageSize";
  371. $result = mysqli_query($conn, $sql);
  372. if (mysqli_num_rows($result) > 0) {
  373. $tempNum = ($page - 1) * $pageSize;
  374. while ($row = mysqli_fetch_assoc($result)) {
  375. $tempNum++;
  376. // 确定消息状态
  377. $is_read_status = $row['is_read'] ?? 0;
  378. // 确定消息类型
  379. $message_type_text = [
  380. 1 => '系统消息',
  381. 2 => '客户相关',
  382. 3 => '订单相关',
  383. 4 => '任务提醒',
  384. 5 => '其他'
  385. ][$row['message_type']] ?? '未知';
  386. // 确定优先级
  387. $priority_class = [
  388. 0 => '',
  389. 1 => 'priority-1',
  390. 2 => 'priority-2'
  391. ][$row['priority']] ?? '';
  392. $priority_text = [
  393. 0 => '普通',
  394. 1 => '重要',
  395. 2 => '紧急'
  396. ][$row['priority']] ?? '未知';
  397. // 行样式类,未读消息加粗显示
  398. $row_class = $is_read_status ? '' : 'unread';
  399. ?>
  400. <div class="tline <?= $row_class ?>">
  401. <div class="col2">
  402. <?php if ($is_read_status): ?>
  403. <span class="badge badge-secondary">已读</span>
  404. <?php else: ?>
  405. <span class="badge badge-primary">未读</span>
  406. <?php endif; ?>
  407. </div>
  408. <div class="col3 msg-slidepanel"><?= htmlspecialcharsFix($row['title']) ?></div>
  409. <div class="col4"><?= $message_type_text ?></div>
  410. <div class="col5 <?= $priority_class ?>"><?= $priority_text ?></div>
  411. <div class="col6"><?= date('Y-m-d H:i', strtotime($row['created_at'])) ?></div>
  412. <div class="col7">
  413. <a href="message_detail.php?id=<?= $row['id'] ?>" class="btn btn-info">查看</a>
  414. <?php if (!$is_read_status): ?>
  415. <a href="javascript:void(0)" class="btn btn-success mark-read" data-id="<?= $row['id'] ?>">标记已读</a>
  416. <?php endif; ?>
  417. <a href="javascript:void(0)" class="btn btn-danger delete-message" data-id="<?= $row['id'] ?>">删除</a>
  418. </div>
  419. </div>
  420. <div class="notepanel clear">
  421. <div class="noteItem" style="padding-top:0px;">详情</div>
  422. <div class="noteInfo">
  423. <div><strong>类型:</strong> <?= $message_type_text ?></div>
  424. <div><strong>优先级:</strong> <span class="<?= $priority_class ?>"><?= $priority_text ?></span></div>
  425. <div><strong>时间:</strong> <?= date('Y-m-d H:i:s', strtotime($row['created_at'])) ?></div>
  426. <?php if ($row['read_time']): ?>
  427. <div><strong>阅读时间:</strong> <?= date('Y-m-d H:i:s', strtotime($row['read_time'])) ?></div>
  428. <?php endif; ?>
  429. </div>
  430. <div class="noteItem" style="padding-top:0px;">内容</div>
  431. <div class="noteContent"><?= nl2br(htmlspecialcharsFix($row['content'])) ?></div>
  432. </div>
  433. <?php
  434. }
  435. } else {
  436. if (empty($keys) && $message_type == 0 && $is_read == -1 && $priority == -1) {
  437. echo '<div class="tline"><div align="center" colspan="6">当前暂无消息记录</div></div>';
  438. } else {
  439. echo '<div class="tline"><div align="center" colspan="6"><a href="?">没有找到匹配的消息记录,点击返回</a></div></div>';
  440. }
  441. }
  442. ?>
  443. <div class="showpagebox">
  444. <?php
  445. if ($totalPages > 1) {
  446. $pageName = "?$urlStr&";
  447. if ($pageName == "?&") {
  448. $pageName = "?";
  449. }
  450. $pageLen = 3;
  451. if ($page > 1) {
  452. echo "<a href=\"{$pageName}Page=1\">首页</a>";
  453. echo "<a href=\"{$pageName}Page=" . ($page - 1) . "\">上一页</a>";
  454. }
  455. if ($pageLen * 2 + 1 >= $totalPages) {
  456. $startPage = 1;
  457. $endPage = $totalPages;
  458. } else {
  459. if ($page <= $pageLen + 1) {
  460. $startPage = 1;
  461. $endPage = $pageLen * 2 + 1;
  462. } else {
  463. $startPage = $page - $pageLen;
  464. $endPage = $page + $pageLen;
  465. }
  466. if ($page + $pageLen > $totalPages) {
  467. $startPage = $totalPages - $pageLen * 2;
  468. $endPage = $totalPages;
  469. }
  470. }
  471. for ($i = $startPage; $i <= $endPage; $i++) {
  472. if ($i == $page) {
  473. echo "<a class=\"current\">$i</a>";
  474. } else {
  475. echo "<a href=\"{$pageName}Page=$i\">$i</a>";
  476. }
  477. }
  478. if ($page < $totalPages) {
  479. if ($totalPages - $page > $pageLen) {
  480. echo "<a href=\"{$pageName}Page=$totalPages\">...$totalPages</a>";
  481. }
  482. echo "<a href=\"{$pageName}Page=" . ($page + 1) . "\">下一页</a>";
  483. echo "<a href=\"{$pageName}Page=$totalPages\">尾页</a>";
  484. }
  485. }
  486. ?>
  487. </div>
  488. </div>
  489. <script>
  490. $(document).ready(function() {
  491. // 处理筛选条件改变
  492. $('.filterSearch').change(function() {
  493. var url = buildSearchUrl();
  494. location.href = url;
  495. });
  496. // 标记为已读
  497. $('.mark-read').click(function() {
  498. var messageId = $(this).data('id');
  499. $.post('message_action.php', {
  500. action: 'mark_read',
  501. message_id: messageId
  502. }, function(response) {
  503. try {
  504. var data = typeof response === 'string' ? JSON.parse(response) : response;
  505. if (data.success) {
  506. location.reload();
  507. } else {
  508. alert('操作失败:' + data.message);
  509. }
  510. } catch (e) {
  511. alert('操作失败:服务器返回未知错误');
  512. }
  513. });
  514. });
  515. // 删除消息
  516. $('.delete-message').click(function() {
  517. if (confirm('确定要删除这条消息吗?')) {
  518. var messageId = $(this).data('id');
  519. $.post('message_action.php', {
  520. action: 'delete',
  521. message_id: messageId
  522. }, function(response) {
  523. try {
  524. var data = typeof response === 'string' ? JSON.parse(response) : response;
  525. if (data.success) {
  526. location.reload();
  527. } else {
  528. alert('操作失败:' + data.message);
  529. }
  530. } catch (e) {
  531. alert('操作失败:服务器返回未知错误');
  532. }
  533. });
  534. }
  535. });
  536. // 点击标题展开消息内容
  537. $('.msg-slidepanel').click(function() {
  538. var $this = $(this);
  539. var $panel = $this.closest('.tline').next('.notepanel');
  540. // 判断是否已经打开
  541. if ($panel.is(':visible')) {
  542. $panel.slideUp('fast');
  543. $this.removeClass('open');
  544. } else {
  545. // 先关闭其他已打开的面板
  546. $('.notepanel').slideUp('fast');
  547. $('.msg-slidepanel').removeClass('open');
  548. // 打开当前点击的面板
  549. $panel.slideDown('fast');
  550. $this.addClass('open');
  551. // 如果消息未读,自动标记为已读
  552. var $row = $this.closest('.tline');
  553. if ($row.hasClass('unread')) {
  554. var messageId = $row.find('.mark-read').data('id');
  555. if (messageId) {
  556. $.post('message_action.php', {
  557. action: 'mark_read',
  558. message_id: messageId
  559. }, function(response) {
  560. try {
  561. var data = typeof response === 'string' ? JSON.parse(response) : response;
  562. if (data.success) {
  563. // 只更新样式,不刷新页面
  564. $row.removeClass('unread');
  565. $row.find('.badge-primary').removeClass('badge-primary').addClass('badge-secondary').text('已读');
  566. $row.find('.mark-read').remove();
  567. }
  568. } catch (e) {
  569. // 忽略错误,不影响用户体验
  570. }
  571. });
  572. }
  573. }
  574. }
  575. });
  576. // 回车键触发搜索
  577. $('#keys').keypress(function(e) {
  578. if (e.which == 13) {
  579. doSearch();
  580. return false;
  581. }
  582. });
  583. });
  584. // 执行搜索
  585. function doSearch() {
  586. var url = buildSearchUrl();
  587. location.href = url;
  588. }
  589. // 构建搜索URL
  590. function buildSearchUrl() {
  591. var url = '?';
  592. var keys = $.trim($('#keys').val());
  593. if (keys) {
  594. url += 'Keys=' + encodeURIComponent(keys) + '&';
  595. }
  596. $('.filterSearch').each(function() {
  597. var name = $(this).attr('name');
  598. var value = $(this).val();
  599. if (value && value != -1) {
  600. url += name + '=' + encodeURIComponent(value) + '&';
  601. }
  602. });
  603. // 移除末尾的&
  604. if (url.endsWith('&')) {
  605. url = url.substring(0, url.length - 1);
  606. }
  607. return url;
  608. }
  609. </script>
  610. </div>
  611. </body>
  612. </html>