UserHome.vue 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513
  1. <!-- 主页 -->
  2. <template>
  3. <div class="bg-gray-50 min-h-screen">
  4. <!-- 顶部信息栏 -->
  5. <PageHeader :show-back="false" />
  6. <!-- 主内容区域 -->
  7. <main class="container mx-auto px-6 py-12 pb-40">
  8. <div class="text-center mb-16">
  9. <h1 class="text-4xl font-bold text-gray-800 mb-4">智能仓储管理系统</h1>
  10. <p class="text-xl text-gray-600 max-w-2xl mx-auto">高效管理物料流转,实时监控库存状态,优化仓储作业流程</p>
  11. </div>
  12. <!-- 数据概览卡片 -->
  13. <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-16">
  14. <div
  15. v-for="stat in statistics" :key="stat.title" class="bg-white rounded-xl shadow-md p-6 border-l-4"
  16. :class="getStatColorClasses(stat.color, 'border')"
  17. >
  18. <div class="flex justify-between items-start">
  19. <div>
  20. <p class="text-gray-500 text-sm">{{ stat.title }}</p>
  21. <p class="text-3xl font-bold mt-2">{{ stat.value }}</p>
  22. </div>
  23. <div class="p-3 rounded-lg" :class="getStatColorClasses(stat.color, 'bg')">
  24. <i class="text-xl" :class="['fas', stat.icon, getStatColorClasses(stat.color, 'text')]" />
  25. </div>
  26. </div>
  27. <div class="mt-4">
  28. <p class="text-sm" :class="stat.trend > 0 ? 'text-green-500' : 'text-red-500'">
  29. <i class="mr-1" :class="stat.trend > 0 ? 'fas fa-arrow-up' : 'fas fa-arrow-down'" />{{ stat.changeText }}
  30. </p>
  31. </div>
  32. </div>
  33. </div>
  34. <!-- 物料分类区域 -->
  35. <div class="mb-16">
  36. <h2 class="text-2xl font-bold text-gray-800 mb-6">物料分类</h2>
  37. <div class="grid grid-cols-2 md:grid-cols-4 lg:grid-cols-6 gap-6">
  38. <div
  39. v-for="category in materialCategories" :key="category.name"
  40. class="bg-white rounded-lg shadow-sm p-4 text-center cursor-pointer hover:shadow-md transition-shadow"
  41. >
  42. <div class="w-full h-20 overflow-hidden mb-3 rounded-lg">
  43. <img :src="category.image" :alt="category.name" class="w-full h-full object-cover object-top" />
  44. </div>
  45. <p class="font-medium">{{ category.name }}</p>
  46. <p class="text-gray-500 text-sm">{{ category.count }} 种</p>
  47. </div>
  48. </div>
  49. </div>
  50. <!-- 最近操作记录 -->
  51. <div>
  52. <div class="flex justify-between items-center mb-6">
  53. <h2 class="text-2xl font-bold text-gray-800">最近操作记录</h2>
  54. <button class="text-blue-500 hover:text-blue-700 font-medium" @click="viewAllRecords">
  55. 查看全部 <i class="fas fa-chevron-right ml-1 text-xs" />
  56. </button>
  57. </div>
  58. <div class="bg-white rounded-xl shadow-sm overflow-hidden">
  59. <a-table :columns="columns" :data-source="recentRecords" :pagination="false" :scroll="{ x: true }">
  60. <template #bodyCell="{ column, record }">
  61. <template v-if="column.key === 'operationType'">
  62. <span class="px-3 py-1 rounded-full text-sm" :class="getOperationTypeClass(record.operationType)">
  63. {{ record.operationType }}
  64. </span>
  65. </template>
  66. <template v-if="column.key === 'status'">
  67. <span class="text-green-500">
  68. <i class="fas fa-check-circle mr-1" />{{ record.status }}
  69. </span>
  70. </template>
  71. </template>
  72. </a-table>
  73. </div>
  74. </div>
  75. </main>
  76. <!-- 底部圆形功能按钮 -->
  77. <footer class="fixed bottom-0 left-0 right-0 bg-white shadow-lg py-6 z-50">
  78. <div class="container mx-auto px-6">
  79. <div class="flex justify-center space-x-12">
  80. <template v-if="isOut">
  81. <button
  82. v-for="action in outButtons" :key="action.label" class="circle-button text-white"
  83. :class="getButtonClass(action.color)" @click="handleAction(action.action)"
  84. >
  85. <i :class="`fas ${action.icon}`" />
  86. <span>{{ action.label }}</span>
  87. </button>
  88. </template>
  89. <template v-if="!isOut">
  90. <button
  91. v-for="action in inButtons" :key="action.label" class="circle-button text-white"
  92. :class="getButtonClass(action.color)" @click="handleAction(action.action)"
  93. >
  94. <i :class="`fas ${action.icon}`" />
  95. <span>{{ action.label }}</span>
  96. </button>
  97. </template>
  98. </div>
  99. </div>
  100. </footer>
  101. <!-- 拣货确认弹窗 -->
  102. <a-modal
  103. v-model:open="pickingModalVisible" title="请确认您要进行的操作?" :closable="true" :mask-closable="false"
  104. @ok="handlePickingConfirm" @cancel="pickingModalVisible = false"
  105. >
  106. <template #footer>
  107. <a-button @click="handlePickingCancel">否</a-button>
  108. <a-button type="primary" danger @click="handlePickingConfirm">是</a-button>
  109. </template>
  110. <p>您是否已经申请了领料。</p>
  111. </a-modal>
  112. </div>
  113. <Loading v-if="loading" />
  114. </template>
  115. <script setup>
  116. import { ref, reactive, onMounted } from 'vue';
  117. import { useRouter } from 'vue-router';
  118. import PageHeader from '../common/PageHeader.vue';
  119. import { cfStockInLeave } from '../api/stockIn.js';
  120. import { message } from 'ant-design-vue';
  121. // 路由
  122. const router = useRouter();
  123. // 加载状态
  124. const loading = ref(false);
  125. // 弹窗控制
  126. const pickingModalVisible = ref(false);
  127. // 是否为外侧屏幕
  128. const isOut = ref(false);
  129. // 初始化时从localStorage读取isOut值
  130. onMounted(() => {
  131. const storedIsOut = localStorage.getItem('isOut');
  132. if (storedIsOut !== null) {
  133. isOut.value = storedIsOut === 'true';
  134. }
  135. });
  136. // 统计数据
  137. const statistics = reactive([
  138. {
  139. title: '总库存量',
  140. value: '12,847',
  141. icon: 'fa-boxes',
  142. color: 'blue',
  143. trend: 1,
  144. changeText: '较昨日 +2.3%',
  145. },
  146. {
  147. title: '今日入库',
  148. value: '1,248',
  149. icon: 'fa-truck-loading',
  150. color: 'green',
  151. trend: 1,
  152. changeText: '较昨日 +5.7%',
  153. },
  154. {
  155. title: '今日出库',
  156. value: '36',
  157. icon: 'fa-tasks',
  158. color: 'yellow',
  159. trend: -1,
  160. changeText: '较昨日 -1.2%',
  161. },
  162. {
  163. title: '库存周转率',
  164. value: '4.2',
  165. icon: 'fa-sync-alt',
  166. color: 'purple',
  167. trend: 1,
  168. changeText: '较上月 +0.8%',
  169. },
  170. ]);
  171. // 物料分类
  172. const materialCategories = reactive([
  173. {
  174. name: '电子元件',
  175. count: 248,
  176. image: 'https://ai-public.mastergo.com/ai/img_res/b752a3a8cb35d2b915a23b12981693d8.jpg',
  177. },
  178. {
  179. name: '机械零件',
  180. count: 186,
  181. image: 'https://ai-public.mastergo.com/ai/img_res/1d4cf39dc61dadac88754be3bd525e3c.jpg',
  182. },
  183. {
  184. name: '化工原料',
  185. count: 92,
  186. image: 'https://ai-public.mastergo.com/ai/img_res/4f12a387c9413be821c1d203bda09622.jpg',
  187. },
  188. {
  189. name: '包装材料',
  190. count: 64,
  191. image: 'https://ai-public.mastergo.com/ai/img_res/c2d2f76aebbef43067223fd11e120c49.jpg',
  192. },
  193. {
  194. name: '办公用品',
  195. count: 127,
  196. image: 'https://ai-public.mastergo.com/ai/img_res/404ab042346c15d0722e9d2eae6bda99.jpg',
  197. },
  198. {
  199. name: '维修工具',
  200. count: 89,
  201. image: 'https://ai-public.mastergo.com/ai/img_res/048d0b638d5cb1f1bd11916983979fed.jpg',
  202. },
  203. ]);
  204. // 表格列定义
  205. const columns = [
  206. {
  207. title: '时间',
  208. dataIndex: 'time',
  209. key: 'time',
  210. width: 150,
  211. },
  212. {
  213. title: '操作类型',
  214. dataIndex: 'operationType',
  215. key: 'operationType',
  216. width: 120,
  217. },
  218. {
  219. title: '物料名称',
  220. dataIndex: 'materialName',
  221. key: 'materialName',
  222. width: 200,
  223. },
  224. {
  225. title: '数量',
  226. dataIndex: 'quantity',
  227. key: 'quantity',
  228. width: 100,
  229. },
  230. {
  231. title: '操作员',
  232. dataIndex: 'operator',
  233. key: 'operator',
  234. width: 100,
  235. },
  236. {
  237. title: '状态',
  238. dataIndex: 'status',
  239. key: 'status',
  240. width: 120,
  241. },
  242. ];
  243. // 最近操作记录
  244. const recentRecords = reactive([
  245. {
  246. key: '1',
  247. time: '2023-12-15 14:32',
  248. operationType: '入库',
  249. materialName: '集成电路芯片 IC-2023',
  250. quantity: '+500',
  251. operator: '张伟',
  252. status: '已完成',
  253. },
  254. {
  255. key: '2',
  256. time: '2023-12-15 13:45',
  257. operationType: '领料',
  258. materialName: '电阻 R-1KΩ 1/4W',
  259. quantity: '-200',
  260. operator: '李娜',
  261. status: '已完成',
  262. },
  263. {
  264. key: '3',
  265. time: '2023-12-15 11:20',
  266. operationType: '还料',
  267. materialName: '电容 C-100μF 50V',
  268. quantity: '+50',
  269. operator: '王强',
  270. status: '已完成',
  271. },
  272. {
  273. key: '4',
  274. time: '2023-12-15 09:15',
  275. operationType: '出库',
  276. materialName: '连接器 CONN-USB-C',
  277. quantity: '-150',
  278. operator: '赵敏',
  279. status: '已完成',
  280. },
  281. {
  282. key: '5',
  283. time: '2023-12-14 16:40',
  284. operationType: '入库',
  285. materialName: '传感器 SHT-30',
  286. quantity: '+300',
  287. operator: '孙磊',
  288. status: '已完成',
  289. },
  290. ]);
  291. // 底部操作按钮
  292. const outButtons = reactive([
  293. {
  294. label: '领料',
  295. icon: 'fa-hand-holding',
  296. color: 'blue',
  297. action: 'materialOut',
  298. },
  299. {
  300. label: '拣货',
  301. icon: 'fa-undo',
  302. color: 'green',
  303. action: 'materialReturn',
  304. },
  305. {
  306. label: '还料',
  307. icon: 'fa-arrow-up',
  308. color: 'yellow',
  309. action: 'materialIn',
  310. },
  311. {
  312. label: 'AGV RFID校验',
  313. icon: 'fa-wifi',
  314. color: 'red',
  315. action: 'agvRfidRecognition',
  316. },
  317. {
  318. label: '成品入库',
  319. icon: 'fa-boxes',
  320. color: 'blue',
  321. action: 'finishedProductIn',
  322. },
  323. // 测试待删除
  324. // {
  325. // label: '还料区管理',
  326. // icon: 'fa-undo',
  327. // color: 'green',
  328. // action: 'returnManagement',
  329. // },
  330. // {
  331. // label: '取料区管理',
  332. // icon: 'fa-arrow-down',
  333. // color: 'red',
  334. // action: 'deliverManagement',
  335. // },
  336. ]);
  337. const inButtons = reactive([
  338. {
  339. label: '出库确认',
  340. icon: 'fa-undo',
  341. color: 'blue',
  342. action: 'materialOutLeave',
  343. },
  344. {
  345. label: '还料离开',
  346. icon: 'fa-arrow-down',
  347. color: 'green',
  348. action: 'materialInLeave',
  349. },
  350. {
  351. label: '成品出库',
  352. icon: 'fa-boxes',
  353. color: 'red',
  354. action: 'finishedProductOut',
  355. },
  356. ]);
  357. // 查看全部记录逻辑
  358. const viewAllRecords = () => {
  359. console.log('查看全部');
  360. };
  361. // 拣货确认弹窗处理
  362. const handlePickingConfirm = () => {
  363. pickingModalVisible.value = false;
  364. router.push('/order-picking');
  365. };
  366. // 去领料
  367. const handlePickingCancel = () => {
  368. pickingModalVisible.value = false;
  369. router.push('/stock-requisition');
  370. };
  371. // 根据不同的action执行不同的逻辑
  372. const handleAction = action => {
  373. console.log('Action clicked:', action);
  374. switch (action) {
  375. case 'materialOut':
  376. // 领料 - 跳转到领料界面
  377. router.push('/stock-requisition');
  378. break;
  379. case 'materialReturn':
  380. // 拣货 - 显示确认弹窗
  381. pickingModalVisible.value = true;
  382. break;
  383. case 'materialOutLeave':
  384. // 领料出库离开
  385. router.push('/outbound-confirm');
  386. break;
  387. case 'materialReturnLeave':
  388. // 还料出库离开
  389. break;
  390. case 'agvRfidRecognition':
  391. // RFID识别
  392. router.push('/agv-rfid-recognition');
  393. break;
  394. case 'materialIn':
  395. // 还料 - 跳转到还料界面
  396. router.push('/in-confirm');
  397. break;
  398. case 'materialInLeave':
  399. // 还料出库离开
  400. validateMaterialInLeave();
  401. break;
  402. // 测试待删除
  403. case 'returnManagement':
  404. router.push('/return-management');
  405. break;
  406. case 'deliverManagement':
  407. router.push('/delivery-management');
  408. break;
  409. }
  410. };
  411. // 校验还料离开时是否带有工装设备信息
  412. const validateMaterialInLeave = async () => {
  413. loading.value = true;
  414. try {
  415. const res = await cfStockInLeave();
  416. if (res.errorCode === 0) {
  417. if (res.datas && res.datas.length > 0) {
  418. router.push('/in-confirm?isLeave=true');
  419. } else {
  420. message.success('您已完成还料操作,请在闸机开门后离开');
  421. }
  422. } else {
  423. message.warning(res.errorMessage);
  424. }
  425. } catch (error) {
  426. console.error('校验还料离开时是否带有工装设备信息失败:', error);
  427. message.error('校验还料离开时是否带有工装设备信息失败');
  428. } finally {
  429. loading.value = false;
  430. }
  431. };
  432. // 渲染样式
  433. const getOperationTypeClass = type => {
  434. const classMap = {
  435. '入库': 'bg-green-100 text-green-800',
  436. '领料': 'bg-blue-100 text-blue-800',
  437. '还料': 'bg-yellow-100 text-yellow-800',
  438. '出库': 'bg-red-100 text-red-800',
  439. };
  440. return classMap[type] || 'bg-gray-100 text-gray-800';
  441. };
  442. // 按钮样式
  443. const getButtonClass = color => {
  444. const colorMap = {
  445. blue: 'bg-blue-500 hover:bg-blue-600',
  446. green: 'bg-green-500 hover:bg-green-600',
  447. yellow: 'bg-yellow-500 hover:bg-yellow-600',
  448. red: 'bg-red-500 hover:bg-red-600',
  449. };
  450. return colorMap[color] || 'bg-gray-500 hover:bg-gray-600';
  451. };
  452. // 统计样式
  453. const getStatColorClasses = (color, type) => {
  454. const colorMap = {
  455. blue: {
  456. border: 'border-blue-500',
  457. bg: 'bg-blue-100',
  458. text: 'text-blue-500',
  459. },
  460. green: {
  461. border: 'border-green-500',
  462. bg: 'bg-green-100',
  463. text: 'text-green-500',
  464. },
  465. yellow: {
  466. border: 'border-yellow-500',
  467. bg: 'bg-yellow-100',
  468. text: 'text-yellow-500',
  469. },
  470. purple: {
  471. border: 'border-purple-500',
  472. bg: 'bg-purple-100',
  473. text: 'text-purple-500',
  474. },
  475. };
  476. return colorMap[color]?.[type] || '';
  477. };
  478. </script>
  479. <style scoped>
  480. /* Additional component-specific styles if needed */
  481. </style>