FeedingArea.vue 50 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038
  1. <!-- 上料区管理 -->
  2. <template>
  3. <div class="feeding-container">
  4. <!-- 动态背景 -->
  5. <div class="tech-background">
  6. <div class="tech-grid" />
  7. <div class="tech-gradient" />
  8. <!-- <div class="tech-particles">
  9. <div v-for="i in 15" :key="i" class="particle" :style="getParticleStyle(i)" />
  10. </div> -->
  11. </div>
  12. <!-- 顶部导航 -->
  13. <header class="tech-header">
  14. <div class="header-left">
  15. <div class="logo-box" style="cursor: pointer;" @click="goBack">
  16. <i class="fas fa-warehouse" />
  17. </div>
  18. <!-- <div class="logo-text">上料区</div> -->
  19. </div>
  20. <div class="header-center">
  21. <h1 class="system-title">料箱上架</h1>
  22. </div>
  23. <div class="header-right">
  24. <!-- <div class="date-display">{{ currentDate }}</div> -->
  25. </div>
  26. </header>
  27. <!-- 主内容区域 -->
  28. <main class="tech-main">
  29. <!-- 操作按钮区域 -->
  30. <div class="action-cards">
  31. <!-- 空料箱上架 -->
  32. <div class="action-card" @click="handlePlaceEmptyBin(false)">
  33. <!-- <div class="card-border" /> -->
  34. <!-- <div class="card-glow" /> -->
  35. <div class="card-content">
  36. <div class="card-icon green">
  37. <i class="fas fa-arrow-up-from-bracket" />
  38. </div>
  39. <div class="card-label">空料箱上架</div>
  40. <!-- <div class="card-decoration">
  41. <div class="deco-line" />
  42. <div class="deco-corner" />
  43. </div> -->
  44. </div>
  45. </div>
  46. <!-- 请求空料箱 -->
  47. <div class="action-card" @click="handleRequestEmptyBin(false)">
  48. <div class="card-content">
  49. <div class="card-icon blue">
  50. <i class="fas fa-box-open" />
  51. </div>
  52. <div class="card-label">请求空料箱</div>
  53. </div>
  54. </div>
  55. <!-- 请求校验 -->
  56. <div class="action-card" @click="handleRequestVerification(false)">
  57. <div class="card-content">
  58. <div class="card-icon orange">
  59. <i class="fas fa-magnifying-glass-chart" />
  60. </div>
  61. <div class="card-label">工装/设备上架</div>
  62. </div>
  63. </div>
  64. <!-- 成品上架 -->
  65. <div class="action-card" @click="handleProductShelf(false)">
  66. <div class="card-content">
  67. <div class="card-icon purple">
  68. <i class="fas fa-boxes-packing" />
  69. </div>
  70. <div class="card-label">成品上架</div>
  71. </div>
  72. </div>
  73. <!-- 成品下架 -->
  74. <div class="action-card" @click="handleProductOffShelf">
  75. <div class="card-content">
  76. <div class="card-icon red">
  77. <i class="fas fa-dolly" />
  78. </div>
  79. <div class="card-label">成品下架</div>
  80. </div>
  81. </div>
  82. </div>
  83. </main>
  84. <!-- 识别结果弹窗 - 科技感风格 -->
  85. <div v-if="showResultDialog" class="tech-modal-overlay" @click.self="closeDialog">
  86. <div class="tech-modal result-modal">
  87. <!-- 弹窗标题 -->
  88. <div class="modal-header">
  89. <h3>{{ dialogTitle }}</h3>
  90. <button class="close-btn" @click="closeDialog">
  91. <i class="fas fa-times" />
  92. </button>
  93. </div>
  94. <div class="dialog-content">
  95. <!-- 状态标签 -->
  96. <div v-if="showVerificationStatus" class="verification-status">
  97. <span class="status-tag" :class="verificationSuccess ? 'success' : 'danger'">
  98. <i :class="verificationSuccess ? 'fas fa-check-circle' : 'fas fa-times-circle'" />
  99. {{ verificationSuccess ? '校验通过' : '校验未通过' }}
  100. </span>
  101. <span v-if="errorResult" class="status-tag danger">
  102. {{ errorResult }}
  103. </span>
  104. </div>
  105. <!-- 双栏布局 -->
  106. <div class="dialog-body">
  107. <!-- 左侧:料箱列表 -->
  108. <div class="list-section">
  109. <h3 class="section-title">
  110. <i class="fas fa-boxes" />
  111. 料箱信息
  112. </h3>
  113. <div class="item-list">
  114. <div v-for="bin in binList" :key="bin.id" class="item-card">
  115. <div class="item-header">
  116. <div class="item-info">
  117. <div class="item-id">{{ bin.feedBoxName }}</div>
  118. </div>
  119. <span class="item-tag">{{ bin.feedBoxNo }}</span>
  120. </div>
  121. </div>
  122. </div>
  123. </div>
  124. <!-- 右侧:工装/设备列表 或 校验结果 -->
  125. <template v-if="!isRequest">
  126. <div class="list-section">
  127. <h3 class="section-title">
  128. <i class="fas fa-tools" />
  129. 工装 / 设备信息
  130. </h3>
  131. <div class="item-list">
  132. <div v-for="tool in toolList" :key="tool.id" class="item-card">
  133. <div class="item-info">
  134. <div class="item-name">{{ tool.inventoryName }}</div>
  135. <div class="item-id">编号: {{ tool.inventoryNo }}</div>
  136. </div>
  137. </div>
  138. </div>
  139. </div>
  140. </template>
  141. <template v-else>
  142. <div class="verification-result">
  143. <div class="result-header">
  144. <i class="fas fa-check-circle" />
  145. <h4>请求成功</h4>
  146. </div>
  147. <p class="result-message">请求空料箱【{{ binList[0]?.feedBoxName }}】成功</p>
  148. </div>
  149. </template>
  150. </div>
  151. </div>
  152. <!-- 底部按钮 -->
  153. <div class="dialog-footer">
  154. <button v-if="showVerificationStatus" class="modal-btn primary-btn" @click="handleReverify">
  155. <i class="fas fa-sync-alt" />
  156. 重新校验
  157. </button>
  158. <button class="modal-btn close-action-btn" @click="closeDialog">
  159. 关闭
  160. </button>
  161. </div>
  162. </div>
  163. </div>
  164. </div>
  165. <!-- 成品上架弹窗 -->
  166. <div v-if="showProductShelfDialog" class="tech-modal-overlay" @click.self="closeProductShelfDialog">
  167. <div class="tech-modal product-modal">
  168. <div class="modal-header">
  169. <h3>成品上架</h3>
  170. <button class="close-btn" @click="closeProductShelfDialog">
  171. <i class="fas fa-times" />
  172. </button>
  173. </div>
  174. <div class="dialog-content">
  175. <!-- 状态标签 -->
  176. <div class="verification-status">
  177. <span class="status-tag" :class="productShelfSuccess ? 'success' : 'danger'">
  178. <i :class="productShelfSuccess ? 'fas fa-check-circle' : 'fas fa-times-circle'" />
  179. {{ productShelfSuccess ? '校验通过' : '校验未通过' }}
  180. </span>
  181. <span v-if="productShelfError" class="status-tag danger">
  182. {{ productShelfError }}
  183. </span>
  184. </div>
  185. <!-- 可滚动内容区域:包含双栏 + 已选成品 -->
  186. <div class="dialog-main">
  187. <!-- 双栏布局 -->
  188. <div class="dialog-body">
  189. <!-- 左侧:料箱列表 -->
  190. <div class="list-section">
  191. <h3 class="section-title">
  192. <i class="fas fa-boxes" />
  193. 料箱信息
  194. </h3>
  195. <div class="item-list">
  196. <div v-for="bin in productShelfBinList" :key="bin.feedBoxId" class="item-card">
  197. <div class="item-header">
  198. <div class="item-info">
  199. <div class="item-id">{{ bin.feedBoxName }}</div>
  200. </div>
  201. <span class="item-tag">{{ bin.feedBoxNo }}</span>
  202. </div>
  203. </div>
  204. <div v-if="productShelfBinList.length === 0" class="empty-tip">
  205. 暂无料箱信息
  206. </div>
  207. </div>
  208. </div>
  209. <!-- 右侧:根据校验状态显示不同内容 -->
  210. <div class="list-section">
  211. <template v-if="!productShelfSuccess">
  212. <!-- 校验未通过时显示工装设备信息 -->
  213. <h3 class="section-title">
  214. <i class="fas fa-tools" />
  215. 工装 / 设备信息
  216. </h3>
  217. <div class="item-list">
  218. <div v-for="tool in productShelfToolList" :key="tool.inventoryNo" class="item-card">
  219. <div class="item-info">
  220. <div class="item-name">{{ tool.inventoryName }}</div>
  221. <div class="item-id">编号: {{ tool.inventoryNo }}</div>
  222. </div>
  223. </div>
  224. <div v-if="productShelfToolList.length === 0" class="empty-tip">
  225. 暂无工装/设备信息
  226. </div>
  227. </div>
  228. </template>
  229. <template v-else>
  230. <!-- 校验通过时显示成品列表,可选择 -->
  231. <h3 class="section-title">
  232. <i class="fas fa-box" />
  233. 成品信息(点击选择/取消)
  234. </h3>
  235. <div class="item-list">
  236. <div
  237. v-for="product in productShelfProductList" :key="product.productId" class="item-card selectable"
  238. :class="{ selected: isProductSelected(product.productId) }"
  239. @click="toggleProductSelection(product)"
  240. >
  241. <div class="item-header">
  242. <div class="item-info">
  243. <div class="item-name">{{ product.productName }}</div>
  244. <div class="item-id">编号: {{ product.productNo }}</div>
  245. </div>
  246. <span v-if="isProductSelected(product.productId)" class="selected-icon">
  247. <i class="fas fa-check-circle" />
  248. </span>
  249. </div>
  250. </div>
  251. <div v-if="productShelfProductList.length === 0" class="empty-tip">
  252. 暂无成品信息
  253. </div>
  254. </div>
  255. </template>
  256. </div>
  257. </div>
  258. <!-- 已选成品区域 -->
  259. <!-- <div v-if="productShelfSuccess && selectedProducts.length > 0" class="selected-section">
  260. <h3 class="section-title">
  261. <i class="fas fa-clipboard-check" />
  262. 已选成品({{ selectedProducts.length }})
  263. </h3>
  264. <div class="selected-list">
  265. <div v-for="product in selectedProducts" :key="product.productId" class="selected-item">
  266. <span class="selected-name">{{ product.productName }}</span>
  267. <span class="selected-no">{{ product.productNo }}</span>
  268. <button class="remove-btn" @click="removeSelectedProduct(product.productId)">
  269. <i class="fas fa-times" />
  270. </button>
  271. </div>
  272. </div>
  273. </div> -->
  274. </div>
  275. <!-- 底部按钮 -->
  276. <div class="dialog-footer">
  277. <button class="modal-btn primary-btn" @click="handleProductShelfReverify">
  278. <i class="fas fa-sync-alt" />
  279. 重新校验
  280. </button>
  281. <button v-if="!isEmptySubmit" class="modal-btn submit-btn" @click="submitProductShelf">
  282. <i class="fas fa-arrow-up-from-bracket" />
  283. 成品上架
  284. </button>
  285. <button v-else class="modal-btn submit-btn" @click="submitProductShelfEmpty">
  286. <i class="fas fa-arrow-up-from-ground-water" />
  287. 空料箱上架
  288. </button>
  289. </div>
  290. </div>
  291. </div>
  292. </div>
  293. <!-- 成品下架弹窗 -->
  294. <div v-if="showProductOffShelfDialog" class="tech-modal-overlay" @click.self="closeProductOffShelfDialog">
  295. <div class="tech-modal product-modal">
  296. <div class="modal-header">
  297. <h3>成品下架</h3>
  298. <button class="close-btn" @click="closeProductOffShelfDialog">
  299. <i class="fas fa-times" />
  300. </button>
  301. </div>
  302. <div class="dialog-content">
  303. <!-- 可滚动内容区域:包含双栏 -->
  304. <div class="dialog-main">
  305. <!-- 双栏布局 -->
  306. <div class="dialog-body">
  307. <!-- 左侧:成品列表 -->
  308. <div class="list-section">
  309. <h3 class="section-title">
  310. <i class="fas fa-box" />
  311. 成品列表(点击选择)
  312. </h3>
  313. <div class="item-list">
  314. <div
  315. v-for="product in offShelfProductList" :key="product.id" class="item-card selectable"
  316. :class="{ selected: isOffShelfProductSelected(product.id) }"
  317. @click="toggleOffShelfProductSelection(product)"
  318. >
  319. <div class="item-header">
  320. <div class="item-info">
  321. <div class="item-name">{{ product.name }}</div>
  322. <div class="item-id">编号: {{ product.no }}</div>
  323. </div>
  324. <span class="item-tag">{{ product.feedBoxName }}</span>
  325. <span v-if="isOffShelfProductSelected(product.id)" class="selected-icon">
  326. <i class="fas fa-check-circle" />
  327. </span>
  328. </div>
  329. </div>
  330. <div v-if="offShelfProductList.length === 0" class="empty-tip">
  331. 暂无成品信息
  332. </div>
  333. </div>
  334. </div>
  335. <!-- 右侧:已选成品 -->
  336. <div class="list-section">
  337. <h3 class="section-title">
  338. <i class="fas fa-clipboard-check" />
  339. 已选成品({{ selectedOffShelfProducts.length }})
  340. </h3>
  341. <div class="item-list">
  342. <div v-for="product in selectedOffShelfProducts" :key="product.id" class="item-card selected-card">
  343. <div class="item-header">
  344. <div class="item-info">
  345. <div class="item-name">{{ product.name }}</div>
  346. <div class="item-id">编号: {{ product.no }}</div>
  347. </div>
  348. <span class="item-tag">{{ product.feedBoxName }}</span>
  349. <button class="remove-btn" @click="removeOffShelfProduct(product.id)">
  350. <i class="fas fa-times" />
  351. </button>
  352. </div>
  353. </div>
  354. <div v-if="selectedOffShelfProducts.length === 0" class="empty-tip">
  355. 请从左侧选择要下架的成品
  356. </div>
  357. </div>
  358. </div>
  359. </div>
  360. <!-- 底部按钮 -->
  361. <div class="dialog-footer">
  362. <button class="modal-btn submit-btn" @click="submitProductOffShelf">
  363. <i class="fas fa-dolly" />
  364. 成品下架
  365. </button>
  366. <button class="modal-btn close-action-btn" @click="closeProductOffShelfDialog">
  367. 关闭
  368. </button>
  369. </div>
  370. </div>
  371. </div>
  372. </div>
  373. <!-- 页脚 -->
  374. <footer class="tech-footer">
  375. <!-- <p>&copy; 2024 智能仓储管理系统. All rights reserved.</p> -->
  376. </footer>
  377. </div>
  378. <!-- Loading -->
  379. <div v-if="loading" class="loading-overlay">
  380. <div class="loading-dots">
  381. <div class="dot" />
  382. <div class="dot" />
  383. <div class="dot" />
  384. </div>
  385. <span class="loading-text">加载中...</span>
  386. </div>
  387. </template>
  388. <script setup>
  389. import { ref, onMounted, onUnmounted } from 'vue';
  390. import { showNotify } from 'vant';
  391. import {
  392. checkBlankBox, requestBlankBox, checkBlankBoxAndInventory, checkFeedBoxAndProduct, productRack,
  393. findProductCanRemoved, productRemoved,
  394. } from '../api/blankBox.js';
  395. import { useRouter } from 'vue-router';
  396. const router = useRouter();
  397. const currentDate = ref('');
  398. const errorResult = ref('');
  399. const loading = ref(false);
  400. const showResultDialog = ref(false);
  401. const dialogTitle = ref('');
  402. const showVerificationStatus = ref(false); // 是否显示校验结果
  403. const isRequest = ref(false); // 是否为请求空料箱
  404. const verificationSuccess = ref(true); // 校验是否成功
  405. const binList = ref([]);
  406. const toolList = ref([]);
  407. // 成品上架相关状态
  408. const showProductShelfDialog = ref(false);
  409. const productShelfSuccess = ref(false);
  410. const productShelfError = ref('');
  411. const productShelfBinList = ref([]);
  412. const productShelfToolList = ref([]);
  413. const productShelfProductList = ref([]);
  414. const selectedProducts = ref([]);
  415. const isEmptySubmit = ref(false); // 是否为成品空料箱上架
  416. // 成品下架相关状态
  417. const showProductOffShelfDialog = ref(false);
  418. const offShelfProductList = ref([]);
  419. const selectedOffShelfProducts = ref([]);
  420. const goBack = () => {
  421. router.back();
  422. };
  423. // 接口数据处理
  424. const handleDataProcessing = (res, isReverify = false) => {
  425. if (res.data) {
  426. binList.value = [];
  427. toolList.value = [];
  428. isRequest.value = false;
  429. const { success, rfidFeedBoxResponseList, rfidInventoryResponseList, feedBoxNo, feedBoxName } = res.data;
  430. if (!isReverify) showResultDialog.value = true;
  431. if (success) {
  432. verificationSuccess.value = true;
  433. } else {
  434. verificationSuccess.value = false;
  435. }
  436. if (rfidFeedBoxResponseList && rfidFeedBoxResponseList.length > 0) {
  437. binList.value = rfidFeedBoxResponseList;
  438. }
  439. if (rfidInventoryResponseList && rfidInventoryResponseList.length > 0) {
  440. toolList.value = rfidInventoryResponseList;
  441. }
  442. if (feedBoxNo && feedBoxName) {
  443. isRequest.value = true;
  444. binList.value = [res.data];
  445. }
  446. }
  447. if (res.errorCode !== 0) {
  448. verificationSuccess.value = false;
  449. errorResult.value = res.errorMessage,
  450. showNotify({ type: 'danger', message: res.errorMessage, duration: 6000, zIndex: 9999999 });
  451. } else {
  452. errorResult.value = '';
  453. }
  454. };
  455. // 空料箱上架
  456. const handlePlaceEmptyBin = async isReverify => {
  457. dialogTitle.value = '上架结果';
  458. showVerificationStatus.value = true;
  459. loading.value = true;
  460. try {
  461. const res = await checkBlankBox();
  462. handleDataProcessing(res, isReverify);
  463. } catch (error) {
  464. verificationSuccess.value = false;
  465. console.error('空料箱上架:', error);
  466. showNotify({ type: 'danger', message: '空料箱上架API调用失败' });
  467. } finally {
  468. loading.value = false;
  469. }
  470. };
  471. // 请求空料箱
  472. const handleRequestEmptyBin = async isReverify => {
  473. dialogTitle.value = '请求结果';
  474. showVerificationStatus.value = false;
  475. loading.value = true;
  476. try {
  477. const res = await requestBlankBox();
  478. handleDataProcessing(res, isReverify);
  479. } catch (error) {
  480. console.error('校验失败:', error);
  481. verificationSuccess.value = false;
  482. showNotify({ type: 'danger', message: '请求空料箱API调用失败' });
  483. } finally {
  484. loading.value = false;
  485. }
  486. };
  487. // 请求校验
  488. const handleRequestVerification = async isReverify => {
  489. dialogTitle.value = '校验结果';
  490. showVerificationStatus.value = true;
  491. loading.value = true;
  492. try {
  493. const res = await checkBlankBoxAndInventory();
  494. handleDataProcessing(res, isReverify);
  495. } catch (error) {
  496. console.error('校验失败:', error);
  497. verificationSuccess.value = false;
  498. showNotify({ type: 'danger', message: '请求校验API调用失败' });
  499. } finally {
  500. loading.value = false;
  501. }
  502. };
  503. const handleReverify = () => {
  504. showNotify({ type: 'primary', message: '正在重新校验....', duration: 1000, zIndex: 9999999 });
  505. if (dialogTitle.value === '上架结果') {
  506. handlePlaceEmptyBin();
  507. } else if (dialogTitle.value === '校验结果')
  508. handleRequestVerification(true);
  509. };
  510. const closeDialog = () => {
  511. showResultDialog.value = false;
  512. };
  513. // 处理成品上架接口数据
  514. const handleProductShelfDataProcessing = (res, isReverify = false) => {
  515. if (res.data) {
  516. isEmptySubmit.value = false;
  517. productShelfBinList.value = [];
  518. productShelfToolList.value = [];
  519. productShelfProductList.value = [];
  520. if (!isReverify) {
  521. selectedProducts.value = [];
  522. }
  523. const { success, rfidFeedBoxResponseList, rfidInventoryResponseList, productResponseList, feedBoxDetailIds, blankFeedBox } = res.data;
  524. if (!isReverify) showProductShelfDialog.value = true;
  525. productShelfSuccess.value = success;
  526. isEmptySubmit.value = blankFeedBox;
  527. if (rfidFeedBoxResponseList && rfidFeedBoxResponseList.length > 0) {
  528. productShelfBinList.value = rfidFeedBoxResponseList;
  529. }
  530. if (rfidInventoryResponseList && rfidInventoryResponseList.length > 0) {
  531. productShelfToolList.value = rfidInventoryResponseList;
  532. }
  533. if (productResponseList && productResponseList.length > 0) {
  534. productShelfProductList.value = productResponseList;
  535. // 根据 feedBoxDetailIds 预选中对应成品
  536. if (Array.isArray(feedBoxDetailIds) && feedBoxDetailIds.length > 0) {
  537. const selectedIdSet = new Set(feedBoxDetailIds);
  538. const preSelected = productResponseList.filter(p => selectedIdSet.has(p.productId));
  539. if (preSelected.length > 0) {
  540. selectedProducts.value = preSelected.map(p => ({ ...p }));
  541. }
  542. }
  543. }
  544. }
  545. if (res.errorCode !== 0) {
  546. productShelfSuccess.value = false;
  547. productShelfError.value = res.errorMessage;
  548. showNotify({ type: 'danger', message: res.errorMessage, duration: 6000, zIndex: 9999999 });
  549. } else {
  550. productShelfError.value = '';
  551. }
  552. if (res.data.blankFeedBox) productShelfError.value += ',您可以进行空料想上架';
  553. };
  554. // 成品上架点击
  555. const handleProductShelf = async (isReverify = false) => {
  556. loading.value = true;
  557. try {
  558. const res = await checkFeedBoxAndProduct();
  559. handleProductShelfDataProcessing(res, isReverify);
  560. } catch (error) {
  561. console.error('成品上架校验失败:', error);
  562. productShelfSuccess.value = false;
  563. showNotify({ type: 'danger', message: '成品上架校验API调用失败' });
  564. } finally {
  565. loading.value = false;
  566. }
  567. };
  568. // 重新校验成品上架
  569. const handleProductShelfReverify = () => {
  570. showNotify({ type: 'primary', message: '正在重新校验....', duration: 1000, zIndex: 9999999 });
  571. handleProductShelf(true);
  572. };
  573. // 关闭成品上架弹窗
  574. const closeProductShelfDialog = () => {
  575. showProductShelfDialog.value = false;
  576. selectedProducts.value = [];
  577. };
  578. // 检查成品是否已选中
  579. const isProductSelected = productId => {
  580. return selectedProducts.value.some(p => p.productId === productId);
  581. };
  582. // 切换成品选中状态
  583. const toggleProductSelection = product => {
  584. const index = selectedProducts.value.findIndex(p => p.productId === product.productId);
  585. if (index > -1) {
  586. selectedProducts.value.splice(index, 1);
  587. } else {
  588. selectedProducts.value.push({ ...product });
  589. }
  590. };
  591. // 移除已选成品
  592. const removeSelectedProduct = productId => {
  593. const index = selectedProducts.value.findIndex(p => p.productId === productId);
  594. if (index > -1) {
  595. selectedProducts.value.splice(index, 1);
  596. }
  597. };
  598. // 提交成品上架
  599. const submitProductShelf = async () => {
  600. console.log(productShelfSuccess.value);
  601. if (!productShelfSuccess.value) {
  602. showNotify({ type: 'danger', message: '校验未通过,请重新校验', duration: 3000, zIndex: 9999999 });
  603. return;
  604. }
  605. // 校验: 是否选择了成品
  606. if (selectedProducts.value.length === 0) {
  607. showNotify({ type: 'danger', message: '请先选择要上架的成品', duration: 3000, zIndex: 9999999 });
  608. return;
  609. }
  610. // 获取料箱ID和所选成品ID集合
  611. const feedBoxId = productShelfBinList.value[0].feedBoxId;
  612. const productIds = selectedProducts.value.map(p => p.productId);
  613. const params = { feedBoxId, productIds };
  614. console.log('成品上架提交数据:', params);
  615. loading.value = true;
  616. try {
  617. const res = await productRack(params);
  618. if (res.errorCode === 0) {
  619. showNotify({ type: 'success', message: '成品上架成功', duration: 3000, zIndex: 9999999 });
  620. closeProductShelfDialog();
  621. } else {
  622. showNotify({ type: 'danger', message: res.errorMessage, duration: 3000, zIndex: 9999999 });
  623. }
  624. } catch (error) {
  625. productShelfSuccess.value = false;
  626. console.error('成品上架校验失败:', error);
  627. showNotify({ type: 'danger', message: '成品上架校验API调用失败' });
  628. } finally {
  629. loading.value = false;
  630. }
  631. };
  632. // 提交成名空料箱上架
  633. const submitProductShelfEmpty = () => {
  634. closeProductShelfDialog();
  635. handlePlaceEmptyBin(false);
  636. };
  637. // 成品下架点击
  638. const handleProductOffShelf = async () => {
  639. offShelfProductList.value = [];
  640. selectedOffShelfProducts.value = [];
  641. loading.value = true;
  642. try {
  643. const res = await findProductCanRemoved();
  644. if (res.errorCode === 0) {
  645. if (res.datas && res.datas.length > 0) {
  646. offShelfProductList.value = res.datas;
  647. selectedOffShelfProducts.value = [];
  648. showProductOffShelfDialog.value = true;
  649. } else {
  650. offShelfProductList.value = [];
  651. }
  652. } else {
  653. showNotify({ type: 'danger', message: res.errorMessage, duration: 3000, zIndex: 9999999 });
  654. }
  655. } catch (error) {
  656. console.error('获取成品下架列表失败:', error);
  657. showNotify({ type: 'danger', message: '获取成品下架列表失败' });
  658. } finally {
  659. loading.value = false;
  660. }
  661. };
  662. // 关闭成品下架弹窗
  663. const closeProductOffShelfDialog = () => {
  664. showProductOffShelfDialog.value = false;
  665. selectedOffShelfProducts.value = [];
  666. };
  667. // 检查成品下架是否已选中
  668. const isOffShelfProductSelected = productId => {
  669. return selectedOffShelfProducts.value.some(p => p.id === productId);
  670. };
  671. // 切换成品下架选中状态
  672. const toggleOffShelfProductSelection = product => {
  673. const index = selectedOffShelfProducts.value.findIndex(p => p.id === product.id);
  674. if (index > -1) {
  675. selectedOffShelfProducts.value.splice(index, 1);
  676. } else {
  677. selectedOffShelfProducts.value.push({ ...product });
  678. }
  679. };
  680. // 移除已选成品下架
  681. const removeOffShelfProduct = productId => {
  682. const index = selectedOffShelfProducts.value.findIndex(p => p.id === productId);
  683. if (index > -1) {
  684. selectedOffShelfProducts.value.splice(index, 1);
  685. }
  686. };
  687. // 提交成品下架
  688. const submitProductOffShelf = async () => {
  689. // 校验是否选择了成品
  690. if (selectedOffShelfProducts.value.length === 0) {
  691. showNotify({ type: 'danger', message: '请先选择要下架的成品', duration: 3000, zIndex: 9999999 });
  692. return;
  693. }
  694. // 获取所选成品ID集合
  695. const params = selectedOffShelfProducts.value.map(p => {
  696. return {
  697. id: p.id,
  698. feedBoxId: p.feedBoxId,
  699. };
  700. });
  701. console.log('成品下架提交数据:', params);
  702. loading.value = true;
  703. try {
  704. const res = await productRemoved(params);
  705. if (res.errorCode === 0) {
  706. showNotify({ type: 'success', message: '成品下架成功', duration: 3000, zIndex: 9999999 });
  707. closeProductOffShelfDialog();
  708. } else {
  709. showNotify({ type: 'danger', message: res.errorMessage, duration: 6000, zIndex: 9999999 });
  710. }
  711. } catch (error) {
  712. console.error('成品下架API调用失败:', error);
  713. showNotify({ type: 'danger', message: '成品下架API调用失败' });
  714. } finally {
  715. loading.value = false;
  716. }
  717. };
  718. const updateDateTime = () => {
  719. const now = new Date();
  720. const year = now.getFullYear();
  721. const month = String(now.getMonth() + 1).padStart(2, '0');
  722. const day = String(now.getDate()).padStart(2, '0');
  723. const hour = String(now.getHours()).padStart(2, '0');
  724. const minute = String(now.getMinutes()).padStart(2, '0');
  725. const second = String(now.getSeconds()).padStart(2, '0');
  726. currentDate.value = `${year}.${month}.${day} ${hour}:${minute}:${second}`;
  727. };
  728. const getParticleStyle = index => {
  729. const size = Math.random() * 4 + 2;
  730. const left = Math.random() * 100;
  731. const animationDuration = Math.random() * 20 + 10;
  732. const animationDelay = Math.random() * 5;
  733. return {
  734. width: `${size}px`,
  735. height: `${size}px`,
  736. left: `${left}%`,
  737. animationDuration: `${animationDuration}s`,
  738. animationDelay: `${animationDelay}s`,
  739. };
  740. };
  741. let timer = null;
  742. onMounted(() => {
  743. // updateDateTime();
  744. // timer = setInterval(updateDateTime, 1000); // 每秒更新一次
  745. });
  746. onUnmounted(() => {
  747. if (timer) {
  748. clearInterval(timer);
  749. }
  750. });
  751. </script>
  752. <style scoped>
  753. /* ========== 主容器 ========== */
  754. .feeding-container {
  755. width: 100vw;
  756. height: 100vh;
  757. position: relative;
  758. background: #0a0e27;
  759. font-family: 'Microsoft YaHei', Arial, sans-serif;
  760. overflow: hidden;
  761. display: flex;
  762. flex-direction: column;
  763. }
  764. /* ========== 动态背景 ========== */
  765. .tech-background {
  766. position: fixed;
  767. top: 0;
  768. left: 0;
  769. width: 100%;
  770. height: 100%;
  771. z-index: 0;
  772. }
  773. .tech-grid {
  774. position: absolute;
  775. width: 100%;
  776. height: 100%;
  777. background-image:
  778. linear-gradient(rgba(0, 153, 255, 0.1) 1px, transparent 1px),
  779. linear-gradient(90deg, rgba(0, 153, 255, 0.1) 1px, transparent 1px);
  780. background-size: 50px 50px;
  781. animation: gridMove 20s linear infinite;
  782. }
  783. @keyframes gridMove {
  784. 0% {
  785. transform: translate(0, 0);
  786. }
  787. 100% {
  788. transform: translate(50px, 50px);
  789. }
  790. }
  791. .tech-gradient {
  792. position: absolute;
  793. width: 100%;
  794. height: 100%;
  795. background: radial-gradient(ellipse at center,
  796. rgba(0, 100, 200, 0.2) 0%,
  797. rgba(10, 14, 39, 0.8) 50%,
  798. #0a0e27 100%);
  799. }
  800. .tech-particles {
  801. position: absolute;
  802. width: 100%;
  803. height: 100%;
  804. overflow: hidden;
  805. }
  806. .particle {
  807. position: absolute;
  808. background: rgba(0, 255, 255, 0.8);
  809. border-radius: 50%;
  810. bottom: -10px;
  811. animation: particleFloat linear infinite;
  812. box-shadow:
  813. 0 0 10px rgba(0, 255, 255, 0.8),
  814. 0 0 20px rgba(0, 153, 255, 0.6);
  815. }
  816. @keyframes particleFloat {
  817. 0% {
  818. transform: translateY(0) rotate(0deg);
  819. opacity: 0;
  820. }
  821. 10% {
  822. opacity: 1;
  823. }
  824. 90% {
  825. opacity: 1;
  826. }
  827. 100% {
  828. transform: translateY(-100vh) rotate(360deg);
  829. opacity: 0;
  830. }
  831. }
  832. /* ========== 顶部栏 ========== */
  833. .tech-header {
  834. position: relative;
  835. z-index: 10;
  836. height: 100px;
  837. display: flex;
  838. align-items: center;
  839. justify-content: space-between;
  840. padding: 0 40px;
  841. background: linear-gradient(180deg, rgba(0, 20, 50, 0.9) 0%, rgba(10, 14, 39, 0.7) 100%);
  842. border-bottom: 2px solid rgba(0, 153, 255, 0.3);
  843. box-shadow: 0 4px 20px rgba(0, 153, 255, 0.2);
  844. }
  845. .header-left {
  846. display: flex;
  847. align-items: center;
  848. gap: 15px;
  849. min-width: 200px;
  850. }
  851. .logo-box {
  852. width: 50px;
  853. height: 50px;
  854. background: linear-gradient(135deg, #00ff88 0%, #0099ff 100%);
  855. border-radius: 8px;
  856. display: flex;
  857. align-items: center;
  858. justify-content: center;
  859. font-size: 28px;
  860. color: #fff;
  861. box-shadow: 0 0 20px rgba(0, 153, 255, 0.6);
  862. animation: logoGlow 2s ease-in-out infinite;
  863. }
  864. @keyframes logoGlow {
  865. 0%,
  866. 100% {
  867. box-shadow: 0 0 20px rgba(0, 153, 255, 0.6);
  868. }
  869. 50% {
  870. box-shadow: 0 0 30px rgba(0, 255, 136, 0.8);
  871. }
  872. }
  873. .logo-text {
  874. font-size: 32px;
  875. font-weight: bold;
  876. color: #00ff88;
  877. text-shadow: 0 0 10px rgba(0, 255, 136, 0.8);
  878. letter-spacing: 2px;
  879. }
  880. .header-center {
  881. flex: 1;
  882. display: flex;
  883. justify-content: center;
  884. }
  885. .system-title {
  886. font-size: 48px;
  887. font-weight: bold;
  888. color: #fff;
  889. text-shadow: 0 0 20px rgba(0, 153, 255, 0.8);
  890. letter-spacing: 8px;
  891. }
  892. .header-right {
  893. min-width: 200px;
  894. display: flex;
  895. justify-content: flex-end;
  896. }
  897. .date-display {
  898. font-size: 24px;
  899. color: #00ffff;
  900. font-weight: 600;
  901. padding: 10px 20px;
  902. background: rgba(0, 153, 255, 0.1);
  903. border: 1px solid rgba(0, 153, 255, 0.3);
  904. border-radius: 8px;
  905. box-shadow: inset 0 0 10px rgba(0, 153, 255, 0.2);
  906. }
  907. /* ========== 主内容区域 ========== */
  908. .tech-main {
  909. position: relative;
  910. z-index: 5;
  911. flex: 1;
  912. padding: 40px 40px;
  913. display: flex;
  914. align-items: flex-start;
  915. justify-content: center;
  916. overflow-y: auto;
  917. overflow-x: hidden;
  918. }
  919. /* ========== 操作卡片区域 ========== */
  920. .action-cards {
  921. display: grid;
  922. grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
  923. gap: 40px;
  924. max-width: 1400px;
  925. width: 100%;
  926. padding: 0 20px;
  927. }
  928. .action-card {
  929. position: relative;
  930. height: 300px;
  931. max-height: calc((100vh - 200px) / 1.5);
  932. cursor: pointer;
  933. transition: all 0.4s ease;
  934. }
  935. .action-card:hover {
  936. transform: translateY(-15px) scale(1.05);
  937. }
  938. .card-border {
  939. position: absolute;
  940. inset: 0;
  941. background: linear-gradient(135deg,
  942. rgba(0, 255, 255, 0.9) 0%,
  943. rgba(0, 153, 255, 0.9) 25%,
  944. rgba(255, 0, 255, 0.9) 50%,
  945. rgba(0, 153, 255, 0.9) 75%,
  946. rgba(0, 255, 255, 0.9) 100%);
  947. background-size: 200% 200%;
  948. border-radius: 20px;
  949. padding: 4px;
  950. -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
  951. mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
  952. -webkit-mask-composite: xor;
  953. mask-composite: exclude;
  954. animation: borderShine 3s linear infinite;
  955. }
  956. @keyframes borderShine {
  957. 0% {
  958. background-position: 0% 50%;
  959. }
  960. 100% {
  961. background-position: 200% 50%;
  962. }
  963. }
  964. .card-glow {
  965. position: absolute;
  966. inset: -8px;
  967. background: radial-gradient(circle at center,
  968. rgba(0, 255, 255, 0.6) 0%,
  969. rgba(0, 153, 255, 0.4) 40%,
  970. transparent 70%);
  971. border-radius: 20px;
  972. opacity: 0.5;
  973. animation: glowPulse 2s ease-in-out infinite;
  974. }
  975. .action-card:hover .card-glow {
  976. opacity: 1;
  977. }
  978. @keyframes glowPulse {
  979. 0%,
  980. 100% {
  981. opacity: 0.5;
  982. }
  983. 50% {
  984. opacity: 1;
  985. }
  986. }
  987. .card-content {
  988. position: relative;
  989. height: 100%;
  990. background: linear-gradient(135deg, rgba(10, 25, 50, 0.95) 0%, rgba(15, 30, 60, 0.95) 100%);
  991. border-radius: 18px;
  992. display: flex;
  993. flex-direction: column;
  994. align-items: center;
  995. justify-content: center;
  996. gap: 30px;
  997. backdrop-filter: blur(15px);
  998. overflow: hidden;
  999. box-shadow: inset 0 0 50px rgba(0, 255, 255, 0.1), 0 15px 50px rgba(0, 0, 0, 0.6);
  1000. }
  1001. .card-icon {
  1002. width: 130px;
  1003. height: 130px;
  1004. border-radius: 50%;
  1005. display: flex;
  1006. align-items: center;
  1007. justify-content: center;
  1008. font-size: 80px;
  1009. color: #fff;
  1010. border: 4px solid;
  1011. transition: all 0.4s ease;
  1012. animation: iconPulse 2s ease-in-out infinite;
  1013. }
  1014. .card-icon.blue {
  1015. background: linear-gradient(135deg, rgba(0, 153, 255, 0.3), rgba(0, 255, 255, 0.3));
  1016. border-color: rgba(0, 255, 255, 0.8);
  1017. box-shadow: 0 0 50px rgba(0, 153, 255, 1), inset 0 0 30px rgba(0, 255, 255, 0.3);
  1018. }
  1019. .card-icon.green {
  1020. background: linear-gradient(135deg, rgba(0, 255, 136, 0.3), rgba(0, 200, 100, 0.3));
  1021. border-color: rgba(0, 255, 136, 0.8);
  1022. box-shadow: 0 0 50px rgba(0, 255, 136, 1), inset 0 0 30px rgba(0, 255, 136, 0.3);
  1023. }
  1024. .card-icon.orange {
  1025. background: linear-gradient(135deg, rgba(255, 153, 0, 0.3), rgba(255, 100, 0, 0.3));
  1026. border-color: rgba(255, 153, 0, 0.8);
  1027. box-shadow: 0 0 50px rgba(255, 153, 0, 1), inset 0 0 30px rgba(255, 153, 0, 0.3);
  1028. }
  1029. .card-icon.purple {
  1030. background: linear-gradient(135deg, rgba(153, 51, 255, 0.3), rgba(102, 0, 204, 0.3));
  1031. border-color: rgba(153, 51, 255, 0.8);
  1032. box-shadow: 0 0 50px rgba(153, 51, 255, 1), inset 0 0 30px rgba(153, 51, 255, 0.3);
  1033. }
  1034. .card-icon.red {
  1035. background: linear-gradient(135deg, rgba(255, 71, 87, 0.3), rgba(220, 20, 60, 0.3));
  1036. border-color: rgba(255, 71, 87, 0.8);
  1037. box-shadow: 0 0 50px rgba(255, 71, 87, 1), inset 0 0 30px rgba(255, 71, 87, 0.3);
  1038. }
  1039. @keyframes iconPulse {
  1040. 0%,
  1041. 100% {
  1042. transform: scale(1);
  1043. }
  1044. 50% {
  1045. transform: scale(1.08);
  1046. }
  1047. }
  1048. .action-card:hover .card-icon {
  1049. transform: scale(1.15) rotate(10deg);
  1050. }
  1051. .card-label {
  1052. font-size: 42px;
  1053. font-weight: bold;
  1054. color: #fff;
  1055. text-shadow: 0 0 20px rgba(0, 255, 255, 1), 0 0 40px rgba(0, 153, 255, 0.8);
  1056. letter-spacing: 4px;
  1057. }
  1058. .card-decoration {
  1059. position: absolute;
  1060. inset: 0;
  1061. pointer-events: none;
  1062. }
  1063. .deco-line {
  1064. position: absolute;
  1065. bottom: 30px;
  1066. left: 50%;
  1067. transform: translateX(-50%);
  1068. width: 70%;
  1069. height: 4px;
  1070. background: linear-gradient(90deg, transparent, #00ffff 20%, #ff00ff 50%, #00ffff 80%, transparent);
  1071. box-shadow: 0 0 20px rgba(0, 255, 255, 1);
  1072. animation: lineFlow 2s ease-in-out infinite;
  1073. }
  1074. @keyframes lineFlow {
  1075. 0%,
  1076. 100% {
  1077. opacity: 0.6;
  1078. }
  1079. 50% {
  1080. opacity: 1;
  1081. }
  1082. }
  1083. .deco-corner {
  1084. position: absolute;
  1085. top: 20px;
  1086. right: 20px;
  1087. width: 40px;
  1088. height: 40px;
  1089. border-top: 4px solid #00ffff;
  1090. border-right: 4px solid #00ffff;
  1091. border-radius: 0 10px 0 0;
  1092. box-shadow: 0 0 20px rgba(0, 255, 255, 1);
  1093. animation: cornerPulse 1.5s ease-in-out infinite;
  1094. }
  1095. @keyframes cornerPulse {
  1096. 0%,
  1097. 100% {
  1098. opacity: 0.6;
  1099. }
  1100. 50% {
  1101. opacity: 1;
  1102. }
  1103. }
  1104. /* ========== 页脚 ========== */
  1105. .tech-footer {
  1106. position: relative;
  1107. z-index: 10;
  1108. padding: 15px;
  1109. text-align: center;
  1110. color: rgba(255, 255, 255, 0.5);
  1111. font-size: 12px;
  1112. background: rgba(10, 14, 39, 0.7);
  1113. border-top: 1px solid rgba(0, 153, 255, 0.2);
  1114. flex-shrink: 0;
  1115. }
  1116. /* ========== 弹窗样式 ========== */
  1117. :deep(.tech-dialog) {
  1118. width: 95vw !important;
  1119. max-width: 1400px !important;
  1120. border-radius: 20px !important;
  1121. background: linear-gradient(135deg, rgba(15, 25, 45, 0.98), rgba(20, 30, 50, 0.98)) !important;
  1122. border: 3px solid rgba(0, 255, 255, 0.5) !important;
  1123. box-shadow: 0 0 50px rgba(0, 255, 255, 0.5), inset 0 0 30px rgba(0, 255, 255, 0.1) !important;
  1124. }
  1125. :deep(.tech-dialog .van-dialog__header) {
  1126. padding: 25px 30px !important;
  1127. font-size: 32px !important;
  1128. font-weight: bold !important;
  1129. color: #00ffff !important;
  1130. text-shadow: 0 0 20px rgba(0, 255, 255, 1) !important;
  1131. background: transparent !important;
  1132. border-bottom: 2px solid rgba(0, 255, 255, 0.3) !important;
  1133. }
  1134. .dialog-content {
  1135. padding: 30px;
  1136. color: #fff;
  1137. }
  1138. .verification-status {
  1139. margin-bottom: 25px;
  1140. display: flex;
  1141. /* justify-content: center; */
  1142. justify-content: space-between;
  1143. }
  1144. :deep(.van-tag) {
  1145. padding: 10px 25px !important;
  1146. font-size: 18px !important;
  1147. font-weight: 600 !important;
  1148. border-radius: 20px !important;
  1149. display: flex !important;
  1150. align-items: center !important;
  1151. gap: 8px !important;
  1152. }
  1153. .dialog-body {
  1154. display: grid;
  1155. grid-template-columns: 1fr 1fr;
  1156. gap: 30px;
  1157. margin-bottom: 30px;
  1158. }
  1159. .list-section {
  1160. background: rgba(10, 20, 40, 0.6);
  1161. border-radius: 12px;
  1162. padding: 20px;
  1163. border: 2px solid rgba(0, 255, 255, 0.2);
  1164. }
  1165. .section-title {
  1166. font-size: 24px;
  1167. font-weight: bold;
  1168. color: #00ffff;
  1169. margin-bottom: 20px;
  1170. text-shadow: 0 0 15px rgba(0, 255, 255, 0.8);
  1171. display: flex;
  1172. align-items: center;
  1173. }
  1174. .item-list {
  1175. max-height: 400px;
  1176. overflow-y: auto;
  1177. }
  1178. .item-card {
  1179. background: rgba(20, 30, 50, 0.6);
  1180. border-radius: 10px;
  1181. padding: 15px;
  1182. margin-bottom: 4px;
  1183. border: 1px solid rgba(0, 153, 255, 0.3);
  1184. transition: all 0.3s ease;
  1185. }
  1186. .item-card:hover {
  1187. transform: translateY(-3px);
  1188. box-shadow: 0 5px 20px rgba(0, 255, 255, 0.3);
  1189. border-color: rgba(0, 255, 255, 0.6);
  1190. }
  1191. .item-header {
  1192. display: flex;
  1193. justify-content: space-between;
  1194. align-items: flex-start;
  1195. }
  1196. .item-info {
  1197. flex: 1;
  1198. }
  1199. .item-id,
  1200. .item-name {
  1201. font-size: 18px;
  1202. font-weight: bold;
  1203. color: #fff;
  1204. margin-bottom: 8px;
  1205. }
  1206. .item-location {
  1207. font-size: 14px;
  1208. color: rgba(255, 255, 255, 0.7);
  1209. }
  1210. .item-type {
  1211. font-size: 14px;
  1212. margin-top: 10px;
  1213. padding: 5px 10px;
  1214. border-radius: 8px;
  1215. display: inline-block;
  1216. }
  1217. .type-tool {
  1218. background: rgba(0, 153, 255, 0.2);
  1219. color: #0099ff;
  1220. border: 1px solid rgba(0, 153, 255, 0.5);
  1221. }
  1222. .type-equipment {
  1223. background: rgba(153, 0, 255, 0.2);
  1224. color: #9900ff;
  1225. border: 1px solid rgba(153, 0, 255, 0.5);
  1226. }
  1227. .verification-result {
  1228. padding: 20px;
  1229. }
  1230. .result-header {
  1231. display: flex;
  1232. align-items: center;
  1233. gap: 15px;
  1234. margin-bottom: 20px;
  1235. }
  1236. .result-header i {
  1237. font-size: 36px;
  1238. }
  1239. .result-header h4 {
  1240. font-size: 24px;
  1241. font-weight: bold;
  1242. }
  1243. .text-success {
  1244. color: #00ff88;
  1245. }
  1246. .text-danger {
  1247. color: #ff3366;
  1248. }
  1249. .result-message {
  1250. font-size: 16px;
  1251. color: rgba(255, 255, 255, 0.9);
  1252. line-height: 1.6;
  1253. margin-bottom: 20px;
  1254. }
  1255. .result-errors {
  1256. background: rgba(255, 51, 102, 0.1);
  1257. border: 1px solid rgba(255, 51, 102, 0.3);
  1258. border-radius: 8px;
  1259. padding: 15px;
  1260. }
  1261. .result-errors h5 {
  1262. font-size: 16px;
  1263. font-weight: bold;
  1264. color: #ff3366;
  1265. margin-bottom: 10px;
  1266. }
  1267. .result-errors ul {
  1268. list-style: disc;
  1269. padding-left: 25px;
  1270. }
  1271. .result-errors li {
  1272. color: rgba(255, 255, 255, 0.8);
  1273. margin-bottom: 5px;
  1274. }
  1275. .dialog-footer {
  1276. display: flex;
  1277. gap: 15px;
  1278. justify-content: flex-end;
  1279. }
  1280. .action-btn {
  1281. min-width: 150px;
  1282. height: 50px;
  1283. font-size: 18px;
  1284. font-weight: 600;
  1285. border-radius: 10px;
  1286. }
  1287. :deep(.van-button--primary) {
  1288. background: linear-gradient(135deg, #0099ff, #00ffff) !important;
  1289. border: 2px solid #00ffff !important;
  1290. box-shadow: 0 0 20px rgba(0, 255, 255, 0.5) !important;
  1291. }
  1292. :deep(.van-button--default) {
  1293. background: rgba(100, 100, 100, 0.3) !important;
  1294. border: 2px solid rgba(255, 255, 255, 0.3) !important;
  1295. color: #fff !important;
  1296. }
  1297. /* ========== 响应式设计 ========== */
  1298. @media (max-width: 1200px) {
  1299. .action-cards {
  1300. grid-template-columns: 1fr;
  1301. gap: 30px;
  1302. padding: 0 10px;
  1303. }
  1304. .dialog-body {
  1305. grid-template-columns: 1fr;
  1306. }
  1307. .system-title {
  1308. font-size: 36px;
  1309. letter-spacing: 4px;
  1310. }
  1311. .action-card {
  1312. height: 260px;
  1313. max-height: calc((100vh - 180px) / 1.5);
  1314. }
  1315. .tech-main {
  1316. padding: 40px 20px;
  1317. }
  1318. }
  1319. @media (max-width: 768px) {
  1320. .tech-header {
  1321. padding: 0 15px;
  1322. height: 70px;
  1323. }
  1324. .header-left,
  1325. .header-right {
  1326. min-width: auto;
  1327. }
  1328. .logo-box {
  1329. width: 40px;
  1330. height: 40px;
  1331. font-size: 20px;
  1332. }
  1333. .logo-text {
  1334. font-size: 20px;
  1335. }
  1336. .date-display {
  1337. font-size: 14px;
  1338. padding: 6px 12px;
  1339. }
  1340. .system-title {
  1341. font-size: 20px;
  1342. letter-spacing: 2px;
  1343. }
  1344. .tech-main {
  1345. padding: 30px 15px;
  1346. }
  1347. .action-cards {
  1348. gap: 25px;
  1349. padding: 0 5px;
  1350. grid-template-columns: 1fr;
  1351. }
  1352. .action-card {
  1353. height: 280px;
  1354. max-height: calc((100vh - 150px) / 1.5);
  1355. }
  1356. .card-icon {
  1357. width: 100px;
  1358. height: 100px;
  1359. font-size: 50px;
  1360. }
  1361. .card-label {
  1362. font-size: 28px;
  1363. letter-spacing: 2px;
  1364. }
  1365. .tech-footer {
  1366. padding: 10px;
  1367. font-size: 11px;
  1368. }
  1369. .dialog-content {
  1370. padding: 20px;
  1371. }
  1372. .section-title {
  1373. font-size: 18px;
  1374. }
  1375. .deco-line {
  1376. bottom: 20px;
  1377. }
  1378. .deco-corner {
  1379. width: 30px;
  1380. height: 30px;
  1381. border-width: 3px;
  1382. }
  1383. }
  1384. @media (max-width: 480px) {
  1385. .tech-header {
  1386. height: 60px;
  1387. padding: 0 10px;
  1388. }
  1389. .logo-box {
  1390. width: 35px;
  1391. height: 35px;
  1392. font-size: 18px;
  1393. }
  1394. .logo-text {
  1395. font-size: 16px;
  1396. }
  1397. .system-title {
  1398. font-size: 16px;
  1399. letter-spacing: 1px;
  1400. }
  1401. .date-display {
  1402. font-size: 12px;
  1403. padding: 5px 10px;
  1404. }
  1405. .tech-main {
  1406. padding: 20px 10px;
  1407. }
  1408. .action-card {
  1409. height: 250px;
  1410. max-height: calc((100vh - 120px) / 1.5);
  1411. }
  1412. .card-icon {
  1413. width: 80px;
  1414. height: 80px;
  1415. font-size: 40px;
  1416. }
  1417. .card-label {
  1418. font-size: 24px;
  1419. }
  1420. .card-content {
  1421. gap: 30px;
  1422. }
  1423. }
  1424. /* 滚动条样式 */
  1425. .item-list::-webkit-scrollbar {
  1426. width: 8px;
  1427. }
  1428. .item-list::-webkit-scrollbar-track {
  1429. background: rgba(0, 0, 0, 0.3);
  1430. border-radius: 4px;
  1431. }
  1432. .item-list::-webkit-scrollbar-thumb {
  1433. background: linear-gradient(180deg, #0099ff, #00ffff);
  1434. border-radius: 4px;
  1435. box-shadow: 0 0 10px rgba(0, 153, 255, 0.5);
  1436. }
  1437. /* 隐藏主内容区域的滚动条 */
  1438. .tech-main::-webkit-scrollbar {
  1439. width: 0;
  1440. display: none;
  1441. }
  1442. .tech-main {
  1443. scrollbar-width: none;
  1444. /* Firefox */
  1445. -ms-overflow-style: none;
  1446. /* IE and Edge */
  1447. }
  1448. .mr-1 {
  1449. margin-right: 4px;
  1450. }
  1451. .mr-2 {
  1452. margin-right: 8px;
  1453. }
  1454. /* ========== 科技感弹窗样式 ========== */
  1455. .tech-modal-overlay {
  1456. position: fixed;
  1457. top: 0;
  1458. left: 0;
  1459. width: 100%;
  1460. height: 100%;
  1461. background: rgba(4, 28, 61, 0.9);
  1462. display: flex;
  1463. align-items: center;
  1464. justify-content: center;
  1465. z-index: 2000;
  1466. }
  1467. .tech-modal {
  1468. background: linear-gradient(135deg, #0a3d6d 0%, #041c3d 100%);
  1469. border: 1px solid #049FD8;
  1470. border-radius: 16px;
  1471. width: 90%;
  1472. max-width: 800px;
  1473. max-height: 88vh;
  1474. overflow: hidden;
  1475. box-shadow: 0 0 40px rgba(4, 159, 216, 0.3);
  1476. display: flex;
  1477. flex-direction: column;
  1478. padding: 20px 28px !important;
  1479. }
  1480. .modal-header {
  1481. display: flex;
  1482. align-items: center;
  1483. justify-content: space-between;
  1484. padding: 14px 24px;
  1485. border-bottom: 1px solid rgba(4, 159, 216, 0.3);
  1486. }
  1487. .modal-header h3 {
  1488. font-size: 22px;
  1489. font-weight: bold;
  1490. color: #fff;
  1491. margin: 0;
  1492. }
  1493. .close-btn {
  1494. width: 36px;
  1495. height: 36px;
  1496. background: rgba(255, 255, 255, 0.1);
  1497. border: none;
  1498. border-radius: 50%;
  1499. color: #7ec8ff;
  1500. font-size: 16px;
  1501. cursor: pointer;
  1502. transition: all 0.3s;
  1503. display: flex;
  1504. align-items: center;
  1505. justify-content: center;
  1506. }
  1507. .close-btn:hover {
  1508. background: rgba(255, 255, 255, 0.2);
  1509. color: #fff;
  1510. }
  1511. .dialog-content {
  1512. padding: 14px 24px 20px;
  1513. overflow: hidden;
  1514. flex: 1;
  1515. display: flex;
  1516. flex-direction: column;
  1517. }
  1518. .verification-status {
  1519. display: flex;
  1520. gap: 10px;
  1521. margin-bottom: 20px;
  1522. flex-wrap: wrap;
  1523. }
  1524. .status-tag {
  1525. display: inline-flex;
  1526. align-items: center;
  1527. gap: 6px;
  1528. padding: 8px 16px;
  1529. border-radius: 20px;
  1530. font-size: 14px;
  1531. font-weight: 600;
  1532. }
  1533. .status-tag.success {
  1534. background: rgba(16, 185, 129, 0.2);
  1535. color: #34d399;
  1536. }
  1537. .status-tag.danger {
  1538. background: rgba(239, 68, 68, 0.2);
  1539. color: #f87171;
  1540. }
  1541. .dialog-body {
  1542. display: flex;
  1543. gap: 20px;
  1544. flex-shrink: 0;
  1545. }
  1546. .list-section {
  1547. flex: 1;
  1548. background: rgba(0, 0, 0, 0.2);
  1549. border-radius: 12px;
  1550. padding: 15px;
  1551. }
  1552. .section-title {
  1553. display: flex;
  1554. align-items: center;
  1555. gap: 8px;
  1556. font-size: 16px;
  1557. color: #7ec8ff;
  1558. margin: 0 0 15px 0;
  1559. padding-bottom: 10px;
  1560. border-bottom: 1px solid rgba(255, 255, 255, 0.1);
  1561. }
  1562. .item-list {
  1563. display: flex;
  1564. flex-direction: column;
  1565. gap: 10px;
  1566. max-height: 300px;
  1567. overflow-y: auto;
  1568. }
  1569. .item-card {
  1570. background: rgba(9, 61, 140, 0.5);
  1571. border: 1px solid rgba(4, 159, 216, 0.3);
  1572. border-radius: 8px;
  1573. padding: 12px;
  1574. }
  1575. .item-header {
  1576. display: flex;
  1577. align-items: center;
  1578. justify-content: space-between;
  1579. }
  1580. .item-info {
  1581. flex: 1;
  1582. }
  1583. .item-id {
  1584. font-size: 14px;
  1585. color: #fff;
  1586. font-weight: 500;
  1587. }
  1588. .item-name {
  1589. font-size: 14px;
  1590. color: #fff;
  1591. margin-bottom: 4px;
  1592. }
  1593. .item-tag {
  1594. background: rgba(16, 185, 129, 0.2);
  1595. color: #34d399;
  1596. padding: 4px 10px;
  1597. border-radius: 12px;
  1598. font-size: 12px;
  1599. }
  1600. .verification-result {
  1601. flex: 1;
  1602. display: flex;
  1603. flex-direction: column;
  1604. align-items: center;
  1605. justify-content: center;
  1606. padding: 40px 20px;
  1607. background: rgba(0, 0, 0, 0.2);
  1608. border-radius: 12px;
  1609. }
  1610. /* 弹窗中部滚动容器:双栏 + 已选成品 */
  1611. .dialog-main {
  1612. flex: 1;
  1613. display: flex;
  1614. flex-direction: column;
  1615. gap: 16px;
  1616. overflow-y: auto;
  1617. padding-right: 4px;
  1618. }
  1619. .result-header {
  1620. display: flex;
  1621. flex-direction: column;
  1622. align-items: center;
  1623. gap: 10px;
  1624. margin-bottom: 15px;
  1625. }
  1626. .result-header i {
  1627. font-size: 48px;
  1628. color: #34d399;
  1629. }
  1630. .result-header h4 {
  1631. font-size: 20px;
  1632. color: #34d399;
  1633. margin: 0;
  1634. }
  1635. .result-message {
  1636. font-size: 14px;
  1637. color: #7ec8ff;
  1638. text-align: center;
  1639. margin: 0;
  1640. }
  1641. .dialog-footer {
  1642. display: flex;
  1643. gap: 15px;
  1644. margin-top: 16px;
  1645. padding-top: 16px;
  1646. border-top: 1px solid rgba(255, 255, 255, 0.1);
  1647. flex-shrink: 0;
  1648. }
  1649. .modal-btn {
  1650. flex: 1;
  1651. padding: 14px 0;
  1652. border-radius: 8px;
  1653. font-size: 16px;
  1654. font-weight: 600;
  1655. cursor: pointer;
  1656. transition: all 0.3s;
  1657. display: flex;
  1658. align-items: center;
  1659. justify-content: center;
  1660. gap: 8px;
  1661. }
  1662. .primary-btn {
  1663. background: linear-gradient(90deg, #1e90ff 0%, #00bfff 100%);
  1664. border: none;
  1665. color: #fff;
  1666. }
  1667. .primary-btn:hover {
  1668. box-shadow: 0 0 20px rgba(30, 144, 255, 0.5);
  1669. }
  1670. .close-action-btn {
  1671. background: transparent;
  1672. border: 1px solid #2a7fff;
  1673. color: #7ec8ff;
  1674. }
  1675. .close-action-btn:hover {
  1676. background: rgba(42, 127, 255, 0.2);
  1677. }
  1678. /* ========== 成品上架/下架相关样式 ========== */
  1679. .product-modal {
  1680. max-width: 900px;
  1681. max-height: 88vh;
  1682. }
  1683. .item-card.selectable {
  1684. cursor: pointer;
  1685. transition: all 0.3s ease;
  1686. }
  1687. .item-card.selectable:hover {
  1688. background: rgba(4, 159, 216, 0.3);
  1689. border-color: rgba(4, 159, 216, 0.6);
  1690. }
  1691. .item-card.selected {
  1692. background: rgba(16, 185, 129, 0.3);
  1693. border-color: rgba(16, 185, 129, 0.8);
  1694. }
  1695. .item-card.selected-card {
  1696. background: rgba(16, 185, 129, 0.2);
  1697. border-color: rgba(16, 185, 129, 0.5);
  1698. }
  1699. .selected-icon {
  1700. color: #34d399;
  1701. font-size: 20px;
  1702. }
  1703. .empty-tip {
  1704. text-align: center;
  1705. color: rgba(255, 255, 255, 0.5);
  1706. padding: 30px 0;
  1707. font-size: 14px;
  1708. }
  1709. /* 已选成品区域 */
  1710. .selected-section {
  1711. background: rgba(0, 0, 0, 0.2);
  1712. border-radius: 12px;
  1713. padding: 15px;
  1714. margin-top: 20px;
  1715. flex-shrink: 0;
  1716. }
  1717. .selected-list {
  1718. display: flex;
  1719. flex-wrap: wrap;
  1720. gap: 10px;
  1721. max-height: 180px;
  1722. overflow-y: auto;
  1723. }
  1724. .selected-item {
  1725. display: flex;
  1726. align-items: center;
  1727. gap: 10px;
  1728. background: rgba(16, 185, 129, 0.2);
  1729. border: 1px solid rgba(16, 185, 129, 0.5);
  1730. border-radius: 20px;
  1731. padding: 8px 12px;
  1732. }
  1733. .selected-name {
  1734. color: #34d399;
  1735. font-size: 14px;
  1736. font-weight: 500;
  1737. }
  1738. .selected-no {
  1739. color: rgba(255, 255, 255, 0.6);
  1740. font-size: 12px;
  1741. }
  1742. .remove-btn {
  1743. width: 22px;
  1744. height: 22px;
  1745. border-radius: 50%;
  1746. background: rgba(239, 68, 68, 0.3);
  1747. border: 1px solid rgba(239, 68, 68, 0.5);
  1748. color: #f87171;
  1749. font-size: 12px;
  1750. cursor: pointer;
  1751. display: flex;
  1752. align-items: center;
  1753. justify-content: center;
  1754. transition: all 0.3s;
  1755. }
  1756. .remove-btn:hover {
  1757. background: rgba(239, 68, 68, 0.5);
  1758. color: #fff;
  1759. }
  1760. /* 提交按钮样式 */
  1761. .submit-btn {
  1762. background: linear-gradient(90deg, #10b981 0%, #34d399 100%);
  1763. border: none;
  1764. color: #fff;
  1765. }
  1766. .submit-btn:hover {
  1767. box-shadow: 0 0 20px rgba(16, 185, 129, 0.5);
  1768. }
  1769. /* 响应式 */
  1770. @media screen and (max-width: 768px) {
  1771. .dialog-body {
  1772. flex-direction: column;
  1773. }
  1774. .tech-modal {
  1775. max-width: 95%;
  1776. max-height: 90vh;
  1777. }
  1778. .product-modal {
  1779. max-width: 95%;
  1780. }
  1781. .selected-list {
  1782. max-height: 100px;
  1783. }
  1784. }
  1785. </style>