index.vue 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547
  1. <!-- 外协结算前审核 -->
  2. <template>
  3. <div class="page-container column-container">
  4. <!-- 搜索区 -->
  5. <el-form class="list-search-container" :model="queryParams" ref="queryRef" :inline="true">
  6. <!-- <el-form-item label="检查日期:" prop="startTime">
  7. <el-input v-model="queryParams.startTime" placeholder="" style="width: 144px" clearable
  8. @keyup.enter="handleQuery" />
  9. <el-date-picker v-model="queryParams.startTime" type="date" placeholder="选择日期" style="width: 144px" clearable
  10. @keyup.enter="handleQuery" value-format="YYYY-MM-DD" />
  11. </el-form-item> -->
  12. <el-form-item label="外协商名称:" prop="supplierName">
  13. <el-input v-model="queryParams.supplierName" placeholder="请输入外协商名称" style="width: 144px" clearable
  14. @keyup.enter="handleQuery" />
  15. </el-form-item>
  16. <el-form-item label="批次号:" prop="lotCode">
  17. <el-input v-model.trim="queryParams.lotCode" placeholder="请输入批次号" style="width: 144px" clearable
  18. @keyup.enter="handleQuery" />
  19. </el-form-item>
  20. <el-form-item label="发出单号:" prop="outsourcedOrderDetailFormCode">
  21. <el-input v-model.trim="queryParams.outsourcedOrderDetailFormCode" placeholder="请输入发出单号" style="width: 144px"
  22. clearable @keyup.enter="handleQuery" />
  23. </el-form-item>
  24. <el-form-item label="收回单号:" prop="returnReceiptDetailFormCode">
  25. <el-input v-model.trim="queryParams.returnReceiptDetailFormCode" placeholder="请输入收回单号" style="width: 144px"
  26. clearable @keyup.enter="handleQuery" />
  27. </el-form-item>
  28. <el-form-item label="检查日期:">
  29. <el-date-picker v-model="queryParams.startTime" type="date" value-format="YYYY-MM-DD" :editable="false"
  30. :clearable="false" placeholder="请选择开始时间" style="width: 130px" @change="handleDateChange" />
  31. <span>到</span>
  32. <el-date-picker v-model="queryParams.endTime" type="date" value-format="YYYY-MM-DD" :editable="false"
  33. :clearable="false" placeholder="请选择结束时间" style="width: 130px" @change="handleDateChange" />
  34. </el-form-item>
  35. <el-form-item label="状态:" prop="auditStatus">
  36. <el-select v-model="queryParams.auditStatus" style="width: 144px" clearable placeholder="请选择">
  37. <el-option v-for="dict in outsource_before_checkout_verify_status" :key="dict.value" :label="dict.label"
  38. :value="dict.value" />
  39. </el-select>
  40. </el-form-item>
  41. <el-form-item>
  42. <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
  43. <el-button icon="Refresh" @click="resetQuery">重置</el-button>
  44. <el-button type="primary" icon="Check" :disabled="ids && ids.length == 0" @click="handlePass"
  45. :loading="confirmLoading">批量通过</el-button>
  46. <el-button type="danger" icon="Close" :disabled="ids && ids.length == 0" @click="handleFail">批量不通过</el-button>
  47. <el-button type="primary" @click="exportForYear">导出审核列表</el-button>
  48. <!-- <el-button v-if="exportSuccess" type="warning" icon="Download" @click="downloadExcel">下载导出excel</el-button>
  49. <el-progress v-if="doingExport" class="export-progress" :percentage="exportProgress" />
  50. <el-button v-if="doingExport && !exportSuccess" type="danger">停止导出</el-button> -->
  51. </el-form-item>
  52. </el-form>
  53. <!-- 功能按钮区 -->
  54. <!-- <div class="list-btns-container">
  55. </div> -->
  56. <!-- 渲染数据区 -->
  57. <div class="el-table-container">
  58. <div class="el-table-inner-container">
  59. <el-table v-loading="loading" :data="orderList" size="small" border height="100%"
  60. @selection-change="handleSelectionChange">
  61. <el-table-column type="selection" width="48" align="center" />
  62. <el-table-column label="外协单位" align="center" prop="outsourcedOrderDetail.supplierName" width="150" />
  63. <el-table-column label="批次号" align="center" prop="lotCode" width="100" />
  64. <el-table-column label="产品描述" align="center" prop="productDescription" width="320" />
  65. <!-- <el-table-column label="检查单号" align="center" prop="outsourcedInspectionNo" width="120" /> -->
  66. <el-table-column label="检查状态" align="center" prop="status" width="120">
  67. <template #default="scope">
  68. <dict-tag :options="process_inspection_status" :value="scope.row.status" />
  69. </template>
  70. </el-table-column>
  71. <el-table-column label="外协工序" align="center" prop="processNames" width="120" />
  72. <el-table-column label="外协日期" align="center" prop="outsourcedOrderDetail.formDate" width="120">
  73. <template #default="scope">
  74. {{
  75. parseTime(
  76. scope.row.outsourcedOrderDetail.formDate,
  77. "{y}-{m}-{d}"
  78. )
  79. }}
  80. </template>
  81. </el-table-column>
  82. <el-table-column label="发出单号" align="center" prop="outsourcedOrderDetail.formCode" width="120" />
  83. <el-table-column label="发出量" align="center" prop="outsourcedOrderDetail.productNum" width="120" />
  84. <el-table-column label="收回日期" align="center" prop="returnReceiptDetail.formDate" width="120" />
  85. <el-table-column label="收回单号" align="center" prop="returnReceiptDetail.formCode" width="120" />
  86. <el-table-column label="收回量" align="center" prop="returnReceiptDetail.auditNum" width="120" />
  87. <el-table-column label="状态" align="center" prop="auditStatus" width="120">
  88. <template #default="scope">
  89. <dict-tag :options="outsource_before_checkout_verify_status" :value="scope.row.auditStatus" />
  90. </template>
  91. </el-table-column>
  92. <el-table-column label="检查日期" align="center" prop="startTime" width="120">
  93. <template #default="scope">
  94. {{ parseTime(scope.row.startTime, "{y}-{m}-{d}") }}
  95. </template>
  96. </el-table-column>
  97. <el-table-column label="备注" align="center" prop="remark" width="120" show-overflow-tooltip />
  98. <el-table-column fixed="right" label="操作" align="center" class-name="small-padding fixed-width" width="200">
  99. <template #default="scope">
  100. <el-button v-if="scope.row.auditStatus == 0 || scope.row.auditStatus == 2" :loading="confirmLoading" link
  101. type="warning" icon="Check" @click="handleConfirm(scope.row)"
  102. v-hasPermi="['business:beforeCheckoutVerify:edit']">
  103. 通过
  104. </el-button>
  105. <el-button v-if="scope.row.auditStatus == 0 || scope.row.auditStatus == 1" link type="primary"
  106. icon="Close" @click="handleReject(scope.row)" v-hasPermi="['business:beforeCheckoutVerify:edit']">
  107. 不通过
  108. </el-button>
  109. </template>
  110. </el-table-column>
  111. </el-table>
  112. </div>
  113. </div>
  114. <!-- 分页 -->
  115. <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum"
  116. v-model:limit="queryParams.pageSize" @pagination="getList" />
  117. <el-dialog v-model="doingExport" title="导出审核列表" width="500px" height="400px" @close="close" append-to-body draggable
  118. :close-on-click-modal="false" :show-close="!doingExport || exportSuccess">
  119. <div v-if="doingExport && !exportSuccess"
  120. style="color: red;font-weight: 600;width:100%; text-align:center;height:40px;display: inline-block;line-height: 40px;">
  121. 导出中请勿关闭</div>
  122. <div v-else
  123. style="color: red;font-weight: 600;width:100%; text-align:center;height:40px;display: inline-block;line-height: 40px;">
  124. 导出完成</div>
  125. <div class="form-btns-container" style="width: 100%;">
  126. <el-progress v-if="doingExport" class="export-progress" :percentage="exportProgress" style="width: 100%;"
  127. :text-inside="true" />
  128. <div style="width: 100%; align-items: center; align-content: center;height: 60px;position: relative;">
  129. <el-button v-if="exportSuccess" type="warning" icon="Download" @click="downloadExcel"
  130. style="position: absolute; top: 0px; left: 50%; transform: translateX(-50%);">下载导出excel</el-button>
  131. <el-button v-if="doingExport && !exportSuccess" type="danger" @click="close"
  132. style="position: absolute; top: 0px; left: 50%; transform: translateX(-50%);">停止导出</el-button>
  133. </div>
  134. </div>
  135. </el-dialog>
  136. </div>
  137. </template>
  138. <script setup name="Order">
  139. import {
  140. listCheckoutInspection,
  141. rejectInspection,
  142. confirmInspection,
  143. checkFurnace,
  144. checkMultiplyFurnace,
  145. exportAsync,
  146. getExportCount
  147. } from "@/api/business/beforeCheckoutVerify";
  148. import { nextTick } from "vue";
  149. import { getToken } from '@/utils/auth'
  150. import useUserStore from '@/store/modules/user'
  151. import { patternNginxExportWsUrl } from "../../utils/ws"
  152. const { proxy } = getCurrentInstance();
  153. /** 字典数组区 */
  154. const { outsource_before_checkout_verify_status } = proxy.useDict(
  155. "outsource_before_checkout_verify_status"
  156. );
  157. const { process_inspection_status } = proxy.useDict(
  158. "process_inspection_status"
  159. );
  160. const orderList = ref([]);
  161. const loading = ref(true);
  162. const ids = ref([]);
  163. const single = ref(true);
  164. const multiple = ref(true);
  165. const total = ref(0);
  166. const selections = ref([])
  167. const printCarriers = ref([]);
  168. const confirmLoading = ref(false)
  169. const exportSuccess = ref(false)
  170. const doingExport = ref(false)
  171. const exportProgress = ref(0)
  172. const exportUrl = ref(null)
  173. // const wsUrl = 'ws://localhost:8081/wsExport/'
  174. const innetUrl = import.meta.env.VITE_WS_INNET_EXPORT_API
  175. const outnetUrl = import.meta.env.VITE_WS_OUTNET_EXPORT_API
  176. const hostIp = import.meta.env.VITE_HOST_IP;
  177. // const wsUrl = import.meta.env.VITE_WS_EXPORT_API
  178. let ws = null
  179. const webHost = import.meta.env.VITE_APP_BASE_API;
  180. /** 查询对象 */
  181. const queryParams = ref({
  182. pageNum: 1,
  183. pageSize: 10,
  184. formCode: null,
  185. formDate: null,
  186. endTime: proxy.moment().format("YYYY-MM-DD"),
  187. startTime: proxy.moment().subtract(1, "month").format("YYYY-MM-DD"),
  188. supplierName: null,
  189. deliveryMethod: null,
  190. packagingMethod: null,
  191. freightPrice: null,
  192. freightAmount: null,
  193. isFirstProcess: 0,
  194. });
  195. /*********************** 方法区 ****************************/
  196. /** 查询外协单主 带箱方式,是整单的。如果换新箱子,明细中,都需要更换箱子列表 */
  197. function getList() {
  198. loading.value = true;
  199. listCheckoutInspection(queryParams.value).then((response) => {
  200. orderList.value = response.rows;
  201. total.value = response.total;
  202. loading.value = false;
  203. });
  204. }
  205. /** 搜索按钮操作 */
  206. function handleQuery() {
  207. console.log(queryParams.value);
  208. queryParams.value.pageNum = 1;
  209. getList();
  210. }
  211. /** 重置按钮操作 */
  212. function resetQuery() {
  213. queryParams.value.endTime = proxy.moment().format("YYYY-MM-DD"),
  214. queryParams.value.startTime = proxy.moment().subtract(1, "month").format("YYYY-MM-DD"),
  215. proxy.resetForm("queryRef");
  216. handleQuery();
  217. }
  218. //批量通过
  219. function handlePass() {
  220. loading.value = true
  221. checkMultiplyFurnace(selections.value).then((res) => {
  222. if (res.code === 200) {
  223. if (res.data.length >= 1) {
  224. proxy
  225. .$confirm("是否确认通过?", "提示", {
  226. confirmButtonText: "确定",
  227. cancelButtonText: "取消",
  228. type: "warning",
  229. })
  230. .then(() => {
  231. confirmInspection(res.data).then((res) => {
  232. getList()
  233. loading.value = false
  234. }).catch(e => {
  235. loading.value = false
  236. })
  237. })
  238. .catch(_ => { loading.value = false })
  239. } else {
  240. proxy
  241. .$confirm("该炉已全部质检,是否全部确认通过?", "提示", {
  242. confirmButtonText: "确定",
  243. cancelButtonText: "取消",
  244. type: "warning",
  245. })
  246. .then(() => {
  247. confirmInspection(res.data).then((res) => {
  248. getList();
  249. loading.value = false
  250. }).catch(e => {
  251. loading.value = false
  252. })
  253. }).catch(e => {
  254. loading.value = false
  255. })
  256. }
  257. } else {
  258. proxy.$msgError(res.msg)
  259. loading.value = false
  260. }
  261. }).catch(e => {
  262. loading.value = false
  263. })
  264. }
  265. //批量不通过
  266. function handleFail() {
  267. checkMultiplyFurnace(selections.value).then((res) => {
  268. if (res.code === 200) {
  269. proxy
  270. .$confirm("是否确认不通过?", "提示", {
  271. confirmButtonText: "确定",
  272. cancelButtonText: "取消",
  273. type: "warning",
  274. })
  275. .then(() => {
  276. rejectInspection(res.data).then((res) => {
  277. getList();
  278. });
  279. })
  280. .catch(_ => ({}))
  281. }
  282. });
  283. }
  284. // 多选框选中数据
  285. function handleSelectionChange(selection) {
  286. selections.value = selection;
  287. ids.value = selection.map((item) => item.id);
  288. single.value = selection.length != 1;
  289. multiple.value = !selection.length;
  290. }
  291. function handleConfirm(row) {
  292. loading.value = true
  293. checkFurnace(row).then((res) => {
  294. if (res.code === 200) {
  295. if (res.data.length === 1) {
  296. proxy
  297. .$confirm("是否确认通过?", "提示", {
  298. confirmButtonText: "确定",
  299. cancelButtonText: "取消",
  300. type: "warning",
  301. })
  302. .then(() => {
  303. confirmInspection(res.data).then((res) => {
  304. getList();
  305. loading.value = false
  306. }).catch(e => {
  307. loading.value = false
  308. });
  309. })
  310. .catch(e => {
  311. loading.value = false
  312. })
  313. } else {
  314. proxy
  315. .$confirm("该炉已全部质检,是否全部确认通过?", "提示", {
  316. confirmButtonText: "确定",
  317. cancelButtonText: "取消",
  318. type: "warning",
  319. })
  320. .then(() => {
  321. confirmInspection(res.data).then((res) => {
  322. loading.value = false
  323. getList();
  324. }).catch(e => {
  325. loading.value = false
  326. })
  327. })
  328. .catch(e => {
  329. loading.value = false
  330. });
  331. }
  332. } else {
  333. proxy.$msgError(res.msg);
  334. loading.value = false
  335. }
  336. }).catch(err => {
  337. loading.value = false
  338. })
  339. }
  340. function handleDateChange() {
  341. console.log(queryParams.value.startTime);
  342. console.log(queryParams.value.endTime);
  343. }
  344. function handleReject(row) {
  345. checkFurnace(row).then((res) => {
  346. if (res.code === 200) {
  347. proxy
  348. .$confirm("是否确认不通过?", "提示", {
  349. confirmButtonText: "确定",
  350. cancelButtonText: "取消",
  351. type: "warning",
  352. })
  353. .then(() => {
  354. rejectInspection(res.data).then((res) => {
  355. getList();
  356. });
  357. });
  358. }
  359. });
  360. }
  361. /** 异步导出excel */
  362. async function exportForYear() {
  363. // 导出前提示
  364. const res = await getExportCount(queryParams.value)
  365. console.log(res)
  366. const num = res.data
  367. const confirm = await proxy.$modal.confirm(`共${num}条数据是否确认导出?`).then(_ => true).catch(_ => false)
  368. if (confirm) {
  369. if (ws == null) {
  370. // 启动一个websocket
  371. console.log(useUserStore().user.userId)
  372. createWebSocket(useUserStore().user.userId, getToken()).then(_ => {
  373. console.log(ws)
  374. initWebsocket()
  375. })
  376. } else {
  377. initWebsocket()
  378. }
  379. // 发送请求要求导出excel
  380. exportAsync(queryParams.value).then(res => {
  381. console.log(res)
  382. if (res.code == 200) {
  383. doingExport.value = true
  384. }
  385. })
  386. }
  387. }
  388. async function createWebSocket(id, token) {
  389. try {
  390. // const options = { headers: { Authorization: 'Bearer ' + token } }
  391. // console.log(options)
  392. var itemUrl = document.location.origin;
  393. var wsUrl = patternNginxExportWsUrl();
  394. // if (itemUrl.includes(hostIp)) {
  395. // wsUrl = innetUrl;
  396. // } else {
  397. // wsUrl = outnetUrl;
  398. // }
  399. ws = new WebSocket(wsUrl + id)
  400. } catch (e) {
  401. console.log(e)
  402. reconnection()
  403. }
  404. }
  405. let lockReconnect = false
  406. let timerReconnect = undefined
  407. let timerHeart = undefined
  408. let timerServerHeart = undefined
  409. const heartTimeOut = 40000
  410. let handClose = false
  411. function reconnection() {
  412. console.log("重新连接")
  413. if (lockReconnect) {
  414. return
  415. }
  416. lockReconnect = true
  417. if (timerReconnect) {
  418. clearTimer(timerReconnect)
  419. }
  420. //没连上会一直重连, 设置迟延,避免请求过多
  421. timerReconnect = setTimeout(() => {
  422. //setTimeout 到点了执行
  423. connection(useUserStore().user.userId, getToken())
  424. lockReconnect = false
  425. }, 5000);
  426. }
  427. async function connection(id, token) {
  428. await createWebSocket(id, token)
  429. }
  430. function clearTimer() {
  431. timerReconnect && clearTimeout(timerReconnect)
  432. timerHeart && clearTimeout(timerHeart)
  433. timerServerHeart && clearTimeout(timerServerHeart)
  434. }
  435. function initWebsocket() {
  436. ws.onopen = (e) => {
  437. ws.send("open server")
  438. console.log("链接成功")
  439. heartCheck()
  440. }
  441. ws.onmessage = (message) => {
  442. // console.log(message)
  443. const data = JSON.parse(message.data)
  444. exportProgress.value = data.progress
  445. if (data.url != null) {
  446. exportSuccess.value = true
  447. exportUrl.value = data.url
  448. closeWs()
  449. downloadExcel()
  450. }
  451. heartCheck()
  452. }
  453. ws.onerror = (e) => {
  454. console.log(e)
  455. console.log('链接失败')
  456. reconnection()
  457. }
  458. ws.onclose = (e) => {
  459. console.log("关闭连接")
  460. if (!handClose) {
  461. reconnection()
  462. }
  463. }
  464. }
  465. function sentMessage(message) {
  466. ws.send(message)
  467. }
  468. function closeWs() {
  469. handClose = true
  470. clearTimer()
  471. if (ws != null) {
  472. ws.close()
  473. ws = null
  474. }
  475. }
  476. function heartCheck() {
  477. console.log('心跳检测')
  478. if (timerHeart) {
  479. clearTimeout(timerHeart)
  480. }
  481. timerHeart = setTimeout(() => {
  482. // console.log("ping")
  483. ws.send("ping")
  484. lockReconnect = false
  485. }, heartTimeOut)
  486. }
  487. function downloadExcel() {
  488. // 下载文件
  489. window.open(webHost + exportUrl.value)
  490. }
  491. function close() {
  492. closeWs()
  493. doingExport.value = false
  494. exportSuccess.value = false
  495. exportProgress.value = 0
  496. }
  497. onUnmounted(() => {
  498. if (ws != null) {
  499. closeWs()
  500. }
  501. })
  502. getList();
  503. </script>
  504. <style>
  505. /* .export-progress .el-progress-bar {
  506. margin-bottom: 15px;
  507. width: 100px;
  508. } */
  509. .export-progress .el-progress-bar__outer {
  510. height: 20px !important;
  511. }
  512. .export-progress {
  513. /* margin-left: 6px;
  514. margin-right: 6px; */
  515. padding-left: 10px;
  516. padding-right: 10px;
  517. margin-bottom: 40px;
  518. }
  519. </style>