index.html 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566
  1. <!DOCTYPE html>
  2. <html lang="zh-CN">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>入库明细列表</title>
  6. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  7. <style>
  8. body {
  9. font-family: "微软雅黑", sans-serif;
  10. padding: 20px;
  11. background: #f9f9f9;
  12. }
  13. h2 {
  14. margin-bottom: 20px;
  15. color: #333;
  16. }
  17. .search-container {
  18. display: flex;
  19. gap: 10px;
  20. margin-bottom: 20px;
  21. align-items: center;
  22. flex-wrap: wrap;
  23. }
  24. #searchInput {
  25. padding: 8px 12px;
  26. width: 300px;
  27. font-size: 14px;
  28. border: 1px solid #ccc;
  29. border-radius: 4px;
  30. flex: 1;
  31. min-width: 200px;
  32. }
  33. .btn {
  34. padding: 8px 16px;
  35. font-size: 14px;
  36. border: 1px solid #ccc;
  37. border-radius: 4px;
  38. cursor: pointer;
  39. display: inline-flex;
  40. align-items: center;
  41. gap: 5px;
  42. transition: all 0.3s;
  43. }
  44. .btn-primary {
  45. background-color: #18bc9c;
  46. color: #fff;
  47. border-color: #18bc9c;
  48. }
  49. .btn-primary:hover {
  50. background-color: #15a589;
  51. border-color: #149078;
  52. }
  53. .btn-success {
  54. background-color: #5cb85c;
  55. color: #fff;
  56. border-color: #4cae4c;
  57. }
  58. .btn-success:hover {
  59. background-color: #4cae4c;
  60. border-color: #398439;
  61. }
  62. .scroll-table-wrapper {
  63. max-height: 500px;
  64. overflow-y: auto;
  65. border: 1px solid #ddd;
  66. background: white;
  67. margin-bottom: 15px;
  68. box-shadow: 0 1px 3px rgba(0,0,0,0.1);
  69. }
  70. table {
  71. width: 100%;
  72. border-collapse: collapse;
  73. table-layout: fixed;
  74. }
  75. th, td {
  76. border: 1px solid #ddd;
  77. text-align: center;
  78. font-size: 14px;
  79. padding: 8px;
  80. word-break: break-word;
  81. }
  82. thead th {
  83. background: #f0f0f0;
  84. position: sticky;
  85. top: 0;
  86. z-index: 2;
  87. font-weight: bold;
  88. }
  89. tbody tr:hover {
  90. background: #f5f5f5;
  91. }
  92. tr.selected {
  93. background-color: #ddefeb !important;
  94. }
  95. #pagination-wrapper {
  96. display: flex;
  97. justify-content: space-between;
  98. align-items: center;
  99. flex-wrap: wrap;
  100. margin-top: 20px;
  101. font-size: 14px;
  102. gap: 10px;
  103. }
  104. #pagination-controls {
  105. display: flex;
  106. align-items: center;
  107. flex-wrap: wrap;
  108. gap: 5px;
  109. }
  110. #pagination-controls button {
  111. padding: 6px 12px;
  112. cursor: pointer;
  113. border: 1px solid #ddd;
  114. background: white;
  115. border-radius: 4px;
  116. transition: all 0.3s;
  117. }
  118. #pagination-controls button:hover:not(:disabled) {
  119. background: #f0f0f0;
  120. }
  121. #pagination-controls button.active {
  122. background: #18bc9c;
  123. color: white;
  124. border-color: #18bc9c;
  125. }
  126. #pagination-controls button:disabled {
  127. background: #eee;
  128. color: #aaa;
  129. cursor: not-allowed;
  130. }
  131. #pageSize {
  132. margin: 0 5px;
  133. padding: 5px;
  134. border-radius: 4px;
  135. border: 1px solid #ddd;
  136. }
  137. .ellipsis {
  138. padding: 6px 10px;
  139. color: #999;
  140. }
  141. .rotating {
  142. animation: spin 1s linear infinite;
  143. }
  144. @keyframes spin {
  145. 0% { transform: rotate(0deg); }
  146. 100% { transform: rotate(360deg); }
  147. }
  148. .form-group {
  149. margin-bottom: 15px;
  150. }
  151. .form-group label {
  152. display: block;
  153. margin-bottom: 5px;
  154. font-weight: bold;
  155. }
  156. .form-control {
  157. width: 100%;
  158. padding: 8px 12px;
  159. border: 1px solid #ccc;
  160. border-radius: 4px;
  161. box-sizing: border-box;
  162. }
  163. .no-data {
  164. text-align: center;
  165. padding: 20px;
  166. color: #999;
  167. font-style: italic;
  168. }
  169. /* 响应式调整 */
  170. @media (max-width: 768px) {
  171. .search-container {
  172. flex-direction: column;
  173. align-items: stretch;
  174. }
  175. #searchInput {
  176. width: 100%;
  177. }
  178. #pagination-wrapper {
  179. flex-direction: column;
  180. align-items: flex-start;
  181. }
  182. }
  183. </style>
  184. </head>
  185. <body>
  186. <h2>入库明细列表</h2>
  187. <div class="search-container">
  188. <button class="btn btn-primary btn-refresh" title="刷新">
  189. <i class="fas fa-sync-alt"></i> 刷新
  190. </button>
  191. <input type="text" id="searchInput" placeholder="搜索产品名称、产品代号、工单编号..." />
  192. <button id="addBtn" class="btn btn-success">
  193. <i class="fas fa-plus"></i> 新增入库产品
  194. </button>
  195. </div>
  196. <div class="scroll-table-wrapper">
  197. <table id="productTable">
  198. <thead>
  199. <tr>
  200. <th style="width: 15%">销售订单号</th>
  201. <th style="width: 10%">工单编号</th>
  202. <th style="width: 10%">产品代号</th>
  203. <th style="width: 30%">产品名称</th>
  204. <th style="width: 10%">数量</th>
  205. <th style="width: 10%">入库日期</th>
  206. </tr>
  207. </thead>
  208. <tbody id="tableBody">
  209. <!-- JS 动态渲染数据 -->
  210. </tbody>
  211. </table>
  212. </div>
  213. <div id="pagination-wrapper">
  214. <div id="pagination-info">共 0 条记录,第 1 / 1 页</div>
  215. <div id="pagination-controls">
  216. 每页显示:
  217. <select id="pageSize">
  218. <option value="10">10</option>
  219. <option value="20" selected>20</option>
  220. <option value="50">50</option>
  221. <option value="100">100</option>
  222. </select> 条
  223. </div>
  224. </div>
  225. <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css">
  226. <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
  227. <script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
  228. <script src="https://cdn.jsdelivr.net/npm/layer/dist/layer.js"></script>
  229. <script>
  230. $(function() {
  231. // 初始化变量
  232. let currentPage = 1;
  233. let searchKey = '';
  234. let pageSize = parseInt($('#pageSize').val());
  235. // 加载数据函数
  236. function loadData(page = 1, search = '') {
  237. pageSize = parseInt($('#pageSize').val());
  238. // 显示加载中
  239. const loadingIndex = layer.load(1, {
  240. shade: [0.1,'#fff']
  241. });
  242. $.ajax({
  243. method: "GET",
  244. url: "Finishedproduct/index",
  245. data: {
  246. page: page,
  247. search: search,
  248. limit: pageSize
  249. },
  250. dataType: "json",
  251. success: function(res) {
  252. layer.close(loadingIndex);
  253. console.log('API响应数据:', res); // 调试用
  254. // 确保res是对象且包含所需数据
  255. if (!res || typeof res !== 'object') {
  256. console.error('返回数据格式不正确:', res);
  257. showNoData();
  258. return;
  259. }
  260. const tbody = $('#tableBody');
  261. tbody.empty();
  262. $('#checkAll').prop('checked', false);
  263. // 检查数据是否存在且是数组
  264. if (Array.isArray(res.data) && res.data.length > 0) {
  265. renderTableData(res.data);
  266. } else if (Array.isArray(res) && res.length > 0) {
  267. // 如果返回的是数组本身而不是包含data属性的对象
  268. renderTableData(res);
  269. res = {
  270. data: res,
  271. total: res.length,
  272. page: page,
  273. limit: pageSize
  274. };
  275. } else {
  276. showNoData();
  277. }
  278. // 更新分页信息
  279. const total = res.total || res.data?.length || 0;
  280. renderPaginationInfo(total, page, pageSize);
  281. renderPagination(total, page, pageSize);
  282. },
  283. error: function(xhr, status, error) {
  284. layer.close(loadingIndex);
  285. console.error("加载数据失败:", error, "状态:", status);
  286. showNoData();
  287. layer.msg('加载数据失败: ' + (xhr.responseJSON?.message || error), {icon: 2});
  288. }
  289. });
  290. }
  291. // 渲染表格数据
  292. function renderTableData(data) {
  293. const tbody = $('#tableBody');
  294. data.forEach(row => {
  295. tbody.append(`
  296. <tr>
  297. <td>${row.订单编号 || '-'}</td>
  298. <td>${row.jjcp_gdbh || '-'}</td>
  299. <td>${row.jjcp_cpdh || '-'}</td>
  300. <td>${row.jjcp_cpmc || '-'}</td>
  301. <td>${row.jjcp_sl || '0'}</td>
  302. <td>${row.Sys_rq}</td>
  303. </tr>
  304. `);
  305. });
  306. }
  307. // 显示无数据提示
  308. function showNoData() {
  309. $('#tableBody').html(`
  310. <tr>
  311. <td colspan="7" class="no-data">
  312. <i class="fas fa-info-circle"></i> 暂无数据
  313. </td>
  314. </tr>
  315. `);
  316. $('#pagination-info').text('共 0 条记录,第 1 / 1 页');
  317. $('#pagination-controls').find('button, .ellipsis').not('#pageSize').remove();
  318. }
  319. // 渲染分页信息
  320. function renderPaginationInfo(total, page, limit) {
  321. const totalPages = Math.ceil(total / limit) || 1;
  322. $('#pagination-info').text(`共 ${total} 条记录,第 ${page} / ${totalPages} 页`);
  323. }
  324. // 渲染分页控件
  325. function renderPagination(total, current, limit) {
  326. const totalPages = Math.ceil(total / limit) || 1;
  327. current = parseInt(current);
  328. const container = $('#pagination-controls');
  329. container.find('button, .ellipsis').not('#pageSize').remove();
  330. // 添加分页按钮
  331. const addButton = (text, pageNum, isActive = false, isDisabled = false) => {
  332. const btn = $('<button>')
  333. .text(text)
  334. .prop('disabled', isDisabled)
  335. .toggleClass('active', isActive)
  336. .click(() => {
  337. if (!isDisabled && pageNum !== current) {
  338. currentPage = pageNum;
  339. loadData(currentPage, searchKey);
  340. }
  341. });
  342. container.append(btn);
  343. };
  344. // 添加上一页按钮
  345. addButton('上一页', current - 1, false, current === 1);
  346. // 计算分页显示逻辑
  347. const pages = [];
  348. if (totalPages <= 5) {
  349. for (let i = 1; i <= totalPages; i++) pages.push(i);
  350. } else {
  351. if (current <= 3) {
  352. pages.push(1, 2, 3, 4, 5);
  353. if (totalPages > 5) pages.push('...', totalPages);
  354. } else if (current >= totalPages - 2) {
  355. pages.push(1, '...');
  356. for (let i = totalPages - 4; i <= totalPages; i++) pages.push(i);
  357. } else {
  358. pages.push(1, '...', current - 1, current, current + 1, '...', totalPages);
  359. }
  360. }
  361. // 渲染页码按钮
  362. let prev = null;
  363. pages.forEach(p => {
  364. if (p === '...') {
  365. if (prev !== '...') {
  366. container.append('<span class="ellipsis">...</span>');
  367. prev = '...';
  368. }
  369. } else {
  370. addButton(p, p, p === current);
  371. prev = p;
  372. }
  373. });
  374. // 添加下一页按钮
  375. addButton('下一页', current + 1, false, current === totalPages);
  376. }
  377. // 行点击切换勾选 + 高亮
  378. $(document).on('click', '#productTable tbody tr', function(e) {
  379. if (!$(e.target).is('input, a, button')) {
  380. const checkbox = $(this).find('.row-check');
  381. checkbox.prop('checked', !checkbox.prop('checked'));
  382. $(this).toggleClass('selected');
  383. }
  384. });
  385. // 勾选框勾选联动行背景
  386. $(document).on('change', '.row-check', function() {
  387. $(this).closest('tr').toggleClass('selected', $(this).is(':checked'));
  388. });
  389. // 全选/取消全选
  390. $('#checkAll').on('change', function() {
  391. const checked = $(this).is(':checked');
  392. $('.row-check').prop('checked', checked);
  393. $('#productTable tbody tr').toggleClass('selected', checked);
  394. });
  395. // 搜索功能
  396. $('#searchInput').on('input', function() {
  397. searchKey = $(this).val().trim();
  398. currentPage = 1;
  399. loadData(currentPage, searchKey);
  400. });
  401. // 每页显示数量变化
  402. $('#pageSize').on('change', function() {
  403. currentPage = 1;
  404. loadData(currentPage, searchKey);
  405. });
  406. // 刷新按钮
  407. $(document).on('click', '.btn-refresh', function() {
  408. const $icon = $(this).find('i');
  409. $icon.addClass('rotating');
  410. currentPage = 1;
  411. $('#searchInput').val('');
  412. searchKey = '';
  413. loadData(currentPage);
  414. setTimeout(() => {
  415. $icon.removeClass('rotating');
  416. }, 1000);
  417. });
  418. // 新增入库产品
  419. $('#addBtn').on('click', function() {
  420. layer.open({
  421. type: 1,
  422. title: '新增入库记录',
  423. area: ['600px', 'auto'],
  424. shadeClose: true,
  425. resize: true,
  426. maxmin: true,
  427. btn: ['确认入库', '取消'],
  428. content: `
  429. <form id="addForm" style="padding: 20px 30px;" class="form-horizontal">
  430. <div class="form-group">
  431. <label>销售订单号</label>
  432. <input type="text" name="订单编号" class="form-control" required />
  433. </div>
  434. <div class="form-group">
  435. <label>工单编号</label>
  436. <input type="text" name="jjcp_gdbh" class="form-control" required />
  437. </div>
  438. <div class="form-group">
  439. <label>产品代号</label>
  440. <input type="text" name="jjcp_cpdh" class="form-control" required />
  441. </div>
  442. <div class="form-group">
  443. <label>产品名称</label>
  444. <input type="text" name="jjcp_cpmc" class="form-control" required />
  445. </div>
  446. <div class="form-group">
  447. <label>数量</label>
  448. <input type="number" name="jjcp_sl" class="form-control" min="1" required />
  449. </div>
  450. </form>
  451. `,
  452. yes: function(index) {
  453. const formData = {};
  454. let isValid = true;
  455. $('#addForm').find('[required]').each(function() {
  456. const $input = $(this);
  457. const val = $input.val().trim();
  458. if (!val) {
  459. layer.tips('此项为必填项', $input, {tips: [1, '#FF5722']});
  460. isValid = false;
  461. return false;
  462. }
  463. formData[$input.attr('name')] = val;
  464. });
  465. if (!isValid) return;
  466. // 添加系统日期
  467. formData.Sys_rq = new Date().toLocaleDateString();
  468. $.ajax({
  469. method: "POST",
  470. url: "Finishedproduct/finished",
  471. contentType: "application/json",
  472. data: JSON.stringify({ data: [formData] }),
  473. success: function(res) {
  474. if (res && res.code === 1) {
  475. layer.msg('入库成功', {icon: 1});
  476. layer.close(index);
  477. loadData();
  478. } else {
  479. layer.msg(res?.msg || '入库失败', {icon: 2});
  480. }
  481. },
  482. error: function(xhr) {
  483. const errorMsg = xhr.responseJSON?.message || xhr.statusText || '请求失败';
  484. layer.msg('入库失败: ' + errorMsg, {icon: 2});
  485. }
  486. });
  487. }
  488. });
  489. });
  490. // 初始加载数据 - 使用setTimeout确保所有资源加载完成
  491. setTimeout(() => {
  492. loadData();
  493. }, 100);
  494. });
  495. </script>
  496. </body>
  497. </html>