index.html 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570
  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: 13%">工单编号</th>
  202. <th style="width: 9%">成品编码</th>
  203. <th style="width: 33%">成品名称</th>
  204. <th style="width: 6%">入库数</th>
  205. <th style="width: 6%">发货数</th>
  206. <th style="width: 6%">剩余数</th>
  207. <!-- <th style="width: 11%">入库日期</th>-->
  208. </tr>
  209. </thead>
  210. <tbody id="tableBody">
  211. <!-- JS 动态渲染数据 -->
  212. </tbody>
  213. </table>
  214. </div>
  215. <div id="pagination-wrapper">
  216. <div id="pagination-info">共 0 条记录,第 1 / 1 页</div>
  217. <div id="pagination-controls">
  218. 每页显示:
  219. <select id="pageSize">
  220. <option value="10">10</option>
  221. <option value="20" selected>20</option>
  222. <option value="50">50</option>
  223. <option value="100">100</option>
  224. </select> 条
  225. </div>
  226. </div>
  227. <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css">
  228. <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
  229. <script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
  230. <script src="https://cdn.jsdelivr.net/npm/layer/dist/layer.js"></script>
  231. <script>
  232. $(function() {
  233. // 初始化变量
  234. let currentPage = 1;
  235. let searchKey = '';
  236. let pageSize = parseInt($('#pageSize').val());
  237. // 加载数据函数
  238. function loadData(page = 1, search = '') {
  239. pageSize = parseInt($('#pageSize').val());
  240. // 显示加载中
  241. const loadingIndex = layer.load(1, {
  242. shade: [0.1,'#fff']
  243. });
  244. $.ajax({
  245. method: "GET",
  246. url: "Finishedproduct/index",
  247. data: {
  248. page: page,
  249. search: search,
  250. limit: pageSize
  251. },
  252. dataType: "json",
  253. success: function(res) {
  254. layer.close(loadingIndex);
  255. console.log('API响应数据:', res); // 调试用
  256. // 确保res是对象且包含所需数据
  257. if (!res || typeof res !== 'object') {
  258. console.error('返回数据格式不正确:', res);
  259. showNoData();
  260. return;
  261. }
  262. const tbody = $('#tableBody');
  263. tbody.empty();
  264. $('#checkAll').prop('checked', false);
  265. // 检查数据是否存在且是数组
  266. if (Array.isArray(res.data) && res.data.length > 0) {
  267. renderTableData(res.data);
  268. } else if (Array.isArray(res) && res.length > 0) {
  269. // 如果返回的是数组本身而不是包含data属性的对象
  270. renderTableData(res);
  271. res = {
  272. data: res,
  273. total: res.length,
  274. page: page,
  275. limit: pageSize
  276. };
  277. } else {
  278. showNoData();
  279. }
  280. // 更新分页信息
  281. const total = res.total || res.data?.length || 0;
  282. renderPaginationInfo(total, page, pageSize);
  283. renderPagination(total, page, pageSize);
  284. },
  285. error: function(xhr, status, error) {
  286. layer.close(loadingIndex);
  287. console.error("加载数据失败:", error, "状态:", status);
  288. showNoData();
  289. layer.msg('加载数据失败: ' + (xhr.responseJSON?.message || error), {icon: 2});
  290. }
  291. });
  292. }
  293. // 渲染表格数据
  294. function renderTableData(data) {
  295. const tbody = $('#tableBody');
  296. data.forEach(row => {
  297. tbody.append(`
  298. <tr>
  299. <td>${row.order_ddbh || '-'}</td>
  300. <td>${row.gdbh || '-'}</td>
  301. <td>${row.cpbm || '-'}</td>
  302. <td>${row.cpmc || '-'}</td>
  303. <td>${row.sl || '0'}</td>
  304. <td>${row.total_chu_quantity || '0'}</td>
  305. <td>${row.remaining_quantity || '0'}</td>
  306. <!-- <td>${row.Sys_rq}</td>-->
  307. </tr>
  308. `);
  309. });
  310. }
  311. // 显示无数据提示
  312. function showNoData() {
  313. $('#tableBody').html(`
  314. <tr>
  315. <td colspan="7" class="no-data">
  316. <i class="fas fa-info-circle"></i> 暂无数据
  317. </td>
  318. </tr>
  319. `);
  320. $('#pagination-info').text('共 0 条记录,第 1 / 1 页');
  321. $('#pagination-controls').find('button, .ellipsis').not('#pageSize').remove();
  322. }
  323. // 渲染分页信息
  324. function renderPaginationInfo(total, page, limit) {
  325. const totalPages = Math.ceil(total / limit) || 1;
  326. $('#pagination-info').text(`共 ${total} 条记录,第 ${page} / ${totalPages} 页`);
  327. }
  328. // 渲染分页控件
  329. function renderPagination(total, current, limit) {
  330. const totalPages = Math.ceil(total / limit) || 1;
  331. current = parseInt(current);
  332. const container = $('#pagination-controls');
  333. container.find('button, .ellipsis').not('#pageSize').remove();
  334. // 添加分页按钮
  335. const addButton = (text, pageNum, isActive = false, isDisabled = false) => {
  336. const btn = $('<button>')
  337. .text(text)
  338. .prop('disabled', isDisabled)
  339. .toggleClass('active', isActive)
  340. .click(() => {
  341. if (!isDisabled && pageNum !== current) {
  342. currentPage = pageNum;
  343. loadData(currentPage, searchKey);
  344. }
  345. });
  346. container.append(btn);
  347. };
  348. // 添加上一页按钮
  349. addButton('上一页', current - 1, false, current === 1);
  350. // 计算分页显示逻辑
  351. const pages = [];
  352. if (totalPages <= 5) {
  353. for (let i = 1; i <= totalPages; i++) pages.push(i);
  354. } else {
  355. if (current <= 3) {
  356. pages.push(1, 2, 3, 4, 5);
  357. if (totalPages > 5) pages.push('...', totalPages);
  358. } else if (current >= totalPages - 2) {
  359. pages.push(1, '...');
  360. for (let i = totalPages - 4; i <= totalPages; i++) pages.push(i);
  361. } else {
  362. pages.push(1, '...', current - 1, current, current + 1, '...', totalPages);
  363. }
  364. }
  365. // 渲染页码按钮
  366. let prev = null;
  367. pages.forEach(p => {
  368. if (p === '...') {
  369. if (prev !== '...') {
  370. container.append('<span class="ellipsis">...</span>');
  371. prev = '...';
  372. }
  373. } else {
  374. addButton(p, p, p === current);
  375. prev = p;
  376. }
  377. });
  378. // 添加下一页按钮
  379. addButton('下一页', current + 1, false, current === totalPages);
  380. }
  381. // 行点击切换勾选 + 高亮
  382. $(document).on('click', '#productTable tbody tr', function(e) {
  383. if (!$(e.target).is('input, a, button')) {
  384. const checkbox = $(this).find('.row-check');
  385. checkbox.prop('checked', !checkbox.prop('checked'));
  386. $(this).toggleClass('selected');
  387. }
  388. });
  389. // 勾选框勾选联动行背景
  390. $(document).on('change', '.row-check', function() {
  391. $(this).closest('tr').toggleClass('selected', $(this).is(':checked'));
  392. });
  393. // 全选/取消全选
  394. $('#checkAll').on('change', function() {
  395. const checked = $(this).is(':checked');
  396. $('.row-check').prop('checked', checked);
  397. $('#productTable tbody tr').toggleClass('selected', checked);
  398. });
  399. // 搜索功能
  400. $('#searchInput').on('input', function() {
  401. searchKey = $(this).val().trim();
  402. currentPage = 1;
  403. loadData(currentPage, searchKey);
  404. });
  405. // 每页显示数量变化
  406. $('#pageSize').on('change', function() {
  407. currentPage = 1;
  408. loadData(currentPage, searchKey);
  409. });
  410. // 刷新按钮
  411. $(document).on('click', '.btn-refresh', function() {
  412. const $icon = $(this).find('i');
  413. $icon.addClass('rotating');
  414. currentPage = 1;
  415. $('#searchInput').val('');
  416. searchKey = '';
  417. loadData(currentPage);
  418. setTimeout(() => {
  419. $icon.removeClass('rotating');
  420. }, 1000);
  421. });
  422. // 新增入库产品
  423. $('#addBtn').on('click', function() {
  424. layer.open({
  425. type: 1,
  426. title: '新增入库记录',
  427. area: ['600px', 'auto'],
  428. shadeClose: true,
  429. resize: true,
  430. maxmin: true,
  431. btn: ['确认入库', '取消'],
  432. content: `
  433. <form id="addForm" style="padding: 20px 30px;" class="form-horizontal">
  434. <div class="form-group">
  435. <label>销售订单号</label>
  436. <input type="text" name="订单编号" class="form-control" required />
  437. </div>
  438. <div class="form-group">
  439. <label>工单编号</label>
  440. <input type="text" name="jjcp_gdbh" class="form-control" required />
  441. </div>
  442. <div class="form-group">
  443. <label>成品编码</label>
  444. <input type="text" name="jjcp_cpbm" class="form-control" required />
  445. </div>
  446. <div class="form-group">
  447. <label>成品名称</label>
  448. <input type="text" name="jjcp_cpmc" class="form-control" required />
  449. </div>
  450. <div class="form-group">
  451. <label>入库数</label>
  452. <input type="number" name="jjcp_sl" class="form-control" min="1" required />
  453. </div>
  454. </form>
  455. `,
  456. yes: function(index) {
  457. const formData = {};
  458. let isValid = true;
  459. $('#addForm').find('[required]').each(function() {
  460. const $input = $(this);
  461. const val = $input.val().trim();
  462. if (!val) {
  463. layer.tips('此项为必填项', $input, {tips: [1, '#FF5722']});
  464. isValid = false;
  465. return false;
  466. }
  467. formData[$input.attr('name')] = val;
  468. });
  469. if (!isValid) return;
  470. // 添加系统日期
  471. formData.Sys_rq = new Date().toLocaleDateString();
  472. $.ajax({
  473. method: "POST",
  474. url: "Finishedproduct/finished",
  475. contentType: "application/json",
  476. data: JSON.stringify({ data: [formData] }),
  477. success: function(res) {
  478. if (res && res.code === 1) {
  479. layer.msg('入库成功', {icon: 1});
  480. layer.close(index);
  481. loadData();
  482. } else {
  483. layer.msg(res?.msg || '入库失败', {icon: 2});
  484. }
  485. },
  486. error: function(xhr) {
  487. const errorMsg = xhr.responseJSON?.message || xhr.statusText || '请求失败';
  488. layer.msg('入库失败: ' + errorMsg, {icon: 2});
  489. }
  490. });
  491. }
  492. });
  493. });
  494. // 初始加载数据 - 使用setTimeout确保所有资源加载完成
  495. setTimeout(() => {
  496. loadData();
  497. }, 100);
  498. });
  499. </script>
  500. </body>
  501. </html>