skuSort.vue 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682
  1. <template>
  2. <div>
  3. <en-table-layout
  4. :tableData="tableData.data"
  5. :loading="loading"
  6. class="draggable-table"
  7. @selection-change="handleSelectionChange"
  8. >
  9. <div slot="toolbar" class="inner-toolbar inner-toolbar2">
  10. <div class="toolbar-btns">
  11. <el-button
  12. size="mini"
  13. type="primary"
  14. :disabled="multipleSelection.length === 0"
  15. @click="batchPrint"
  16. >批量打印</el-button
  17. >
  18. </div>
  19. <!-- <en-table-search @search="searchEvent" @advancedSearch="advancedSearchEvent" advanced advancedWidth="465"
  20. placeholder=''>
  21. <template slot="advanced-content">
  22. <el-form ref="advancedForm" :model="advancedForm" label-width="80px">
  23. <el-form-item label="日志级别">
  24. <el-select v-model="advancedForm.level" placeholder="请选择" clearable>
  25. <el-option label="一般" value="normal" />
  26. <el-option label="重要" value="important" />
  27. </el-select>
  28. </el-form-item>
  29. <el-form-item label="操作时间">
  30. <el-date-picker v-model="advancedForm.order_time_range" type="daterange" align="center" :editable="false"
  31. unlink-panels :default-time="['00:00:00', '23:59:59']" range-separator="-" start-placeholder="开始日期"
  32. end-placeholder="结束日期" value-format="timestamp"
  33. :picker-options="{ disabledDate(time) { return time.getTime() - 1 >= new Date(new Date().getFullYear(), new Date().getMonth(), new Date().getDate()).getTime() + 86400000 - 1 }, shortcuts: MixinPickerShortcuts }">
  34. </el-date-picker>
  35. </el-form-item>
  36. </el-form>
  37. </template>
  38. </en-table-search> -->
  39. </div>
  40. <template slot="table-columns">
  41. <el-table-column label="" width="100">
  42. <template>
  43. <span class="drag-handle">&#9776;</span>
  44. <!-- 拖拽图标 -->
  45. <!-- 其他操作按钮 -->
  46. </template>
  47. </el-table-column>
  48. <el-table-column type="selection" width="55" />
  49. <el-table-column label="图标">
  50. <template slot-scope="scope">
  51. <img
  52. :src="scope.row.goods_vo.image_small_list"
  53. alt=""
  54. width="50px"
  55. />
  56. </template>
  57. </el-table-column>
  58. <el-table-column prop="goods_vo.name" label="商品名称">
  59. <template slot-scope="scope">
  60. <span class="selectable">{{ scope.row.goods_vo.name }}</span>
  61. </template>
  62. </el-table-column>
  63. <el-table-column prop="goods_vo.sn" label="商品编号">
  64. <template slot-scope="scope">
  65. <span class="selectable">{{ scope.row.goods_vo.sn }}</span>
  66. </template>
  67. </el-table-column>
  68. <el-table-column label="SKU" align="center">
  69. <template slot-scope="scope">
  70. <span class="selectable">{{ scope.row.sku }}</span>
  71. </template>
  72. </el-table-column>
  73. <el-table-column prop="spec_name" label="规格型号">
  74. <!-- <template slot-scope="scope">
  75. <span class="selectable">{{ scope.row.spec_name }}</span>
  76. </template> -->
  77. </el-table-column>
  78. <!-- <el-table-column label="条码" align="center">
  79. <template slot-scope="scope">{{ scope.row.bar_code }}</template>
  80. </el-table-column> -->
  81. <!-- <el-table-column prop="sort" label="排序号">
  82. <template slot-scope="scope">
  83. <span class="selectable">{{ scope.row.sort }}</span>
  84. </template>
  85. </el-table-column> -->
  86. <el-table-column label="操作" width="310">
  87. <template slot-scope="scope">
  88. <el-button size="mini" @click="PrintIng(scope.row)"
  89. >打印SKU</el-button
  90. >
  91. </template>
  92. </el-table-column>
  93. </template>
  94. <el-pagination
  95. v-if="tableData"
  96. slot="pagination"
  97. @size-change="handlePageSizeChange"
  98. @current-change="handlePageCurrentChange"
  99. :current-page="tableData.page_no"
  100. :page-sizes="[20, 50, 70, 100]"
  101. :page-size="tableData.page_size"
  102. layout="total, sizes, prev, pager, next, jumper"
  103. :total="tableData.data_total"
  104. >
  105. </el-pagination>
  106. </en-table-layout>
  107. <!-- <el-dialog title="日志详情" :visible.sync="showLogDetail" width="40%">
  108. <div class="logInfo">账号:{{ currDetailLog.operator_name }}</div>
  109. <div class="logInfo">操作描述:{{ currDetailLog.operate_detail }}</div>
  110. <div class="logInfo">请求方法:{{ currDetailLog.method }}</div>
  111. <div class="logInfo">请求参数:{{ currDetailLog.params }}</div>
  112. <div class="logInfo">ip地址:{{ currDetailLog.operate_ip }}</div>
  113. <div class="logInfo">日志级别:{{ currDetailLog.level === 'important' ? '重要' : '一般' }}</div>
  114. <div class="logInfo">操作时间:{{ currDetailLog.operate_time | unixToDate }}</div>
  115. </el-dialog> -->
  116. <el-dialog
  117. title="打印预览"
  118. :visible.sync="PrintVisible"
  119. width="940px"
  120. center
  121. @close="handleCancle"
  122. >
  123. <el-form :inline="true" class="print-options">
  124. <el-form-item label="打印规格">
  125. <el-select v-model="selectedSize" placeholder="请选择打印规格">
  126. <el-option label="大号 (9.5cm × 3.5cm)" value="large"></el-option>
  127. </el-select>
  128. </el-form-item>
  129. <el-form-item label="缩放">
  130. <el-input-number
  131. v-model="scale"
  132. :min="50"
  133. :max="150"
  134. :step="10"
  135. ></el-input-number>
  136. </el-form-item>
  137. <el-form-item label="打印方向">
  138. <el-select v-model="printOrientation" placeholder="请选择打印方向">
  139. <el-option label="纵向" value="portrait"></el-option>
  140. <el-option label="横向" value="landscape"></el-option>
  141. </el-select>
  142. </el-form-item>
  143. </el-form>
  144. <div class="print-preview-container">
  145. <div id="deliverySheet" :style="{ transform: `scale(${scale / 100})` }">
  146. <div class="tips-t" v-for="(item, index) in codeList" :key="index">
  147. <div class="content-wrapper">
  148. <div class="barcode-container">
  149. <svg :id="`barcode-${index}`"></svg>
  150. </div>
  151. <div class="numOrder">{{ String(item).toUpperCase() }}</div>
  152. </div>
  153. </div>
  154. </div>
  155. </div>
  156. <div slot="footer" class="dialog-footer">
  157. <el-button @click="handleCancle">取消</el-button>
  158. <el-button type="primary" @click="handlePrintGoodsLend">打印</el-button>
  159. </div>
  160. </el-dialog>
  161. </div>
  162. </template>
  163. <script>
  164. import * as API_Setting from "@/api/setting";
  165. import * as API_ProductClass from "@/api/productClass";
  166. import JsBarcode from "jsbarcode";
  167. import Sortable from "sortablejs";
  168. import Print from "print-js";
  169. export default {
  170. name: "systemLogs",
  171. data() {
  172. return {
  173. // 列表loading状态
  174. loading: false,
  175. // 列表参数
  176. params: {
  177. page_no: 1,
  178. page_size: 20,
  179. },
  180. // 列表数据
  181. tableData: "",
  182. // 时间输入框
  183. searchTime: "",
  184. // 是否显示日志详情弹窗
  185. showLogDetail: false,
  186. // 当前日志详情信息
  187. currDetailLog: {},
  188. // 高级搜索数据
  189. advancedForm: {},
  190. PrintVisible: false,
  191. codeList: [],
  192. barcodeValue: "",
  193. // 当前已选择的行
  194. multipleSelection: [],
  195. selectedSize: "large",
  196. scale: 100,
  197. codeList: [], // 假设这里存储了需要生成条形码的数据
  198. PrintVisible: false,
  199. printOrientation: "portrait", // 默认为纵向打印
  200. };
  201. },
  202. mounted() {
  203. this.GET_SystemLogs();
  204. this.rowDrop();
  205. },
  206. watch: {
  207. PrintVisible(newVal) {
  208. if (newVal) {
  209. this.$nextTick(() => {
  210. this.generateBarcodes();
  211. });
  212. }
  213. },
  214. },
  215. methods: {
  216. generateBarcodes() {
  217. this.$nextTick(() => {
  218. this.codeList.forEach((item, index) => {
  219. JsBarcode(`#barcode-${index}`, item, {
  220. format: "CODE128",
  221. width: 2,
  222. height: 60,
  223. displayValue: false,
  224. });
  225. });
  226. });
  227. },
  228. /* handlePrintGoodsLend() {
  229. this.generateBarcodes();
  230. this.$nextTick(() => {
  231. const printContent = document.getElementById("deliverySheet").innerHTML;
  232. const printWindow = window.open("", "_blank");
  233. printWindow.document.write(`
  234. <html>
  235. <head>
  236. <title>打印标签</title>
  237. <style>
  238. @page {
  239. size: 95mm 35mm;
  240. margin: 0;
  241. }
  242. body {
  243. margin: 0;
  244. width: 95mm;
  245. height: 35mm;
  246. }
  247. .tips-t {
  248. width: 95mm;
  249. height: 35mm;
  250. box-sizing: border-box;
  251. padding: 2mm;
  252. page-break-after: always;
  253. display: flex;
  254. flex-direction: column;
  255. justify-content: center;
  256. align-items: center;
  257. }
  258. .content-wrapper {
  259. display: flex;
  260. flex-direction: column;
  261. align-items: center;
  262. }
  263. .barcode-container {
  264. margin-bottom: 2mm;
  265. }
  266. .numOrder {
  267. font-size: 14px;
  268. }
  269. </style>
  270. </head>
  271. <body>${printContent}</body>
  272. </html>
  273. `);
  274. printWindow.document.close();
  275. printWindow.focus();
  276. printWindow.print();
  277. printWindow.close();
  278. });
  279. }, */
  280. handleCancle() {
  281. this.PrintVisible = false;
  282. },
  283. /** 多选改变 */
  284. handleSelectionChange(val) {
  285. this.multipleSelection = val;
  286. },
  287. // 批量打印
  288. batchPrint() {
  289. const ids = this.multipleSelection.map((item) => item.sku);
  290. this.codeList = ids;
  291. this.PrintVisible = true;
  292. },
  293. /** 打印对账详情 */
  294. handlePrintGoodsLend() {
  295. // 生成条形码
  296. this.generateBarcodes();
  297. // 准备打印样式
  298. const style = `
  299. @page {
  300. size: ${this.printOrientation};
  301. margin: 0;
  302. }
  303. body {
  304. margin: 0;
  305. }
  306. .tips-t {
  307. width: 95mm;
  308. height: 35mm;
  309. page-break-after: always;
  310. display: flex;
  311. flex-direction: column;
  312. justify-content: center;
  313. align-items: center;
  314. box-sizing: border-box;
  315. padding: 2mm;
  316. }
  317. .content-wrapper {
  318. display: flex;
  319. flex-direction: column;
  320. align-items: center;
  321. }
  322. .barcode-container {
  323. margin-bottom: 2mm;
  324. }
  325. .numOrder {
  326. font-size: 14px;
  327. }
  328. ${
  329. this.printOrientation !== "landscape"
  330. ? `
  331. .tips-t {
  332. transform: rotate(90deg);
  333. transform-origin: top left;
  334. width: 35mm;
  335. height: 95mm;
  336. position: absolute;
  337. left: 35mm;
  338. top: 0;
  339. }
  340. `
  341. : ""
  342. }
  343. `;
  344. // 使用 print-js 打印
  345. Print({
  346. printable: "deliverySheet",
  347. type: "html",
  348. css: style,
  349. style: style,
  350. scanStyles: false,
  351. targetStyles: ["*"],
  352. });
  353. },
  354. handleCancle() {
  355. this.PrintVisible = false;
  356. this.codeList = [];
  357. },
  358. // 打印
  359. PrintIng(row) {
  360. this.barcodeValue = row.sku;
  361. this.$nextTick(() => {
  362. this.codeList.push(row.sku);
  363. this.PrintVisible = true;
  364. });
  365. },
  366. // 行拖拽
  367. rowDrop() {
  368. const tbody = document.querySelector(
  369. ".draggable-table .el-table__body-wrapper tbody"
  370. );
  371. const _this = this;
  372. Sortable.create(tbody, {
  373. animation: 300,
  374. handle: ".drag-handle", // 只有拖拽图标可以触发拖拽
  375. onEnd({ newIndex, oldIndex }) {
  376. const currRow = _this.tableData.data.splice(newIndex, 1)[0];
  377. /* const sort = _this.tableData.data.splice(newIndex+1, 1)[0].sort; */
  378. _this.tableData.data.splice(newIndex, 0, currRow);
  379. /* console.log("拖拽后的行详情:", currRow); */
  380. //console.log(currRow,sort);
  381. _this.updateList(currRow.id, newIndex);
  382. },
  383. });
  384. },
  385. // 更新列表顺序
  386. updateList(id, newIndex) {
  387. const data = [
  388. {
  389. position: "own",
  390. product_id: this.tableData.data[newIndex].id,
  391. sort: this.tableData.data[newIndex].sort,
  392. },
  393. ];
  394. if (newIndex > 0) {
  395. // Check if there is a row before
  396. data.push({
  397. position: "before",
  398. product_id: this.tableData.data[newIndex - 1].id,
  399. sort: this.tableData.data[newIndex - 1].sort,
  400. });
  401. }
  402. if (newIndex < this.tableData.data.length - 1) {
  403. // Check if there is a row after
  404. data.push({
  405. position: "after",
  406. product_id: this.tableData.data[newIndex + 1].id,
  407. sort: this.tableData.data[newIndex + 1].sort,
  408. });
  409. }
  410. // 假设API需要商品ID和新的索引位置
  411. API_ProductClass.updateSort({ data: data })
  412. .then((res) => {
  413. this.$message.success("更新成功!");
  414. this.GET_SystemLogs();
  415. })
  416. .catch(() => {
  417. this.$message.error("更新失败!");
  418. });
  419. },
  420. /** 分页大小发生改变 */
  421. handlePageSizeChange(size) {
  422. this.params.page_size = size;
  423. this.GET_SystemLogs();
  424. },
  425. /** 分页页数发生改变 */
  426. handlePageCurrentChange(page) {
  427. this.params.page_no = page;
  428. this.GET_SystemLogs();
  429. },
  430. /** 搜索事件触发 */
  431. searchEvent(data) {
  432. this.params = {
  433. ...this.params,
  434. keyword: data,
  435. };
  436. Object.keys(this.advancedForm).forEach((key) => delete this.params[key]);
  437. this.params.page_no = 1;
  438. this.GET_SystemLogs();
  439. },
  440. /** 高级搜索事件触发 */
  441. advancedSearchEvent() {
  442. this.params = {
  443. ...this.params,
  444. ...this.advancedForm,
  445. };
  446. delete this.params.start_time;
  447. delete this.params.end_time;
  448. if (this.advancedForm.order_time_range) {
  449. this.params.start_time = this.advancedForm.order_time_range[0] / 1000;
  450. this.params.end_time = this.advancedForm.order_time_range[1] / 1000;
  451. }
  452. delete this.params.keyword;
  453. delete this.params.order_time_range;
  454. this.params.page_no = 1;
  455. this.GET_SystemLogs();
  456. },
  457. /** 获取日志列表 */
  458. GET_SystemLogs() {
  459. this.loading = true;
  460. const params = this.MixinClone(this.params);
  461. Object.keys(params).forEach((key) => {
  462. if (!params[key] && params[key] !== 0) {
  463. delete params[key];
  464. }
  465. });
  466. API_ProductClass.getProduct(params)
  467. .then((response) => {
  468. this.loading = false;
  469. this.tableData = response;
  470. this.params.page_no = response.page_no;
  471. this.params.page_size = response.page_size;
  472. })
  473. .catch(() => {
  474. this.loading = false;
  475. });
  476. },
  477. /** 显示日志详情 */
  478. handleShowDetail(row) {
  479. this.showLogDetail = true;
  480. this.currDetailLog = row;
  481. },
  482. },
  483. };
  484. </script>
  485. <style>
  486. .print {
  487. margin-left: 790px;
  488. }
  489. .inner-toolbar2 {
  490. justify-content: flex-start !important;
  491. }
  492. .inner-toolbar2 .dateSearch {
  493. margin-right: 20px;
  494. }
  495. .toolbar-search {
  496. display: flex;
  497. width: 400px;
  498. }
  499. .toolbar-search .el-button {
  500. margin-left: 18px;
  501. }
  502. </style>
  503. <style type="text/scss" lang="scss" scoped>
  504. .nc-container {
  505. height: 32px !important;
  506. overflow: hidden;
  507. }
  508. .drag-handle {
  509. cursor: move;
  510. /* 设置鼠标为拖拽样式 */
  511. }
  512. .face-image {
  513. display: block;
  514. width: 50px;
  515. height: 50px;
  516. margin: 0 auto;
  517. }
  518. /deep/ .el-dialog__body {
  519. padding: 10px 20px;
  520. }
  521. /deep/ .img-code {
  522. width: 80%;
  523. .el-input-group__append {
  524. padding: 0;
  525. margin: 0;
  526. img {
  527. display: block;
  528. height: 30px;
  529. cursor: pointer;
  530. }
  531. }
  532. }
  533. .dateSearch {
  534. }
  535. .logInfo {
  536. margin-bottom: 20px;
  537. }
  538. .print-preview-container {
  539. overflow: auto;
  540. width: 100%;
  541. height: 400px;
  542. display: flex;
  543. justify-content: center;
  544. align-items: flex-start;
  545. &.portrait {
  546. #deliverySheet {
  547. display: flex;
  548. flex-direction: column;
  549. align-items: center;
  550. }
  551. .tips-t {
  552. width: 95mm;
  553. height: 35mm;
  554. }
  555. }
  556. &.landscape {
  557. #deliverySheet {
  558. display: flex;
  559. flex-wrap: wrap;
  560. justify-content: flex-start;
  561. }
  562. .tips-t {
  563. width: 35mm;
  564. height: 95mm;
  565. margin-right: 2mm;
  566. }
  567. }
  568. }
  569. #deliverySheet {
  570. transform-origin: top left;
  571. }
  572. .tips-t {
  573. border: 1px solid #ccc;
  574. margin-bottom: 2mm;
  575. padding: 2mm;
  576. box-sizing: border-box;
  577. display: flex;
  578. flex-direction: column;
  579. justify-content: center;
  580. align-items: center;
  581. .content-wrapper {
  582. width: 100%;
  583. height: 100%;
  584. display: flex;
  585. flex-direction: column;
  586. justify-content: center;
  587. align-items: center;
  588. }
  589. .numOrder {
  590. font-size: 14px;
  591. margin-top: 1mm;
  592. }
  593. .barcode-container {
  594. width: 100%;
  595. display: flex;
  596. justify-content: center;
  597. margin-bottom: 2mm;
  598. svg {
  599. max-width: 100%;
  600. height: auto;
  601. }
  602. }
  603. }
  604. #deliverySheet {
  605. transform-origin: top left;
  606. padding: 10mm;
  607. }
  608. #deliverySheet.portrait .tips-t {
  609. width: 95mm;
  610. height: 35mm;
  611. }
  612. #deliverySheet.landscape .tips-t {
  613. width: 35mm;
  614. height: 95mm;
  615. }
  616. .dialog-footer {
  617. text-align: right;
  618. margin-top: 20px;
  619. }
  620. @media print {
  621. @page {
  622. size: auto;
  623. margin: 0;
  624. }
  625. body {
  626. margin: 0;
  627. }
  628. .print-options,
  629. .dialog-footer {
  630. display: none;
  631. }
  632. #deliverySheet {
  633. transform: none !important;
  634. }
  635. .tips-t {
  636. page-break-inside: avoid;
  637. margin-bottom: 0;
  638. }
  639. }
  640. </style>