index.html 18 KB

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