初版作成

This commit is contained in:
梁州 2024-06-21 08:16:12 +08:00
parent e400c7d64f
commit f41f130077
57 changed files with 29995 additions and 2 deletions

23
.gitignore vendored Normal file
View File

@ -0,0 +1,23 @@
.DS_Store
node_modules
/dist
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

View File

@ -1,3 +1,24 @@
# wms_client_miniload_bk7
# helloworld
宝开7号厂房miniload客户端代码
## Project setup
```
npm install
```
### Compiles and hot-reloads for development
```
npm run serve
```
### Compiles and minifies for production
```
npm run build
```
### Lints and fixes files
```
npm run lint
```
### Customize configuration
See [Configuration Reference](https://cli.vuejs.org/config/).

5
babel.config.js Normal file
View File

@ -0,0 +1,5 @@
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset'
]
}

19
jsconfig.json Normal file
View File

@ -0,0 +1,19 @@
{
"compilerOptions": {
"target": "es5",
"module": "esnext",
"baseUrl": "./",
"moduleResolution": "node",
"paths": {
"@/*": [
"src/*"
]
},
"lib": [
"esnext",
"dom",
"dom.iterable",
"scripthost"
]
}
}

23808
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

57
package.json Normal file
View File

@ -0,0 +1,57 @@
{
"name": "WMS",
"version": "0.1.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint"
},
"dependencies": {
"axios": "^1.3.3",
"core-js": "^3.8.3",
"element-plus": "^2.4.0",
"file-saver": "^2.0.5",
"moment": "^2.29.4",
"node-polyfill-webpack-plugin": "^2.0.1",
"qrcode": "^1.5.3",
"qrcode.vue": "^3.4.1",
"stream-http": "^3.2.0",
"vue": "^3.2.13",
"vue-print-nb": "^1.7.5",
"vue-router": "^4.0.3",
"vue3-print-nb": "^0.1.4",
"vuex": "^4.0.0",
"xlsx": "^0.18.5"
},
"devDependencies": {
"@babel/core": "^7.12.16",
"@babel/eslint-parser": "^7.12.16",
"@vue/cli-plugin-babel": "~5.0.0",
"@vue/cli-plugin-router": "~5.0.0",
"@vue/cli-plugin-vuex": "~5.0.0",
"@vue/cli-service": "~5.0.0"
},
"eslintConfig": {
"root": true,
"env": {
"node": true
},
"extends": [
"plugin:vue/vue3-essential",
"eslint:recommended"
],
"parserOptions": {
"parser": "@babel/eslint-parser"
},
"rules": {
"no-unused-vars": "off"
}
},
"browserslist": [
"> 1%",
"last 2 versions",
"not dead",
"not ie 11"
]
}

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

17
public/index.html Normal file
View File

@ -0,0 +1,17 @@
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>

55
src/App.vue Normal file
View File

@ -0,0 +1,55 @@
<template>
<router-view></router-view>
</template>
<script>
export default {
created() {
//sessionStorage
if (sessionStorage.getItem('storeState')) {
//replaceStatestore
this.$store.replaceState(Object.assign({}, this.$store.state, JSON.parse(sessionStorage.getItem('storeState'))))
}
//vuexsessionStorage
window.addEventListener('beforeunload', () => {
sessionStorage.setItem('storeState', JSON.stringify(this.$store.state))
})
}
}
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
height: 100%;
width: 100%;
}
nav {
padding: 30px;
}
nav a {
font-weight: bold;
color: #2c3e50;
}
nav a.router-link-exact-active {
color: #42b983;
}
html {
height: 100%;
width: 100%;
}
body {
margin: 0px;
height: 100%;
width: 100%;
}
</style>

39
src/api/config.js Normal file
View File

@ -0,0 +1,39 @@
import request from "@/http/request";
const getConfigs = () => {
return request.get('/config/getConfigs')
}
const updateConfig = (params) => {
return request.post('/config/updateConfig', params)
}
const queryBoxConfig = (params) => {
return request.post('/config/queryBoxConfig', params)
}
const queryBoxConfigByPage = (params) => {
return request.post('/config/queryBoxConfigByPage', params)
}
const updateBoxConfig = (params) => {
return request.post('/config/updateBoxConfig', params)
}
const addBoxConfig = (params) => {
return request.post('/config/addBoxConfig', params)
}
const deleteBoxConfig = (params) => {
return request.post('/config/deleteBoxConfig', params)
}
export {
getConfigs,
updateConfig,
queryBoxConfig,
queryBoxConfigByPage,
updateBoxConfig,
addBoxConfig,
deleteBoxConfig
}

183
src/api/excel.js Normal file
View File

@ -0,0 +1,183 @@
import request from "@/http/request";
const downlocadExcel = () => {
return request.get('/test/testExcelExport', {
responseType: 'blob'
})
}
const uploadExcel = (formData) => {
return request({
url: '/test/testExcelImport',
method: 'post',
data: formData,
timeout: 100000
})
}
const uploadExcelPeijian = (data) => {
return request({
url: '/excel/uploadExcelPeijian',
method: 'post',
data: data,
timeout: 100000
})
}
const uploadExcelHejian = (data) => {
return request({
url: '/excel/uploadExcelHejian',
method: 'post',
data: data,
timeout: 100000
})
}
const uploadExcelJinji = (data) => {
return request({
url: '/excel/uploadExcelJinji',
method: 'post',
data: data,
timeout: 100000
})
}
const uploadExcelKatePackage = (data) => {
return request({
url: '/excel/uploadExcelKatePackage',
method: 'post',
data: data,
timeout: 100000
})
}
const uploadExcelParts = (data) => {
return request({
url: '/excel/uploadStocks',
method: 'post',
data: data,
timeout: 100000
})
}
const uploadBoxConfig = (data) => {
return request({
url: '/excel/uploadBoxConfig',
method: 'post',
data: data,
timeout: 100000
})
}
const distributePeijianTasks = (data) => {
return request({
url: '/excel/distributePeijianTasks',
method: 'post',
data: data,
timeout: 100000
})
}
const distributeHejianTasks = (data) => {
return request({
url: '/excel/distributeHejianTasks',
method: 'post',
data: data,
timeout: 100000
})
}
const distributeJinjiTasks = (data) => {
return request({
url: '/excel/distributeJinjiTasks',
method: 'post',
data: data,
timeout: 100000
})
}
const downloadStockExcel = () => {
return request({
url: '/excel/downloadStockExcel',
method: 'get',
responseType: 'blob'
})
}
const downloadKateTaskExcel = () => {
return request({
url: '/excel/downloadKateTaskExcel',
method: 'get',
responseType: 'blob'
})
}
const downloadPeijianExcel = () => {
return request({
url: '/excel/downloadPeijianExcel',
method: 'get',
responseType: 'blob'
})
}
const downloadRukuExcel = () => {
return request({
url: '/excel/downloadRukuExcel',
method: 'get',
responseType: 'blob'
})
}
const downloadHejianExcel = () => {
return request({
url: '/excel/downloadHejianExcel',
method: 'get',
responseType: 'blob'
})
}
const downloadJinjiExcel = () => {
return request({
url: '/excel/downloadJinjiExcel',
method: 'get',
responseType: 'blob'
})
}
const downloadPackageExcel = () => {
return request({
url: '/excel/downloadPackageExcel',
method: 'get',
responseType: 'blob'
})
}
const downloadMaterialExcel = () => {
return request({
url: '/excel/downloadMaterialExcel',
method: 'get',
responseType: 'blob'
})
}
export {
downlocadExcel,
uploadExcel,
uploadExcelPeijian,
uploadExcelHejian,
uploadExcelParts,
distributePeijianTasks,
distributeHejianTasks,
downloadStockExcel,
downloadKateTaskExcel,
uploadExcelJinji,
distributeJinjiTasks,
downloadPeijianExcel,
downloadRukuExcel,
downloadHejianExcel,
downloadJinjiExcel,
downloadPackageExcel,
uploadBoxConfig,
uploadExcelKatePackage,
downloadMaterialExcel
}

66
src/api/goods.js Normal file
View File

@ -0,0 +1,66 @@
import request from "@/http/request";
const getAllGoods = () => {
return request({
url: '/goods/getAllGoods',
method: 'get'
})
}
const getPartInfo = (params) => {
return request({
url: '/goods/getPartInfo',
method: 'post',
data: params
})
}
const updateGoodsInfo = (params) => {
return request({
url: '/goods/updateGoodsInfo',
method: 'post',
data: params
})
}
const queryPartInfoByPartNo = (params) => {
return request({
url: '/goods/queryPartInfoByPartNo',
method: 'post',
data: params
})
}
const updatePartInfo = (params) => {
return request({
url: '/goods/updatePartInfo',
method: 'post',
data: params
})
}
const queryPartNo = (params) => {
return request({
url: '/goods/queryPartNo',
method: 'post',
data: params
})
}
const deleteCurrentPartInfo = (params) => {
return request({
url: '/goods/deletePartInfo',
method: 'post',
data: params
})
}
export {
getAllGoods,
updateGoodsInfo,
queryPartInfoByPartNo,
getPartInfo,
updatePartInfo,
queryPartNo,
deleteCurrentPartInfo
}

31
src/api/location.js Normal file
View File

@ -0,0 +1,31 @@
import request from "@/http/request";
const getLocations = (params) => {
return request({
url: '/location/getLocations',
method: 'post',
data: params
})
}
const updateLocation = (params) => {
return request({
url: '/location/updateLocation',
method: 'post',
data: params
})
}
const getAvailableLocations = (params) => {
return request({
url: '/location/getAvailableLocations',
method: 'post',
data: params
})
}
export {
getLocations,
updateLocation,
getAvailableLocations
}

31
src/api/login.js Normal file
View File

@ -0,0 +1,31 @@
import request from "@/http/request";
const loginWithoutAuth = (params) => {
return request({
url: '/user/loginWithoutAuth',
method: 'post',
data: params
})
}
const loginWithAuth = (params) => {
return request({
url: '/user/loginWithAuth',
method: 'post',
data: params
})
}
const getUser = (params) => {
return request({
url: '/login/getUser',
method: 'post',
data: params
})
}
export {
loginWithoutAuth,
loginWithAuth,
getUser
}

13
src/api/record.js Normal file
View File

@ -0,0 +1,13 @@
import request from "@/http/request";
const getTaskRecords = (params) => {
return request({
url: '/record/getTaskRecords',
method: 'post',
data: params
})
}
export {
getTaskRecords
}

22
src/api/stand.js Normal file
View File

@ -0,0 +1,22 @@
import request from "@/http/request";
const getAllStands = (params) => {
return request({
url: '/stand/getAllStands',
method: 'post',
data: params
})
}
const updateStandInfo = (params) => {
return request({
url: '/stand/updateStandInfo',
method: 'post',
data: params
})
}
export {
getAllStands,
updateStandInfo
}

22
src/api/stock.js Normal file
View File

@ -0,0 +1,22 @@
import request from "@/http/request";
const getAllStocks = (params) => {
return request({
url: '/stock/getAllStocks',
method: 'post',
data: params
})
}
const updateStockInfo = (params) => {
return request({
url: '/stock/updateStockInfo',
method: 'post',
data: params
})
}
export {
getAllStocks,
updateStockInfo
}

102
src/api/task.js Normal file
View File

@ -0,0 +1,102 @@
import request from "@/http/request";
const sendGoodsInTask = (params) => {
return request({
url: '/task/sendGoodsInTask',
method: 'post',
data: params
})
}
const sendGoodsOutTask = (params) => {
return request({
url: '/task/sendGoodsOutTask',
method: 'post',
data: params
})
}
const getAllTasks = () => {
return request({
url: '/task/getAllTasks',
method: 'get'
})
}
const sendInventoryTask = (params) => {
return request({
url: '/task/sendInventoryTask',
method: 'post',
data: params
})
}
const finishInventoryTask = (params) => {
return request({
url: '/task/finishInventoryTask',
method: 'post',
data: params
})
}
const getTasks = (params) => {
return request({
url: '/task/getTasks',
method: 'post',
data: params
})
}
const finishPicking = (params) => {
return request({
url: '/task/finishPicking',
method: 'post',
data: params
})
}
const getTaskByTask = (params) => {
return request({
url: '/task/getTaskByTask',
method: 'post',
data: params
})
}
const changeTaskStatus = (params) => {
return request({
url: '/taskDeal/changeTaskStatus',
method: 'post',
data: params
})
}
const queryNotConfirmScanInfo = (params) => {
return request({
url: '/task/queryNotConfirmScanInfo',
method: 'post',
data: params
})
}
const solveScanDifference = (params) => {
return request({
url: '/task/solveScanDifference',
method: 'post',
data: params
})
}
export {
sendGoodsInTask,
sendGoodsOutTask,
getAllTasks,
sendInventoryTask,
finishInventoryTask,
getTasks,
finishPicking,
getTaskByTask,
changeTaskStatus,
queryNotConfirmScanInfo,
solveScanDifference
}

59
src/api/user.js Normal file
View File

@ -0,0 +1,59 @@
import request from "@/http/request";
// 登录方法
export function login(username, password, code, uuid) {
const data = {
username,
password,
code,
uuid
}
return request({
url: '/login',
headers: {
isToken: false
},
method: 'post',
data: data
})
}
// 注册方法
export function register(data) {
return request({
url: '/register',
headers: {
isToken: false
},
method: 'post',
data: data
})
}
// 获取用户详细信息
export function getInfo() {
return request({
url: '/getInfo',
method: 'get'
})
}
// 退出方法
export function logout() {
return request({
url: '/logout',
method: 'post'
})
}
// 获取验证码
export function getCodeImg() {
return request({
url: '/captchaImage',
headers: {
isToken: false
},
method: 'get',
timeout: 20000
})
}

31
src/api/vehicle.js Normal file
View File

@ -0,0 +1,31 @@
import request from "@/http/request";
const getAllVehicles = (params) => {
return request({
url: '/location/getVehicles',
method: 'post',
data: params
})
}
const updateVehicleInfo = (params) => {
return request({
url: '/location/updateVehicleInfo',
method: 'post',
data: params
})
}
const deleteCurrentVehicle = (params) => {
return request({
url: '/location/deleteVehicle',
method: 'post',
data: params
})
}
export {
getAllVehicles,
updateVehicleInfo,
deleteCurrentVehicle
}

13
src/api/wmsLog.js Normal file
View File

@ -0,0 +1,13 @@
import request from "@/http/request";
const queryLogs = (params) => {
return request({
url: '/log/queryWmsLog',
method: 'post',
data: params
})
}
export {
queryLogs
}

BIN
src/assets/fdbk_log.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

BIN
src/assets/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

71
src/components/appTag.vue Normal file
View File

@ -0,0 +1,71 @@
// AppTag
<template>
<div class="app-tag">
<el-tag closable size="default" v-for="(tag, index) in tags" :key="tag.labelName" :disable-transitions="true"
:effect="$route.path === tag.path ? 'dark' : 'plain'" @close="handleClose(tag, index)"
@click="handleClick(tag)">
{{ tag.labelName }}
</el-tag>
</div>
</template>
<script>
import { mapState, mapMutations } from 'vuex';
export default {
name: 'app-tag',
data() {
return {
tags: []
}
},
created() {
//stateTagsListstate.jstagskeystateTagsList
this.tags = this.stateTagsList;
},
computed: {
...mapState(['stateTagsList'])
},
methods: {
...mapMutations({
close: 'mutationCloseTag'
}),
handleClose(tag, index) {
if (this.tags.length === 1) { //
return
}
this.close(tag) // tag
if (this.$router.path === tag.path) { //
return
} else {
if (index === (this.tags.length - 1)) { // ,
this.$router.push(this.tags[index].path)
} else { //
if (index === 0) {
this.$router.push(this.tags[0].path)
} else {
this.$router.push(this.tags[index - 1].path)
}
}
}
},
// tags
handleClick(tag) {
this.$router.push(tag.path)
}
}
}
</script>
<style scoped>
.app-tag .el-tag {
cursor: pointer;
height: 30px;
margin-right: 2px;
}
.app-tag .el-tag:hover {
color: #000;
background-color: #5A9CF8;
}
</style>

View File

@ -0,0 +1,58 @@
<template>
<div class="menu-title">主菜单</div>
<!-- 侧边栏菜单区域 -->
<el-menu active-text-color="#409Eff" background-color="#fff" text-color="#000" :router="true" unique-opened>
<!-- 一级菜单 -->
<el-sub-menu :index="item.id" v-for="item in menuList" :key="item.id">
<!-- 一级菜单模板区域 -->
<template #title>
<el-icon>
<component :is="item.iconValue"></component>
</el-icon>
<span>{{ item.labelName }}</span>
</template>
<!-- 二级菜单 -->
<el-menu-item :index="subItem.path" v-for="subItem in item.children" :key="subItem.id"
@click="clickMenu(subItem)">
<template #title>
<span>{{ subItem.labelName }}</span>
</template>
</el-menu-item>
</el-sub-menu>
</el-menu>
</template>
<script setup>
import store from '@/store'
// import { mapState, mapMutations } from 'vuex';
</script>
<script>
export default {
name: 'side-menu',
data() {
return {
menuList: []
}
},
mounted() {
this.getMenuList()
},
methods: {
getMenuList() {
// this.menuList = this.$store.state.menuList
this.menuList = store.getters.getMenuList
},
//
clickMenu(value) {
//vuexstore
store.commit('mutationSelectTags', value)
}
}
}
</script>
<style scoped>
.menu-title {
margin-top: 5px;
}
</style>

55
src/demo/excelDemo.vue Normal file
View File

@ -0,0 +1,55 @@
<template>
<el-upload :on-preview="previewFile" :limit="1" :on-change="changeFile" :auto-upload="false" :data="uploadForm.data">
<template #trigger>
<el-button size="small" type="primary">选取文件</el-button>
</template>
<el-button style="margin-left: 10px;" size="small" type="success" @click="submitUpload">确认导入</el-button>
</el-upload>
</template>
<script setup>
import { ref, reactive } from 'vue';
import { uploadExcel } from '@/api/excel.js'
import { ElMessage } from 'element-plus'
const file = ref()
const uploadForm = reactive({
data: {
fileId: '',
name: '',
type: ''
}
})
const changeFile = (uploadFile) => {
file.value = uploadFile
}
const submitUpload = () => {
if (uploadForm == undefined || file.value == undefined) {
ElMessage.error('请选择文件之后再导入')
}
const jsonStr = JSON.stringify(uploadForm.data);
const blob = new Blob([jsonStr], {
type: 'application/json'
});
let formData = new FormData();
// file.value.rawfilefile.valueProxy
formData.append("file", file.value.raw);
uploadExcel(formData).then(res => {
console.log(res.data)
if (res.data.code == 0) {
ElMessage({
message: '导入成功',
type: 'success',
})
} else {
ElMessage.error(res.data.message)
}
}).catch(err => {
console.log(err)
ElMessage.error('导入错误')
})
}
const previewFile = () => {
}
</script>
<style scoped></style>

View File

@ -0,0 +1,58 @@
<template>
<el-upload ref="uploadRef" :on-preview="previewFile" :limit="1" :on-change="changeFile" :auto-upload="false" :data="uploadForm.data">
<template #trigger>
<el-button type="primary">选取文件</el-button>
</template>
<el-button style="margin-left: 10px;" type="success" @click="uploadParts">确认导入库存数据</el-button>
</el-upload>
</template>
<script setup>
import { ref, reactive } from 'vue';
import { uploadExcelParts } from '@/api/excel.js'
import { ElMessage } from 'element-plus'
const file = ref()
const uploadForm = reactive({
data: {
fileId: '',
name: '',
type: ''
}
})
const changeFile = (uploadFile) => {
file.value = uploadFile
}
const uploadRef = ref()
const uploadParts = () => {
if (uploadForm == undefined || file.value == undefined) {
ElMessage.error('请选择文件之后再导入')
}
const jsonStr = JSON.stringify(uploadForm.data);
const blob = new Blob([jsonStr], {
type: 'application/json'
});
let formData = new FormData();
// file.value.rawfilefile.valueProxy
formData.append("file", file.value.raw);
uploadExcelParts(formData).then(res => {
if (res.data.code == 0) {
ElMessage({
message: '导入成功',
type: 'success',
})
//
uploadRef.value.clearFiles()
file.value = undefined
} else {
ElMessage.error(res.data.message)
}
}).catch(err => {
console.log(err)
ElMessage.error('导入错误')
})
}
const previewFile = () => {
}
</script>
<style scoped></style>

46
src/http/request.js Normal file
View File

@ -0,0 +1,46 @@
import axios from 'axios'
const request = axios.create({
baseURL: 'http://10.18.61.7:12315/wms',
timeout: 5000
})
// axios.defaults.baseURL = 'http://10.18.61.7:12315/wms'
// axios.defaults.baseURL = 'http://localhost:12315/wms'
// axios.defaults.baseURL = 'http://10.30.9.89:12315/wms'
// // request 请求器
// // 可以自请求发送前对请求做一些处理
// // 比如统一加token对请求参数统一加密
// request.interceptors.request.use(config => {
// if (config && config.headers) {
// config.headers['Content-Type'] = 'application/json;charset=utf-8';
// }
// // config.headers['token'] = user.token; // 设置请求头
// return config
// }, error => {
// return Promise.reject(error)
// });
// // response 拦截器
// // 可以在接口响应后统一处理结果
// request.interceptors.response.use(
// response => {
// let res = response.data;
// // 如果是返回的文件
// if (response.config.responseType === 'blob') {
// return res
// }
// // 兼容服务端返回的字符串数据
// if (typeof res === 'string') {
// res = res ? JSON.parse(res) : res
// }
// return res;
// },
// error => {
// console.log('err' + error) // for debug
// return Promise.reject(error)
// }
// )
export default request

145
src/layout/config.vue Normal file
View File

@ -0,0 +1,145 @@
<template>
<el-config-provider :locale="zhCn">
<el-container class="content">
<fieldset class="input-area">
<legend>系统配置</legend>
<div v-for="config in configs">
<el-form :model="config" :label-position="'top'" label-width="100px" style="max-width: 544px"
:rules="rules" status-icon>
<el-row :gutter="16">
<el-col :span="12" :offset="1" v-if="config.configKey == 'MAIL_ADDRESS'">
<el-form-item label="邮箱账户(账户之间用;隔开)">
<div>
<el-input v-model="config.configValue" @change="updateCurrentConfig(config)" />
</div>
</el-form-item>
</el-col>
<el-col :span="12" :offset="1"
v-if="config.configType == '1' && config.configKey != 'MAIL_ADDRESS'">
<el-form-item :label="config.configName">
<div>
<el-input v-model="config.configValue" @change="updateCurrentConfig(config)" />
</div>
</el-form-item>
</el-col>
<el-col :span="12" :offset="1"
v-if="config.configType == '2' && config.configKey != 'MAIL_ADDRESS'">
<el-form-item :label="config.configName">
<div v-if="config.configType == '2'">
<el-select v-model="config.configValue" multiple collapse-tags collapse-tags-tooltip
:placeholder="'请选择' + config.configName" @change="updateCurrentConfig(config)">
<el-option v-for="(value, index) in mails" :key="index" :label="value"
:value="value" />
</el-select>
</div>
</el-form-item>
</el-col>
</el-row>
</el-form>
</div>
</fieldset>
</el-container>
</el-config-provider>
</template>
<script setup>
import zhCn from 'element-plus/dist/locale/zh-cn.mjs'
import { getConfigs, updateConfig } from '@/api/config.js'
import { ElMessage, ElLoading } from 'element-plus'
</script>
<script>
export default {
name: 'config',
data() {
return {
configs: [],
mails: [],
rules: {}
}
},
mounted() {
this.getAllConfigs()
},
methods: {
getAllConfigs() {//
const loading = ElLoading.service({
lock: true,
text: 'Loading',
background: 'rgba(0, 0, 0, 0.7)',
})
getConfigs().then(res => {
loading.close()
this.configs = res.data
this.configs.forEach((config) => {
if (config.configType == '2') {
const tempArray = config.configValue.split(';')
config.configValue = tempArray
}
if (config.configKey == 'MAIL_ADDRESS') {
const tempArray = config.configValue.split(';')
this.mails = tempArray
}
})
}).catch(err => {
console.log(err)
loading.close()
ElMessage.error('查询系统配置失败!')
})
},
updateCurrentConfig(config) {//
// config
const param = Object.assign({}, config)
const loading = ElLoading.service({
lock: true,
text: 'Loading',
background: 'rgba(0, 0, 0, 0.7)',
})
if (param.configType == '2') {
param.configValue = param.configValue.join(';')
}
updateConfig(param).then(res => {
loading.close()
if (res.data.code == 0) {
ElMessage({
message: '更新系统配置成功!',
type: 'success',
})
} else {
ElMessage.error(res.data.message)
}
}).catch(err => {
loading.close()
ElMessage.error('更新系统配置失败!')
})
}
}
}
</script>
<style scoped>
.content {
display: flex;
width: 100%;
}
.input-area {
margin: 10px;
width: 100%;
height: 85%;
border: solid 1px;
border-radius: 10px;
box-shadow: 0px 15px 10px -15px #000;
}
.el-row .el-form-item .el-select {
width: 512px;
}
.el-row .el-form-item .el-input {
width: 512px;
}
.el-row .el-form-item .el-button {
margin: auto;
}
</style>

363
src/layout/goods.vue Normal file
View File

@ -0,0 +1,363 @@
<template>
<div style="margin-bottom: 10px">
<el-config-provider :locale="zhCn">
<el-row>
<UploadExcelPart></UploadExcelPart>
</el-row>
<el-row style="margin-top: 10px;">
<el-input v-model="queryKey" style="width: 256px; margin-right: 10px;" placeholder="零件号" />
<el-button type="primary" @click="search()">搜索</el-button>
<el-button type="warning" @click="reset()">重置</el-button>
<el-button type="success" @click="search()">刷新</el-button>
<el-button type="success" @click="exportExcel()">导出信息</el-button>
</el-row>
<br />
<el-table :data="partInfos" stripe border v-loading="loading" class="table-class" max-height="650px"
highlight-current-row @row-click="getCurrentRow" :header-cell-style="{ 'text-align': 'center' }"
:cell-style="{ 'text-align': 'center' }">
<el-table-column width="65px" fixed="left">
<template v-slot="scope">
<el-radio :label="scope.row.material" v-model="material">&nbsp;</el-radio>
</template>
</el-table-column>
<el-table-column prop="material" label="零件号" fixed="left" sortable min-width="120px" />
<el-table-column prop="itemDesc" label="描述" fixed="left" min-width="120px" />
<el-table-column prop="category" label="零件类型" fixed="left" sortable min-width="120px" />
<el-table-column prop="categoryRemark" label="策略" min-width="120px" />
<el-table-column prop="unloadPlace" label="卸货点" min-width="120px" />
<el-table-column prop="kittingPoint" label="配料点" min-width="120px" />
<el-table-column prop="property" label="可用性" min-width="120px" />
<el-table-column prop="vendorId" label="供应商代码" min-width="120px" />
<el-table-column prop="dataOwner" label="数据负责人" min-width="120px" />
<el-table-column prop="partWeight" label="重量" min-width="120px" />
<el-table-column prop="storageLocation" label="库位" min-width="120px" />
<el-table-column prop="storageType" label="存储类型" min-width="120px" />
<el-table-column prop="storageBin" label="BIN位" min-width="120px" />
<el-table-column prop="vendorNameEN" label="供应商名称(英文)" min-width="120px" />
<el-table-column prop="vendorNameCN" label="供应商名称(中文)" min-width="120px" />
<el-table-column prop="vendorCountry" label="供应商国家/地区" min-width="120px" />
<el-table-column prop="SLED" label="SLED" min-width="120px" />
<el-table-column prop="updateDate" label="更新日期" min-width="120px" />
<el-table-column fixed="right" label="操作" width="240px">
<template v-slot="scope">
<el-button plain type="primary" @click="editCurrentRowGoods(scope.row)">编辑</el-button>
<el-button plain type="danger" @click="deleteCurrentRowGoods(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<br />
<el-pagination v-model:current-page="currentPage" v-model:page-size="pageSize" :page-sizes="[10, 25, 50]"
:small="false" :disabled="false" :background="false" :default-page-size="10"
layout="total, sizes, prev, pager, next, jumper" :total="total" @size-change="search"
@current-change="search" />
<el-dialog v-model="dialogVisible" title="物料信息" width="40%" draggable :show-close="false">
<el-form ref="goodsFormRef" :model="goodsFormEntity" :label-position="labelPosition" label-width="100px"
style="max-width: 100%" :rules="rules" status-icon>
<el-row :gutter="16">
<el-col :span="12" :offset="0">
<el-form-item label="零件号" prop="material">
<el-input v-model="goodsFormEntity.material" disabled />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="描述" prop="itemDesc">
<el-input v-model="goodsFormEntity.itemDesc" clearable />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="16">
<el-col :span="12" :offset="0">
<el-form-item label="零件类型" prop="category">
<el-input v-model="goodsFormEntity.category" clearable />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="策略" prop="categoryRemark">
<el-input v-model="goodsFormEntity.categoryRemark" clearable />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="16">
<el-col :span="12" :offset="0">
<el-form-item label="卸货点" prop="unloadPlace">
<el-input v-model="goodsFormEntity.unloadPlace" clearable />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="配料点" prop="kittingPoint">
<el-input v-model="goodsFormEntity.kittingPoint" clearable />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="16">
<el-col :span="12" :offset="0">
<el-form-item label="可用性" prop="property">
<el-input v-model="goodsFormEntity.property" clearable />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="供应商代码" prop="vendorId">
<el-input v-model="goodsFormEntity.vendorId" clearable />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="16">
<el-col :span="12" :offset="0">
<el-form-item label="数据负责人" prop="dataOwner">
<el-input v-model="goodsFormEntity.dataOwner" clearable />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="重量" prop="partWeight">
<el-input v-model="goodsFormEntity.partWeight" clearable />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="16">
<el-col :span="12" :offset="0">
<el-form-item label="库位" prop="storageLocation">
<el-input v-model="goodsFormEntity.storageLocation" clearable />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="存储类型" prop="storageType">
<el-input v-model="goodsFormEntity.storageType" clearable />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="16">
<el-col :span="12" :offset="0">
<el-form-item label="BIN位" prop="storageBin">
<el-input v-model="goodsFormEntity.storageBin" clearable />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="供应商名称(英文)" prop="vendorNameEN">
<el-input v-model="goodsFormEntity.vendorNameEN" clearable />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="16">
<el-col :span="12" :offset="0">
<el-form-item label="供应商名称(中文)" prop="vendorNameCN">
<el-input v-model="goodsFormEntity.vendorNameCN" clearable />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="供应商国家/地区" prop="vendorCountry">
<el-input v-model="goodsFormEntity.vendorCountry" clearable />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="16">
<el-col :span="12" :offset="0">
<el-form-item label="SLED" prop="SLED">
<el-input-number v-model.number="goodsFormEntity.SLED" clearable controls-position="right"
:min="0" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="更新日期" prop="updateDate">
<el-input v-model="goodsFormEntity.updateDate" clearable />
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="submitGoodsInfo(goodsFormEntity)">
确定
</el-button>
</span>
</template>
</el-dialog>
</el-config-provider>
</div>
</template>
<script setup>
import { getPartInfo, updatePartInfo, deleteCurrentPartInfo } from '@/api/goods.js'
import zhCn from 'element-plus/dist/locale/zh-cn.mjs'
import { ElMessage } from 'element-plus'
import { ref, reactive } from 'vue'
import { dateFormatter } from '@/utils/formatter.js'
import UploadExcelPart from '@/excel/UploadExcelPart.vue'
import { downloadMaterialExcel } from '@/api/excel.js'
</script>
<script>
export default {
name: 'goods',
data() {
return {
partInfos: [],
pageInfo: {},
currentPage: 1,
pageSize: 10,
total: 0,
queryKey: '',
loading: true,
dialogVisible: false,
material: '',
goodsFormEntity: reactive({}),
labelPosition: 'top',
goodsFormRef: ref(),
rules: reactive({})
}
},
mounted() {
this.search()
},
methods: {
search() {
this.loading = true
this.pageInfo.pageNum = this.currentPage
this.pageInfo.pageSize = this.pageSize
const tableRequest = {
page: this.pageInfo,
param: {
material: this.queryKey.trim()
},
}
getPartInfo(tableRequest).then(res => {
const tableResponse = res.data
if (tableResponse.code != 0) {
console.log(tableResponse.code + ':' + tableResponse.message)
ElMessage.error(tableResponse.message)
}
this.partInfos = tableResponse.rows
this.total = tableResponse.total
}).catch(err => {
ElMessage.error('查询物料错误' + err.message)
})
this.loading = false
},
dateFormat: (row, column, cellValue, index) => {
return dateFormatter(cellValue)
},
reset() {
this.queryKey = ''
this.search
},
// getCurrentPageGoods() {
// this.currentGoods = this.displayGoods.slice((this.currentPage - 1) * this.pageSize, this.currentPage * this.pageSize)
// },
editCurrentRowGoods(row) {
this.goodsFormEntity = row
this.dialogVisible = true
},
deleteCurrentRowGoods(row) {
this.material = row.material
const goods = {
material: row.material
}
deleteCurrentPartInfo(goods).then(res => {
if (res.data.code == 0) {
ElMessage({
message: '删除零件信息成功',
type: 'success',
})
this.search()
} else {
ElMessage.error(res.data.message)
}
}).catch(err => {
ElMessage.error('删除零件信息失败:' + err)
})
},
submitGoodsInfo(formData) {
updatePartInfo(formData).then(res => {
if (res.data.code == 0) {
this.dialogVisible = false
ElMessage({
message: '更新零件信息成功',
type: 'success',
})
this.search()
} else {
ElMessage.error(res.data.message)
}
}).catch(err => {
ElMessage.error('更新零件信息失败')
})
},
getCurrentRow(row) {
this.goodsId = row.goodsId
},
exportExcel() {
downloadMaterialExcel().then(res => {
const link = document.createElement('a');//a
try {
// let blob = new Blob([res.data],{type: 'application/vnd.ms-excel'}); //blobblobtypexls
let blob = res.data //blob
// let _fileName = res.headers['content-disposition'].split(';')[1].split('=')[1]; //
let _fileName = "导出物料信息" + dateFormatter(new Date) + ".xlsx"
link.style.display = 'none'//
// URL
const url = window.URL || window.webkitURL || window.moxURL
link.href = url.createObjectURL(blob)
link.setAttribute('download', _fileName.substring(_fileName.lastIndexOf('_') + 1))
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
url.revokeObjectURL(link.href)//url
} catch (e) {
ElMessage({
message: '下载文件失败:: ' + e,
type: 'error',
showClose: true
})
}
}).catch(err => {
ElMessage({
message: '导出失败:: ' + err,
type: 'error',
showClose: true
})
})
},
},
// watch: {
// currentPage() {
// this.getCurrentPageGoods()
// },
// pageSize() {
// this.getCurrentPageGoods()
// }
// }
}
</script>
<style scoped>
.el-pagination {
padding-left: 5px;
}
.el-row .el-button {
width: 72px;
margin-left: 0px;
margin-right: 5px;
}
.table-class {
width: 100%;
}
.el-row .el-form-item {
width: 10% inherit;
justify-content: center;
}
.el-row .el-form-item .el-select-v2 {
width: 100% !important;
}
.el-row .el-form-item .el-input-number {
width: 100% !important;
}
.el-row .el-form-item .el-button {
margin: auto;
}
</style>

328
src/layout/goodsIn.vue Normal file
View File

@ -0,0 +1,328 @@
<template>
<el-config-provider :locale="zhCn">
<el-container class="content">
<div class="left">
<fieldset class="display-area">
<legend>
入库暂存信息
</legend>
<el-table :data="tempTasks" stripe border class="table-class" max-height="684px"
:header-cell-style="{ 'text-align': 'center' }" :cell-style="{ 'text-align': 'center' }">
<el-table-column prop="goodsType" label="型号" fixed="left" min-width="120px" />
<el-table-column prop="specification" label="规格" fixed="left" min-width="120px" />
<el-table-column prop="batchNo" label="批次号" min-width="120px" />
<el-table-column prop="quantity" label="数量" min-width="120px" />
<el-table-column prop="barCode" label="条码" min-width="120px" />
<el-table-column prop="standId" label="入库站台" min-width="120px" />
<el-table-column prop="position" label="入库位置" min-width="120px" />
<el-table-column fixed="right" label="操作" width="120px">
<template #default>
<el-button plain type="primary" @click="deleteRowTask(row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<br />
</fieldset>
</div>
<div class="right">
<fieldset class="input-area">
<legend>输入入库信息</legend>
<el-form ref="taskInRequestRef" :model="taskInRequestEntity" :label-position="labelPosition"
label-width="100px" style="max-width: 100%" :rules="rules" status-icon>
<el-row :gutter="16">
<el-col :span="24" :offset="0">
<el-form-item label="条码号" prop="barCode">
<el-input v-model="taskInRequestEntity.barCode" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="16">
<el-col :span="12" :offset="0">
<el-form-item label="型号" prop="goodsType">
<el-input v-model="taskInRequestEntity.goodsType" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="规格" prop="specification">
<el-input v-model="taskInRequestEntity.specification" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="16">
<el-col :span="12" :offset="0">
<el-form-item label="批次号" prop="batchNo">
<el-input v-model="taskInRequestEntity.batchNo" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="数量" prop="quantity">
<el-input-number v-model.number="taskInRequestEntity.quantity" clearable
controls-position="right" :min="1"></el-input-number>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="16">
<el-col :span="12" :offset="0">
<el-form-item label="入库位置" prop="position">
<el-select-v2 v-model="taskInRequestEntity.position" placeholder="请选择入库位置"
:options="positionOptions" :disabled="disabledEmpty"></el-select-v2>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="入库站台" prop="standId">
<el-select-v2 v-model="taskInRequestEntity.standId" placeholder="请选择入库站台"
:options="standOptions"></el-select-v2>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="16" class="btn-area">
<el-col :span="8" :offset="0">
<el-form-item>
<el-button type="primary" round
@click="addTempTasks(taskInRequestRef)">绑定信息</el-button>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item>
<el-button type="success" round
@click="submitGoodsInTask(taskInRequestRef)">下发任务</el-button>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item>
<el-button type="danger" round @click="resetForm(taskInRequestRef)">清空信息</el-button>
</el-form-item>
</el-col>
</el-row>
</el-form>
</fieldset>
</div>
</el-container>
</el-config-provider>
</template>
<script setup>
import { sendGoodsInTask } from '@/api/task'
import { genTaskId } from '@/utils/stringUtils'
import { reactive, ref } from 'vue'
import { ElMessage } from 'element-plus'
import zhCn from 'element-plus/dist/locale/zh-cn.mjs'
const taskInRequestRef = ref()
</script>
<script>
export default {
name: 'goodsIn',
data() {
return {
labelPosition: "top",
tempTasks: [],
tempStandId: '',
taskInRequestEntity: reactive({
barCode: '',//
goodsType: '',//
specification: '',//
batchNo: '',//
quantity: 1,//
weight: 1.0,//
position: null,
standId: null
}),
rules: reactive({
barCode: [
{ required: true, message: '请扫描条码号' }
],
goodsType: [
{ required: true, message: '请输入型号' }
],
specification: [
{ required: true, message: '请输入规格' }
],
batchNo: [
{ required: true, message: '请输入批次号' }
],
quantity: [
{ required: true, message: '请输入数量' },
{ type: 'number', message: '请输入数字' }
],
position: [
{ required: true, message: '请选择入库位置' }
],
standId: [
{ required: true, message: '请选择入库站台' }
],
}),
standOptions: [
{
value: "R1",
label: '入库站台1'
},
{
value: "R2",
label: '入库站台2'
}
],
positionOptions: [
{
value: 1,
label: '1号位置'
},
{
value: 2,
label: '2号位置'
},
{
value: 3,
label: '3号位置'
}
],
}
},
mounted() {
},
methods: {
addTempTasks(taskInRequestRef) {//
if (!taskInRequestRef) return
taskInRequestRef.validate((valid) => {
if (!valid) {
ElMessage({
message: '请输入必须的入库信息!',
type: 'warning',
})
return
}
if (this.tempStandId != '' && this.tempStandId != this.taskInRequestEntity.standId) {
ElMessage({
message: '入库站台不一致!',
type: 'warning',
})
return
}
if (this.tempTasks.find((task) => task.position == this.taskInRequestEntity.position) != null) {
ElMessage({
message: '入库位置重复!',
type: 'warning',
})
return
}
this.tempStandId = this.taskInRequestEntity.standId
const tempTask = {
barCode: this.taskInRequestEntity.barCode,//
goodsType: this.taskInRequestEntity.goodsType,//
specification: this.taskInRequestEntity.specification,//
batchNo: this.taskInRequestEntity.batchNo,//
quantity: this.taskInRequestEntity.quantity,//
position: this.taskInRequestEntity.position,
standId: this.taskInRequestEntity.standId,
weight: this.taskInRequestEntity.weight
}
this.tempTasks.push(tempTask)
taskInRequestRef.resetFields()
ElMessage({
message: '绑定成功!',
type: 'success',
})
})
},
submitGoodsInTask(formEl) {//
const inParams = {
standId: this.tempStandId,
taskId: genTaskId(''),
taskType: 1,
detailTaskInfoList: this.tempTasks
}
sendGoodsInTask(inParams).then(res => {
if (res.data.code == 0) {
ElMessage({
message: '创建入库任务成功!',
type: 'success',
})
this.tempTasks = []
this.tempStandId = ''
formEl.resetFields()
} else {
ElMessage.error(res.data.message)
}
}).catch(err => {
console.log(err)
ElMessage.error('创建入库任务错误!' + err.message)
})
},
resetForm(formEl) {//
if (!formEl) return
formEl.resetFields()
},
deleteRowTask(row) {//
this.tempTasks.splice(this.tempTasks.indexOf(row), 1)
if (this.tempTasks.length == 0) {
this.tempVehicleNo = ''
}
}
}
}
</script>
<style scoped>
.content {
display: flex;
width: 100%;
}
.el-row .el-form-item {
width: 30% inherit;
justify-content: center;
}
/* .btn-area .el-form-item {
width: 30% inherit;
padding-left: 15px;
} */
.el-row .el-form-item .el-select {
width: 100% !important;
}
.el-row .el-form-item .el-select-v2 {
width: 100% !important;
}
.el-row .el-form-item .el-input-number {
width: 100% !important;
}
.el-row .el-form-item .el-button {
margin: auto;
}
.right {
width: 40%;
padding: 5px;
}
.left {
width: 60%;
padding: 5px;
}
.input-area {
margin: auto;
max-width: inherit;
height: 632px;
border: solid 1px;
border-radius: 10px;
box-shadow: 0px 15px 10px -15px #000;
}
.display-area {
margin: auto;
min-width: inherit;
height: 632px;
border: solid 1px;
border-radius: 10px;
box-shadow: 0px 15px 10px -15px #000;
}
.table-class {
width: 100%;
}
</style>

View File

@ -0,0 +1,469 @@
<template>
<el-config-provider :locale="zhCn">
<el-container class="content">
<fieldset class="input-area">
<legend style="font-size: 25px;">入库界面</legend>
<el-form ref="taskInRequestRef" :model="taskInRequestEntity" :label-position="labelPosition"
label-width="100px" style="max-width: 100%" :rules="rules" status-icon>
<el-row>
<el-col :span="22" :offset="1">
<el-form-item label="是否入空托" prop="isEmpty">
<el-select-v2 v-model="taskInRequestEntity.isEmpty" placeholder="请选择入库类型"
:options="stockTypeOptions"
@change="autoCompleteEmptyInfo(taskInRequestEntity)"></el-select-v2>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="22" :offset="1">
<el-form-item label="箱号" prop="vehicleNo">
<el-input v-model="taskInRequestEntity.vehicleNo" ref="vehicleNo" clearable
v-on:keyup.tab="detectEndInputVehicleNo(taskInRequestEntity)" />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="22" :offset="1">
<el-form-item label="零件号" prop="goodsId">
<el-input v-model="taskInRequestEntity.goodsId" ref="goodsId" clearable
:disabled="disabledEmpty" @blur="queryAndAutoComplete(taskInRequestEntity)" />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="22" :offset="1">
<el-form-item label="零件名称" prop="goodsName">
<el-input v-model="taskInRequestEntity.goodsName" clearable :disabled="disabledEmpty" />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="22" :offset="1">
<el-form-item label="零件数量" prop="goodsNum">
<el-input-number v-model.number="taskInRequestEntity.goodsNum" ref="goodsNum" clearable
:disabled="disabledEmpty" controls-position="right" :min="1"
@blur="detectEndInputNum(taskInRequestEntity)"></el-input-number>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="22" :offset="1">
<el-form-item label="零件重量(千克)" prop="weight">
<el-input v-model="taskInRequestEntity.weight" clearable :disabled="disabledEmpty" />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="22" :offset="1" v-show="disPlayDateFlag">
<el-form-item label="有效期(年)" prop="shelfLife">
<!-- // TODO -->
<el-input v-model="taskInRequestEntity.shelfLife" clearable :disabled="disabledEmpty" />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="22" :offset="1" v-show="disPlayDateFlag">
<el-form-item label="生产日期" prop="productionDate">
<el-date-picker v-model="taskInRequestEntity.productionDate" ref="productionDate"
:disabled="disabledEmpty" style="width: 100%;height: 75px;" type="date"
placeholder="请选择生产日期" :editable="false" size="large" clearable />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="11" :offset="1">
<el-form-item>
<el-button type="primary" round
@click="addTempTasks(taskInRequestRef, taskInRequestEntity)">绑定信息</el-button>
</el-form-item>
</el-col>
<el-col :span="11">
<el-form-item>
<el-button type="warning" round
@click="callEmptyVehicles(taskInRequestEntity)">请求空箱</el-button>
</el-form-item>
</el-col>
</el-row>
</el-form>
</fieldset>
<div ref="btnArea"></div>
<fieldset class="display-area">
<legend style="font-size: 25px;">
入库暂存信息
</legend>
<el-table :data="tempTasks" stripe border class="table-class" max-height="684px"
:header-cell-style="{ 'text-align': 'center', 'height': '100px' }"
:cell-style="{ 'text-align': 'center' }">
<el-table-column prop="vehicleNo" label="箱号" fixed="left" min-width="1rem" />
<el-table-column prop="goodsId" label="零件号" fixed="left" min-width="1rem" />
<el-table-column prop="goodsNum" label="数量" min-width="1rem" />
<el-table-column prop="goodsName" label="零件名称" min-width="1rem" />
<el-table-column prop="weight" label="重量" min-width="1rem" />
<el-table-column fixed="right" label="操作" min-width="1rem">
<template #default>
<el-button style="width: 1rem;" plain type="primary"
@click="deleteRowTask(row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<br />
<el-row :gutter="4">
<el-col :span="24">
<el-form-item>
<el-button type="success" round
@click="submitGoodsInTask(taskInRequestEntity)">下发任务</el-button>
</el-form-item>
</el-col>
</el-row>
</fieldset>
</el-container>
</el-config-provider>
</template>
<script setup>
import store from '@/store'
import { sendGoodsInTask, callEmptyVehicle } from '@/api/task'
import { queryPartInfoByPartNo } from '@/api/goods'
import { reactive, ref } from 'vue'
import { ElMessage } from 'element-plus'
import zhCn from 'element-plus/dist/locale/zh-cn.mjs'
import UploadExcelPart from '@/excel/UploadExcelPart.vue'
const taskInRequestRef = ref()
const taskInRequestEntity = reactive({
vehicleNo: '',//
goodsId: '',//
goodsName: '',//
goodsNum: 1,//
weight: 0,//
shelfLife: 0,//
productionDate: '',//
isEmpty: '1',
userName: store.getters.getUserName//
})
const rules = reactive({
vehicleNo: [
{ required: true, message: '请输入载具编号' }
],
goodsId: [
{ required: true, message: '请输入物料编号' }
],
goodsNum: [
{ required: true, message: '请输入数量' },
{ type: 'number', message: '请输入数字' }
]
})
const labelPosition = "top"
const displayWidth = window.screen.width
</script>
<script>
export default {
name: 'goodsInPda',
components: {
UploadExcelPart
},
data() {
return {
tempTasks: [],
tempVehicleNo: '',
disPlayDateFlag: false,
currentPartInfo: {},
stockTypeOptions: [
{
value: '0',
label: '空箱入库'
},
{
value: '1',
label: '带料入库'
}
],
disabledEmpty: false,
totalWeight: 0,
isOverWeight: false
}
},
mounted() {
this.$refs.vehicleNo.focus()
},
methods: {
//
queryAndAutoComplete(taskInRequestEntity) {
this.loading = true
const param = { material: taskInRequestEntity.goodsId }
queryPartInfoByPartNo(param).then(res => {
console.log(res)
if (res.data.code == 0) {
this.currentPartInfo = res.data.returnData
taskInRequestEntity.goodsName = this.currentPartInfo.itemDesc
this.calWeight(taskInRequestEntity)
if (this.currentPartInfo.SLED > 0) {//
this.disPlayDateFlag = true
taskInRequestEntity.shelfLife = this.currentPartInfo.SLED
} else {
this.disPlayDateFlag = false
taskInRequestEntity.shelfLife = 0
taskInRequestEntity.productionDate = ''
}
taskInRequestEntity.goodsNum = ''
this.$refs.goodsNum.focus()
} else {
taskInRequestEntity.goodsName = ''
taskInRequestEntity.goodsNum = ''
taskInRequestEntity.weight = 0
this.disPlayDateFlag = false
taskInRequestEntity.shelfLife = 0
taskInRequestEntity.productionDate = ''
ElMessage.error(res.data.message)
}
}).catch(err => {
taskInRequestEntity.goodsName = ''
taskInRequestEntity.goodsNum = ''
taskInRequestEntity.weight = 0
this.disPlayDateFlag = false
taskInRequestEntity.shelfLife = 0
taskInRequestEntity.productionDate = ''
ElMessage.error('查询物料信息错误' + err.message)
})
this.loading = false
},
calWeight(taskInRequestEntity) {
taskInRequestEntity.weight = this.currentPartInfo.partWeight * taskInRequestEntity.goodsNum
this.totalWeight = this.totalWeight + taskInRequestEntity.weight
if (this.totalWeight > 30) {
this.isOverWeight = true
ElMessage({
message: '箱子重量超过30kg',
type: 'warning',
})
}
},
autoCompleteEmptyInfo(taskInRequestEntity) {//
if (taskInRequestEntity.isEmpty == '0') {//
taskInRequestEntity.goodsId = '000000000'
taskInRequestEntity.batchNo = '000000000'
taskInRequestEntity.goodsNum = 1
taskInRequestEntity.weight = 0
taskInRequestEntity.shelfLife = 0
taskInRequestEntity.productionDate = ''
taskInRequestEntity.goodsName = '空箱'
this.currentPartInfo = {}
this.disabledEmpty = true
this.disPlayDateFlag = false
} else {
taskInRequestEntity.goodsId = ''
taskInRequestEntity.batchNo = ''
taskInRequestEntity.goodsNum = 1
this.disabledEmpty = false
}
this.$refs.vehicleNo.focus()
},
addTempTasks(formEl, formData) {//
if (!formEl) return
formEl.validate((valid) => {
if (!valid) {
ElMessage({
message: '请输入必须的入库信息!',
type: 'warning',
})
return
}
if (this.tempVehicleNo !== '' && this.tempVehicleNo !== formData.vehicleNo) {
ElMessage({
message: '载具编号不一致,请确认后重新输入!',
type: 'warning',
})
return
}
if (this.tempVehicleNo != '' && formData.isEmpty == '0') {
ElMessage({
message: '此箱子已经绑定过,请下发。',
type: 'warning',
})
return
}
if (this.isOverWeight) {
ElMessage({
message: '已超重,不可继续绑定,请移除此物料,并下发任务',
type: 'error',
showClose: true
})
return
}
this.tempVehicleNo = formData.vehicleNo
formData.userName = store.getters.getUserName
const sameIndex = this.tempTasks.findIndex(task => task.goodsId == formData.goodsId)
if (sameIndex != -1) {//
var sameGoods = this.tempTasks[sameIndex]
sameGoods.goodsNum = sameGoods.goodsNum + formData.goodsNum
sameGoods.weight = sameGoods.weight + formData.weight
} else {
this.tempTasks.push(Object.assign({}, formData))
}
this.resetFields(formData)
formData.vehicleNo = this.tempVehicleNo
ElMessage({
message: '绑定成功!',
type: 'success',
})
})
},
submitGoodsInTask(taskInRequestEntity) {//
if (this.tempTasks.length == 0) {
ElMessage.error("未绑定任何信息")
}
sendGoodsInTask(this.tempTasks).then(res => {
if (res.data.code == 0) {
this.tempTasks = []
this.tempVehicleNo = ''
this.resetFields(taskInRequestEntity)
this.totalWeight = 0
this.isOverWeight = false
ElMessage({
message: '下发任务成功,剩余库位可用数量: ' + res.data.returnData,
type: 'success'
})
this.$refs.vehicleNo.focus()
} else {
ElMessage.error(res.data.message)
}
}).catch(err => {
console.log(err)
ElMessage.error('创建入库任务错误!' + err.message)
})
},
resetFields(taskInRequestEntity) {
taskInRequestEntity.vehicleNo = ''
if (taskInRequestEntity.isEmpty != '0') {//
taskInRequestEntity.goodsId = ''
taskInRequestEntity.goodsName = ''
taskInRequestEntity.goodsNum = 1
taskInRequestEntity.weight = 0
taskInRequestEntity.shelfLife = 0
taskInRequestEntity.productionDate = ''
}
this.disPlayDateFlag = false
this.currentPartInfo = {}
},
callEmptyVehicles(taskInRequestEntity) {
callEmptyVehicle(taskInRequestEntity).then(res => {
if (res.data.code == 0) {
ElMessage({
message: res.data.message,
type: 'success'
})
} else {
ElMessage.error(res.data.message)
}
}).catch(err => {
console.log(err)
ElMessage.error('呼叫空箱错误' + err.message)
})
},
deleteRowTask(row) {//
this.tempTasks.splice(this.tempTasks.indexOf(row), 1)
if (this.tempTasks.length == 0) {
this.tempVehicleNo = ''
}
},
detectEndInputVehicleNo(taskInRequestEntity) {
if (taskInRequestEntity.isEmpty == '0') {//
this.$refs.btnArea.scrollIntoView();
// this.$refs.vehicleNo.blur()
} else {
this.$refs.goodsId.focus()
}
},
detectEndInputNum(taskInRequestEntity) {
this.calWeight(taskInRequestEntity)
}
}
}
</script>
<style scoped>
.content {
display: flex;
flex-direction: column;
width: v-bind(displayWidth);
}
.el-row {
margin: 20px 0;
}
:deep(.el-form-item__label) {
font-size: 25px;
/* margin: 10px; */
padding: 10px 0;
}
:deep(.el-select-v2__placeholder) {
font-size: 25px;
}
:deep(.el-select-v2__wrapper) {
height: 80px;
}
:deep(.el-form-item .el-input) {
height: 80px;
font-size: 25px;
}
:deep(.el-form-item .el-input-number) {
height: 80px;
font-size: 25px;
}
:deep(.el-table .cell) {
line-height: 100%;
}
.el-row .el-form-item {
justify-content: center;
}
.el-row .el-form-item .el-select {
width: 100% !important;
}
.el-row .el-form-item .el-select-v2 {
width: 100% !important;
}
.el-row .el-form-item .el-input-number {
width: 100% !important;
}
.el-row .el-form-item .el-button {
width: 180px;
height: 60px;
margin: auto;
font-size: 25px;
}
.input-area {
margin: 10px;
width: v-bind(displayWidth) - 20px;
/* height: 730px; */
border: solid 1px;
border-radius: 10px;
box-shadow: 0px 15px 10px -15px #000;
}
.display-area {
margin: 10px;
width: v-bind(displayWidth) - 20px;
/* height: auto; */
border: solid 1px;
border-radius: 10px;
box-shadow: 0px 15px 10px -15px #000;
}
.table-class {
width: 100%;
font-size: 1rem;
}
</style>

310
src/layout/goodsOut.vue Normal file
View File

@ -0,0 +1,310 @@
<template>
<el-config-provider :locale="zhCn">
<el-container class="content">
<div class="left">
<fieldset class="display-area">
<legend>
当前WMS出库任务
</legend>
<!-- 此表格现在显示当前当前站台正在执行的出库任务 -->
<el-table :data="tasks" stripe border class="table-class" max-height="250px"
:header-cell-style="{ 'text-align': 'center' }" :cell-style="{ 'text-align': 'center' }">
<el-table-column prop="goodsType" label="型号" fixed="left" min-width="120px" />
<el-table-column prop="specification" label="规格" min-width="120px" />
<el-table-column prop="taskGroup" label="任务单号" min-width="120px" />
<el-table-column prop="batchNo" label="批次号" min-width="120px" />
<el-table-column prop="operateNum" label="操作数量" min-width="120px" />
<el-table-column prop="totalNum" label="库存数量" min-width="120px" />
<el-table-column prop="origin" label="起始位置" min-width="120px" />
<el-table-column prop="destination" label="目标位置" min-width="120px" />
<el-table-column prop="createTime" label="创建时间" :formatter="timeFormat" min-width="120px" />
<el-table-column prop="createTime" label="运行时长" :formatter="dueFormat" min-width="120px" />
<el-table-column prop="taskStatus" label="任务状态" fixed="right" :formatter="taskStatusFormat"
min-width="120px" />
</el-table>
<br />
<el-pagination v-model:current-page="currentPage" v-model:page-size="pageSize"
:page-sizes="[10, 25, 50]" :small="false" :disabled="false" :background="false"
:default-page-size="10" @size-change="getAllTasks" @current-change="getAllTasks"
layout="total, sizes, prev, pager, next, jumper" :total="total" />
</fieldset>
</div>
<div class="right">
<fieldset class="input-area-up">
<legend>出库输入信息</legend>
<el-form ref="taskOutRequestRef" :model="taskOutRequestEntity" :label-position="labelPosition"
label-width="100px" style="max-width: 100%" :rules="rules" status-icon>
<el-row :gutter="16">
<el-col :span="12" :offset="0">
<el-form-item label="型号" prop="goodsType">
<el-input v-model="taskOutRequestEntity.goodsType" clearable placeholder="此处输入型号" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="规格" prop="specification">
<el-input v-model="taskOutRequestEntity.specification" clearable
placeholder="此处输入规格" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="16">
<el-col :span="12" :offset="0">
<el-form-item label="数量" prop="quantity">
<el-input-number v-model.number="taskOutRequestEntity.quantity"
controls-position="right" :min="0" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="任务单号" prop="taskId">
<el-input v-model="taskOutRequestEntity.taskId" clearable placeholder="此处输入任务单号" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="16" class="btn-area">
<el-col :span="8" :offset="8">
<el-form-item>
<el-button type="success" round
@click="submitGoodsOutTask(taskOutRequestRef, taskOutRequestEntity)">下发任务</el-button>
</el-form-item>
</el-col>
</el-row>
</el-form>
</fieldset>
</div>
</el-container>
</el-config-provider>
</template>
<script setup>
import store from '@/store'
import { sendGoodsOutTask, getTasks } from '@/api/task'
import { taskStatusFormatter, dueFormatter, timeFormatter } from '@/utils/formatter.js'
import { genTaskId } from '@/utils/stringUtils'
import { reactive, ref } from 'vue'
import { ElMessage } from 'element-plus'
import zhCn from 'element-plus/dist/locale/zh-cn.mjs'
const taskOutRequestRef = ref()
const rules = reactive({
goodsType: [
{ required: true, message: '请输入型号' }
],
specification: [
{ required: true, message: '请输入规格' }
],
quantity: [
{ required: true, message: '请输入数量' },
{ type: 'number', message: '请输入数字' }
]
})
</script>
<script>
export default {
name: 'goodsOut',
data() {
return {
timer: '',
tasks: [],
pageInfo: {},
currentPage: 1,
pageSize: 10,
total: 0,
loading: false,
labelPosition: 'top',
taskOutRequestEntity: {
goodsType: '',
specification: '',
quantity: 0,
taskId: '',
userName: store.getters.getUserName,
}
}
},
mounted() {
this.timer = setInterval(() => {
this.getAllTasks()
}, 2000)
},
beforeUnmount() {
clearInterval(this.timer)
},
methods: {
getAllTasks() {
this.pageInfo.pageNum = this.currentPage
this.pageInfo.pageSize = this.pageSize
const tableRequest = {
page: this.pageInfo,
param: {
taskType: 2,
userName: store.getters.getUserName
}
}
getTasks(tableRequest).then(res => {
const tableResponse = res.data
if (tableResponse.code != 0) {
ElMessage.error(tableResponse.message)
}
this.tasks = tableResponse.rows
this.total = tableResponse.total
}).catch(err => {
console.log(err)
ElMessage.error('查询任务错误')
})
},
//
taskStatusFormat: (row, column, cellValue, index) => {
return taskStatusFormatter(cellValue)
},
//
dueFormat: (row, column, cellValue, index) => {
return dueFormatter(cellValue)
},
//
timeFormat: (row, column, cellValue, index) => {
return timeFormatter(cellValue)
},
submitGoodsOutTask(formEl, formData) {
if (!formEl) return
formEl.validate((valid) => {
if (!valid) {
ElMessage({
message: '请输入必须的出库信息!',
type: 'warning',
})
return
}
if (formData.quantity <= 0) {
ElMessage({
message: '数量必须要大于0的数字',
type: 'error',
})
return
}
const outParams = {
taskId: formData.taskId == '' ? genTaskId('') : formData.taskId,
taskType: 2,
detailTaskInfoList: [
{
goodsType: formData.goodsType,
specification: formData.specification,
quantity: formData.quantity
}
]
}
sendGoodsOutTask(outParams).then(res => {
if (res.data.code == 0) {
ElMessage({
message: '创建出库任务成功!',
type: 'success',
})
formEl.resetFields()
} else {
ElMessage.error(res.data.message)
}
}).catch(err => {
console.log(err)
ElMessage.error('创建出库任务错误!')
})
})
}
}
}
</script>
<style scoped>
.content {
display: flex;
width: 100%;
}
.el-row .el-form-item {
width: 30% inherit;
justify-content: center;
}
.btn-area .el-form-item {
width: 30% inherit;
padding-left: 15px;
}
.el-row .el-form-item .el-select-v2 {
width: 100% !important;
}
.el-row .el-form-item .el-select {
width: 100% !important;
}
.el-row .el-form-item .el-input-number {
width: 100% !important;
}
.el-row .el-form-item .el-button {
margin: auto;
}
.right {
width: 40%;
padding: 5px;
}
.left {
width: 60%;
padding: 5px;
}
.input-area-up {
margin: auto;
max-width: inherit;
height: 632px;
border: solid 1px;
border-radius: 10px;
box-shadow: 0px 15px 10px -15px #000;
}
.display-area {
margin: auto;
min-width: inherit;
height: 632px;
border: solid 1px;
border-radius: 10px;
box-shadow: 0px 15px 10px -15px #000;
}
.table-class {
width: 100%;
}
.qr-code-container {
width: 80px;
height: 80px;
margin: 20px;
}
.card-box {
display: flex;
align-items: center;
}
.text-over {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
max-width: 130px;
}
.objectDialogFlowPrint .myPrint {
/* 打印的时候是否显示底色 */
print-color-adjust: exact;
}
.objectDialogFlowPrint .pageWarp {
/*这句话很重要,控制时候强制分页 https://www.w3school.com.cn/cssref/pr_page-break-after.asp*/
page-break-after: always;
height: 100%;
width: 100%;
text-align: center;
margin: 5px auto;
padding: auto;
}
</style>

168
src/layout/inTaskRecord.vue Normal file
View File

@ -0,0 +1,168 @@
<template>
<div style="margin-bottom: 15px">
<el-config-provider :locale="zhCn">
<el-row>
<el-input v-model="goodsTypeQuery" style="width: 256px; margin-right: 10px;" placeholder="型号" />
<el-input v-model="specificationQuery" style="width: 256px; margin-right: 10px;" placeholder="规格" />
<el-button type="primary" @click="search()">搜索</el-button>
<el-button type="warning" @click="reset()">重置</el-button>
<el-button type="success" @click="exportExcel()">导出记录</el-button>
</el-row>
<br />
<el-table :data="tasks" stripe border v-loading="loading" style="width: 100%" max-height="684px"
class="table-class" :header-cell-style="{ 'text-align': 'center' }"
:cell-style="{ 'text-align': 'center' }">
<el-table-column prop="taskType" label="任务类型" fixed="left" :formatter="taskTypeFormat" min-width="120px" />
<el-table-column prop="goodsType" label="型号" min-width="120px" />
<el-table-column prop="specification" label="规格" min-width="120px" />
<el-table-column prop="batchNo" label="批次号" min-width="120px" />
<el-table-column prop="taskGroup" label="任务单号" min-width="120px" />
<el-table-column prop="origin" label="起点" min-width="120px" />
<el-table-column prop="destination" label="终点" min-width="120px" />
<el-table-column prop="operateNum" label="操作数量" min-width="120px" />
<el-table-column prop="totalNum" label="库存数量" min-width="120px" />
<el-table-column prop="taskPriority" label="任务优先级" min-width="120px" />
<el-table-column prop="preTask" label="前置任务" min-width="120px" show-overflow-tooltip />
<el-table-column prop="createTime" label="创建时间" :formatter="timeFormat" min-width="120px" />
<!-- <el-table-column prop="userName" label="操作人员姓名" min-width="120px" /> -->
<el-table-column prop="taskStatus" label="任务状态" fixed="right" :formatter="taskStatusFormat"
min-width="120px" />
</el-table>
<br />
<el-pagination v-model:current-page="currentPage" v-model:page-size="pageSize" :page-sizes="[10, 25, 50]"
:small="false" :disabled="false" :background="false" :default-page-size="10"
layout="total, sizes, prev, pager, next, jumper" :total="total" @size-change="search"
@current-change="search" />
</el-config-provider>
</div>
</template>
<script setup>
import { getTaskRecords } from '@/api/record.js'
import { dateFormatter, taskStatusFormatter, timeFormatter } from '@/utils/formatter.js'
import { downloadRukuExcel } from '@/api/excel.js'
import zhCn from 'element-plus/dist/locale/zh-cn.mjs'
import { ElMessage } from 'element-plus'
import store from '@/store'
</script>
<script>
export default {
name: 'inTaskRecord',
data() {
return {
pageInfo: {},
tasks: [],
currentPage: 1,
pageSize: 10,
total: 0,
goodsTypeQuery: '',
specificationQuery: '',
loading: true
}
},
mounted() {
this.search()
},
methods: {
dateFormat: (row, column, cellValue, index) => {
return dateFormatter(cellValue)
},
timeFormat: (row, column, cellValue, index) => {
return timeFormatter(cellValue)
},
taskStatusFormat: (row, column, cellValue, index) => {
return taskStatusFormatter(cellValue)
},
taskTypeFormat: (row, column, cellValue, index) => {
switch (cellValue) {
case 1: return '入库'
case 2: return '出库'
case 3: return '盘点'
case 9: return '移库'
default: return '未知'
}
},
search() {
this.loading = true
this.pageInfo.pageNum = this.currentPage
this.pageInfo.pageSize = this.pageSize
const tableRequest = {
page: this.pageInfo,
param: {
taskType: 1,
goodsType: this.goodsTypeQuery.trim(),
specification: this.specificationQuery.trim(),
userName: store.getters.getUserName
}
}
getTaskRecords(tableRequest).then(res => {
const tableResponse = res.data
if (tableResponse.code != 0) {
ElMessage.error(tableResponse.message)
}
this.tasks = tableResponse.rows
this.total = tableResponse.total
}).catch(err => {
console.log(err)
ElMessage.error('查询任务记录错误')
})
this.loading = false
},
reset() {
this.goodsTypeQuery = ''
this.specificationQuery = ''
this.search()
},
exportExcel() {
downloadRukuExcel().then(res => {
const link = document.createElement('a');//a
try {
// let blob = new Blob([res.data],{type: 'application/vnd.ms-excel'}); //blobblobtypexls
let blob = res.data //blob
// let _fileName = res.headers['content-disposition'].split(';')[1].split('=')[1]; //
let _fileName = "导出入库记录" + dateFormatter(new Date) + ".xlsx"
link.style.display = 'none'//
// URL
const url = window.URL || window.webkitURL || window.moxURL
link.href = url.createObjectURL(blob)
link.setAttribute('download', _fileName.substring(_fileName.lastIndexOf('_') + 1))
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
url.revokeObjectURL(link.href)//url
} catch (e) {
ElMessage({
message: '下载文件失败:: ' + e,
type: 'error',
showClose: true
})
}
}).catch(err => {
ElMessage({
message: '导出失败:: ' + err,
type: 'error',
showClose: true
})
})
},
},
}
</script>
<style scoped>
.el-pagination {
padding-left: 5px;
}
.el-row .el-button {
width: 72px;
margin-left: 0px;
margin-right: 5px;
}
.table-class {
width: 100%;
}
</style>

399
src/layout/inventory.vue Normal file
View File

@ -0,0 +1,399 @@
<template>
<el-config-provider :locale="zhCn">
<el-container class="content">
<div class="left">
<fieldset class="display-area">
<legend>
当前WMS盘点任务
</legend>
<!-- 此表格现在显示当前当前站台正在执行的出库任务 -->
<el-table :data="tasks" stripe border class="table-class" max-height="250px"
:header-cell-style="{ 'text-align': 'center' }" :cell-style="{ 'text-align': 'center' }">
<el-table-column prop="vehicleNo" label="托盘号" fixed="left" min-width="120px" />
<el-table-column prop="goodsId" label="物料编码" fixed="left" min-width="120px" />
<el-table-column prop="goodsName" label="物料描述" min-width="120px" />
<el-table-column prop="operateNum" label="操作数量" min-width="120px" />
<el-table-column prop="totalNum" label="库存数量" min-width="120px" />
<el-table-column prop="expirationDate" label="有效日期" min-width="120px" />
<el-table-column prop="batchNo" label="批次号" min-width="120px" />
<el-table-column prop="wipEntityId" label="工单号" min-width="120px" />
<el-table-column prop="taskStatus" label="任务状态" fixed="right" :formatter="taskStatusFormat" min-width="120px" />
<el-table-column prop="createTime" label="运行时长" :formatter="dueFormat" min-width="120px" />
</el-table>
<br />
<el-pagination v-model:current-page="currentPage" v-model:page-size="pageSize"
:page-sizes="[10, 25, 50]" :small="false" :disabled="false" :background="false"
:default-page-size="10" @size-change="getAllTasks" @current-change="getAllTasks"
layout="total, sizes, prev, pager, next, jumper" :total="total" />
</fieldset>
</div>
<div class="right">
<fieldset class="input-area">
<legend>盘点下发信息</legend>
<el-form ref="inventoryTaskOutRequestRef" :model="inventoryOutEntity" :label-position="labelPosition"
label-width="100px" style="max-width: 100%" :rules="rules" status-icon>
<el-row :gutter="16">
<el-col :span="12" :offset="0">
<el-form-item label="物料编码" prop="goodsId">
<el-select v-model="inventoryOutEntity.goodsId" :multiple="false" filterable remote
reserve-keyword placeholder="请输入物料编码" :remote-method="queryGoodsId"
:loading="loading">
<el-option v-for="item in goodsOptions" :key="item.goodsId" :label="item.goodsId"
:value="item.goodsId" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="库存数量" prop="totalNum">
<el-input-number v-model.number="inventoryOutEntity.totalNum" readonly
controls-position="right" :min="1" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="16">
<el-col :span="12" :offset="0">
<el-form-item label="托盘号" prop="vehicleId">
<el-input v-model="inventoryOutEntity.vehicleId" clearable placeholder="请输入托盘号" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="出库站台" prop="destination">
<el-select-v2 v-model="inventoryOutEntity.destination" placeholder="请选择盘点站台"
:options="standOptions"></el-select-v2>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="16">
<el-col :span="12" :offset="0">
<el-form-item label="*盘点前请确认无其他任务正在执行" class="red-character">
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="16" class="btn-area">
<el-col :span="8" :offset="8">
<el-form-item>
<el-button type="success" round @click="submitInventoryOutTask()">下发盘点</el-button>
</el-form-item>
</el-col>
</el-row>
</el-form>
</fieldset>
<fieldset class="input-area" style="margin-top: 15px;">
<legend>盘点确认信息</legend>
<el-form ref="inventoryTaskConfirmRequestRef" :model="inventoryConfirmEntity"
:label-position="labelPosition" label-width="100px" style="max-width: 100%" :rules="rules"
status-icon>
<el-row :gutter="16">
<el-col :span="12" :offset="0">
<el-form-item label="托盘号">
<el-input v-model="inventoryConfirmEntity.vehicleNo" readonly />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="物料编码">
<el-input v-model="inventoryConfirmEntity.goodsId" readonly />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="16">
<el-col :span="12" :offset="0">
<el-form-item label="盘点前数量">
<el-input v-model="inventoryConfirmEntity.originNum" readonly />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="确认数量" prop="realNum">
<el-input-number v-model.number="inventoryConfirmEntity.realNum" clearable
controls-position="right" :min="0" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="16" class="btn-area">
<el-col :span="8" :offset="8">
<el-form-item>
<el-button type="success" round @click="submitInventoryConfirmTask()">确认盘点</el-button>
</el-form-item>
</el-col>
</el-row>
</el-form>
</fieldset>
</div>
</el-container>
</el-config-provider>
</template>
<script setup>
import store from '@/store'
import { sendInventoryTask, finishPicking, getTasks, getTaskByTask } from '@/api/task'
import { queryPartNo } from '@/api/goods'
import { taskStatusFormatter, dueFormatter } from '@/utils/formatter.js'
import { reactive, ref } from 'vue'
import { ElMessage } from 'element-plus'
import zhCn from 'element-plus/dist/locale/zh-cn.mjs'
const inventoryTaskOutRequestRef = ref()
const inventoryTaskConfirmRequestRef = ref()
const rules = reactive({
goodsId: [
{ required: true, message: '请输入物料编号' }
],
realNum: [
{ required: true, message: '请输入数量' },
{ type: 'number', message: '请输入数字' }
],
destination: [
{ required: true, message: '请选择盘点站台' }
]
})
</script>
<script>
export default {
name: 'inventory',
data() {
return {
timer: '',
tasks: [],
pageInfo: {},
currentPage: 1,
pageSize: 10,
total: 0,
loading: false,
labelPosition: 'top',
inventoryOutEntity: {
goodsId: '',
totalNum: 0,
vehicleId: '',
destination: '',
userName: store.getters.getUserName
},
inventoryConfirmEntity: {
taskId: '',
goodsId: '',
vehicleNo: '',
originNum: 0,
realNum: 0,
userName: store.getters.getUserName
},
goodsOptions: [],
standOptions: [
{
value: 'P1',
label: '1号盘点站台'
},
{
value: 'P2',
label: '2号盘点站台'
},
]
}
},
mounted() {
this.timer = setInterval(() => {
this.getAllTasks()
this.getCurrentInventoryInfo()
}, 2000)
},
beforeUnmount() {
clearInterval(this.timer)
},
methods: {
queryGoodsId(query) {
const param = { material: query }
queryPartNo(param).then(res => {
if (res.data.code == 0) {
this.goodsOptions = res.data.returnData
} else {
this.goodsOptions = []
}
}).catch(err => {
this.goodsOptions = []
ElMessage.error('查询物料信息错误' + err.message)
})
},
getAllTasks() {
this.pageInfo.pageNum = this.currentPage
this.pageInfo.pageSize = this.pageSize
const tableRequest = {
page: this.pageInfo,
param: {
taskType: 3,
userName: store.getters.getUserName
}
}
getTasks(tableRequest).then(res => {
const tableResponse = res.data
if (tableResponse.code != 0) {
console.log(tableResponse.code + ':' + tableResponse.message)
ElMessage.error(tableResponse.message)
}
this.tasks = tableResponse.rows
this.total = tableResponse.total
}).catch(err => {
console.log(err)
ElMessage.error('查询任务错误')
})
},
//
getCurrentInventoryInfo() {
//
const taskQuery1 = {
taskType: 3,
taskStatus: 8
}
//
getTaskByTask(taskQuery1).then(res1 => {
if (res1.data.code == 0) {
const currentFinishTask = res1.data.returnData;
if (currentFinishTask != null) {
if (currentFinishTask.taskId == this.inventoryConfirmEntity.taskId) {//
return
}
this.inventoryConfirmEntity.taskId = currentFinishTask.taskId
this.inventoryConfirmEntity.goodsId = currentFinishTask.goodsId
this.inventoryConfirmEntity.vehicleNo = currentFinishTask.vehicleNo
this.inventoryConfirmEntity.originNum = currentFinishTask.totalNum
this.inventoryConfirmEntity.realNum = currentFinishTask.totalNum
} else {
this.inventoryConfirmEntity.taskId = ''
this.inventoryConfirmEntity.goodsId = ''
this.inventoryConfirmEntity.vehicleNo = ''
this.inventoryConfirmEntity.originNum = 0
this.inventoryConfirmEntity.realNum = 0
}
}
}).catch(err => {
console.log(err)
ElMessage.error('查询任务错误')
})
},
taskStatusFormat: (row, column, cellValue, index) => {
return taskStatusFormatter(cellValue)
},
dueFormat: (row, column, cellValue, index) => {
return dueFormatter(cellValue)
},
submitInventoryOutTask() {
if (this.tasks.length > 0) {
ElMessage({
message: '当前还有盘点任务未完成',
type: 'error',
})
return
}
sendInventoryTask(this.inventoryOutEntity).then(res => {
console.log(res)
if (res.data.code == 0) {
this.inventoryOutEntity.totalNum = res.data.returnData.realNum
ElMessage({
message: '创建盘点出库任务成功!',
type: 'success',
})
} else {
this.inventoryOutEntity.totalNum = 0
ElMessage.error(res.data.message)
}
}).catch(err => {
this.inventoryOutEntity.totalNum = 0
console.log(err)
ElMessage.error('创建盘点出库任务错误!')
})
},
submitInventoryConfirmTask() {
if (this.inventoryConfirmEntity.taskId == null) {
ElMessage({
message: '没有正在盘点的任务,请勿乱点按钮',
type: 'error',
})
return
}
finishPicking(this.inventoryConfirmEntity).then(res => {
if (res.data.code == 0) {
ElMessage({
message: '盘点成功,正在回库',
type: 'success',
})
this.inventoryConfirmEntity.taskId = ''
this.inventoryConfirmEntity.goodsId = ''
this.inventoryConfirmEntity.vehicleNo = ''
this.inventoryConfirmEntity.originNum = 0
this.inventoryConfirmEntity.realNum = 0
} else {
ElMessage.error(res.data.message)
}
}).catch(err => {
console.log(err)
ElMessage.error('盘点错误')
})
}
}
}
</script>
<style scoped>
.content {
display: flex;
width: 100%;
}
.el-row .el-form-item {
width: 30% inherit;
justify-content: center;
}
.btn-area .el-form-item {
width: 30% inherit;
padding-left: 15px;
}
.el-row .el-form-item .el-select-v2 {
width: 100% !important;
}
.el-row .el-form-item .el-select {
width: 100% !important;
}
.el-row .el-form-item .el-input-number {
width: 100% !important;
}
.el-row .el-form-item .el-button {
margin: auto;
}
.right {
width: 40%;
padding: 5px;
}
.left {
width: 60%;
padding: 5px;
}
.input-area {
margin: auto;
max-width: inherit;
height: 300px;
border: solid 1px;
border-radius: 10px;
box-shadow: 0px 15px 10px -15px #000;
}
.display-area {
margin: auto;
min-width: inherit;
height: 632px;
border: solid 1px;
border-radius: 10px;
box-shadow: 0px 15px 10px -15px #000;
}
.table-class {
width: 100%;
}
.red-character :deep(.el-form-item__label) {
color: red;
}
</style>

View File

@ -0,0 +1,150 @@
<template>
<div style="margin-bottom: 15px">
<el-config-provider :locale="zhCn">
<el-row>
<el-input v-model="goodsIdQuery" style="width: 256px; margin-right: 10px;" placeholder="零件号" />
<el-input v-model="vehicleNoQuery" style="width: 256px; margin-right: 10px;" placeholder="箱号" />
<el-button type="primary" @click="search()">搜索</el-button>
<el-button type="warning" @click="reset()">重置</el-button>
<el-button type="success" @click="refresh()">刷新</el-button>
</el-row>
<br />
<el-table :data="tasks" stripe border v-loading="loading" style="width: 100%" max-height="684px"
class="table-class" :header-cell-style="{ 'text-align': 'center' }" :cell-style="{ 'text-align': 'center' }">
<el-table-column prop="goodsId" label="零件号" fixed="left" min-width="120px" />
<el-table-column prop="vehicleNo" label="箱号" fixed="left" min-width="120px" />
<el-table-column prop="goodsName" label="零件名称" min-width="120px" />
<el-table-column prop="taskType" label="任务类型" :formatter="taskTypeFormat" sortable min-width="120px" />
<el-table-column prop="taskGroup" label="任务组" min-width="120px" />
<el-table-column prop="origin" label="起点" min-width="120px" />
<el-table-column prop="destination" label="终点" min-width="120px" />
<el-table-column prop="pickStand" label="拣选站台" min-width="120px" />
<el-table-column prop="weight" label="重量" min-width="120px" />
<el-table-column prop="productionDate" label="生产日期" :formatter="dateFormat" min-width="120px" />
<el-table-column prop="expirationDate" label="有效日期" :formatter="dateFormat" min-width="120px" />
<el-table-column prop="operateNum" label="操作数量" min-width="120px" />
<el-table-column prop="totalNum" label="库存数量" min-width="120px" />
<el-table-column prop="taskPriority" label="任务优先级" sortable min-width="120px" />
<el-table-column prop="kateTaskId" label="配件任务号" sortable min-width="140px" />
<el-table-column prop="userName" label="操作人员姓名" min-width="120px" />
<el-table-column prop="createTime" label="创建时间" :formatter="timeFormat" min-width="120px" />
<el-table-column prop="finishTime" label="任务完成时间" :formatter="timeFormat" min-width="120px" />
<el-table-column prop="taskStatus" label="任务状态" fixed="right" :formatter="taskStatusFormat" min-width="120px" />
<!-- <el-table-column fixed="right" label="操作" width="180px">
<template v-slot="scope">
<el-popconfirm confirm-button-text="" cancel-button-text="" title="请确认是否取消任务?" :hide-after="10" @confirm="">
<template #reference>
<el-button plain type="warning">取消</el-button>
</template>
</el-popconfirm>
<el-popconfirm confirm-button-text="" cancel-button-text="" title="请确认是否完成任务?" :hide-after="10" @confirm="">
<template #reference>
<el-button plain type="primary">完成</el-button>
</template>
</el-popconfirm>
</template>
</el-table-column> -->
</el-table>
<br />
<el-pagination v-model:current-page="currentPage" v-model:page-size="pageSize" :page-sizes="[10, 25, 50]"
:small="false" :disabled="false" :background="false" :default-page-size="10"
layout="total, sizes, prev, pager, next, jumper" :total="total" @size-change="search"
@current-change="search" />
</el-config-provider>
</div>
</template>
<script setup>
import { getTaskRecords } from '@/api/record.js'
import { dateFormatter, taskStatusFormatter, timeFormatter } from '@/utils/formatter.js'
import zhCn from 'element-plus/dist/locale/zh-cn.mjs'
import { ElMessage } from 'element-plus'
import store from '@/store'
</script>
<script>
export default {
name: 'inventoryRecord',
data() {
return {
pageInfo: {},
tasks: [],
currentPage: 1,
pageSize: 10,
total: 0,
goodsIdQuery: '',
vehicleNoQuery: '',
loading: true
}
},
mounted() {
this.search()
},
methods: {
dateFormat: (row, column, cellValue, index) => {
return dateFormatter(cellValue)
},
timeFormat: (row, column, cellValue, index) => {
return timeFormatter(cellValue)
},
taskStatusFormat: (row, column, cellValue, index) => {
return taskStatusFormatter(cellValue)
},
taskTypeFormat: (row, column, cellValue, index) => {
switch (cellValue) {
case 1: return '入库'
case 2: return '出库'
case 3: return '盘点'
default: return '未知'
}
},
search() {
this.loading = true
this.pageInfo.pageNum = this.currentPage
this.pageInfo.pageSize = this.pageSize
const tableRequest = {
page: this.pageInfo,
param: {
taskType: 3,
goodsId: this.goodsIdQuery.trim(),
vehicleNo: this.vehicleNoQuery.trim(),
userName: store.getters.getUserName
}
}
getTaskRecords(tableRequest).then(res => {
const tableResponse = res.data
if (tableResponse.code != 0) {
ElMessage.error(tableResponse.message)
}
this.tasks = tableResponse.rows
this.total = tableResponse.total
}).catch(err => {
console.log(err)
ElMessage.error('查询盘点记录错误')
})
this.loading = false
},
reset() {
this.goodsIdQuery = ''
this.vehicleNoQuery = ''
this.search()
},
},
}
</script>
<style scoped>
.el-pagination {
padding-left: 5px;
}
.el-row .el-button {
width: 72px;
margin-left: 0px;
margin-right: 5px;
}
.table-class {
width: 100%;
}
</style>

183
src/layout/location.vue Normal file
View File

@ -0,0 +1,183 @@
<template>
<div style="margin-bottom: 10px">
<el-config-provider :locale="zhCn">
<el-row style="align-items: center;">
<!-- <span>选择库区</span>
<el-select v-model="areaId" placeholder="请选择库区" style="width: 120px;" @change="getAllLocations">
<el-option label="3楼库区" :value='1' />
<el-option label="2楼库区" :value='2' />
</el-select> -->
<span style="margin-left: 5px;">状态说明</span>
<el-button color="green">空闲</el-button>
<el-button color="red">占用</el-button>
<el-button color="yellow">锁定</el-button>
<el-button color="#00BFFF" style="margin: 0 0 0 auto;" @click="getAllLocations">刷新</el-button>
</el-row>
<hr style="border: 1px solid #ff0000" />
<div>
<el-scrollbar max-height="760px">
<div v-for="currentRowLocation in allLocations">
<span>{{ currentRowLocation.row }}</span>
<div class="row" v-for="currentLayerLocation in currentRowLocation.currentLayerLocations">
<div class="col" v-for="currentColLocation in currentLayerLocation.currentColLocations">
<el-button :color="getLocationStatus(currentColLocation)"
@click="showDialog(currentColLocation)">
<span style="font-size: 15px;">{{ currentColLocation.queue }}{{ currentColLocation.line}}{{ currentColLocation.layer}}</span>
</el-button>
</div>
</div>
<hr style="border: 1px solid #000000" />
</div>
</el-scrollbar>
<el-dialog v-model="dialogVisible" :title="currentLocationTitle" width="30%" draggable :show-close="false">
<el-form :model="location">
<el-form-item label="占用/解除占用库位" :label-width="140">
<el-select v-model="location.locationStatus" placeholder="库位是否占用" style="width: 70%;">
<el-option label="空闲" :value='0' />
<el-option label="占用" :value='1' />
</el-select>
</el-form-item>
<el-form-item label="锁定/解锁库位" :label-width="140">
<el-select v-model="location.isLock" placeholder="库位是否锁定" style="width: 70%;">
<el-option label="解锁" :value='0' />
<el-option label="锁定" :value='1' />
</el-select>
</el-form-item>
<el-form-item label="载具" :label-width="140">
<el-input v-model="location.vehicleId" style="width: 70%;" />
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="submitLocationStatus()">
确定
</el-button>
</span>
</template>
</el-dialog>
</div>
</el-config-provider>
</div>
</template>
<script setup>
import zhCn from 'element-plus/dist/locale/zh-cn.mjs'
import { getLocations, updateLocation } from '@/api/location.js'
import { ElMessage, ElLoading } from 'element-plus'
import { reactive } from 'vue'
</script>
<script>
export default {
name: 'location',
data() {
return {
dialogVisible: false,
currentSelectedLocation: {},
allLocations: [],
location: reactive({
locationStatus: '',
isLock: '',
vehicleId: ''
}),
currentLocationTitle: '',
areaId: 1
}
},
mounted() {
this.getAllLocations()
},
methods: {
getLocationStatus: (currentColLocation) => {
if (currentColLocation.isLock === 1) {
return 'yellow'
}
if (currentColLocation.locationStatus === 1) {
return 'red'
}
return 'green'
},
getAllLocations() {
const loading = ElLoading.service({
lock: true,
text: 'Loading',
background: 'rgba(0, 0, 0, 0.7)',
})
const data = { areaId: this.areaId }
getLocations(data).then(res => {
loading.close()
if (res.data.code == 0) {
console.log(res.data.returnData)
this.allLocations = res.data.returnData
} else {
ElMessage.error(res.data.message)
}
}).catch(err => {
loading.close()
ElMessage.error('查询库位失败!')
})
},
showDialog(currentColLocation) {
this.dialogVisible = true
this.currentSelectedLocation = currentColLocation
this.setCurrentLocationTitle()
this.location.locationStatus = this.currentSelectedLocation.locationStatus
this.location.isLock = this.currentSelectedLocation.isLock
this.location.vehicleId = this.currentSelectedLocation.vehicleId
},
submitLocationStatus() {
const loading = ElLoading.service({
lock: true,
text: 'Loading',
background: 'rgba(0, 0, 0, 0.7)',
})
this.currentSelectedLocation.locationStatus = this.location.locationStatus
this.currentSelectedLocation.isLock = this.location.isLock
this.currentSelectedLocation.vehicleId = this.location.vehicleId
updateLocation(this.currentSelectedLocation).then(res => {
if (res.data.code == 0) {
loading.close()
this.dialogVisible = false
ElMessage({
message: '更新库位信息成功!',
type: 'success',
})
this.getAllLocations()
} else {
ElMessage.error(res.data.message)
}
}).catch(err => {
loading.close()
ElMessage.error('更新库位信息失败')
})
},
setCurrentLocationTitle() {
this.currentLocationTitle = '当前选择库位:' + this.currentSelectedLocation.queue + '排' + this.currentSelectedLocation.line + '列' + this.currentSelectedLocation.layer + '层'
}
}
}
</script>
<style scoped>
.el-pagination {
padding-left: 5px;
}
.row {
display: flex;
justify-content: space-between;
margin: 5px;
}
.col {
/* width: 100px; */
/* height: 40px; */
padding: 1px;
margin: 1px;
}
.col .el-button {
width: 100px;
height: 50px;
}
</style>

View File

@ -0,0 +1,132 @@
<template>
<div style="margin-bottom: 15px">
<el-config-provider :locale="zhCn">
<el-row>
<el-input v-model="goodsTypeQuery" style="width: 256px; margin-right: 10px;" placeholder="型号" />
<el-input v-model="specificationQuery" style="width: 256px; margin-right: 10px;" placeholder="规格" />
<el-button type="primary" @click="search()">搜索</el-button>
<el-button type="warning" @click="reset()">重置</el-button>
</el-row>
<br />
<el-table :data="tasks" stripe border v-loading="loading" style="width: 100%" max-height="684px"
class="table-class" :header-cell-style="{ 'text-align': 'center' }" :cell-style="{ 'text-align': 'center' }">
<el-table-column prop="taskType" label="任务类型" fixed="left" :formatter="taskTypeFormat" min-width="120px" />
<el-table-column prop="goodsType" label="型号" min-width="120px" />
<el-table-column prop="specification" label="规格" min-width="120px" />
<el-table-column prop="batchNo" label="批次号" min-width="120px" />
<el-table-column prop="taskGroup" label="任务单号" min-width="120px" />
<el-table-column prop="origin" label="起点" min-width="120px" />
<el-table-column prop="destination" label="终点" min-width="120px" />
<el-table-column prop="operateNum" label="操作数量" min-width="120px" />
<el-table-column prop="totalNum" label="库存数量" min-width="120px" />
<el-table-column prop="taskPriority" label="任务优先级" min-width="120px" />
<el-table-column prop="preTask" label="前置任务" min-width="120px" show-overflow-tooltip />
<el-table-column prop="createTime" label="创建时间" :formatter="timeFormat" min-width="120px" />
<!-- <el-table-column prop="userName" label="操作人员姓名" min-width="120px" /> -->
<el-table-column prop="taskStatus" label="任务状态" fixed="right" :formatter="taskStatusFormat"
min-width="120px" />
</el-table>
<br />
<el-pagination v-model:current-page="currentPage" v-model:page-size="pageSize" :page-sizes="[10, 25, 50]"
:small="false" :disabled="false" :background="false" :default-page-size="10"
layout="total, sizes, prev, pager, next, jumper" :total="total" @size-change="search"
@current-change="search" />
</el-config-provider>
</div>
</template>
<script setup>
import { getTaskRecords } from '@/api/record.js'
import { dateFormatter, taskStatusFormatter, timeFormatter } from '@/utils/formatter.js'
import zhCn from 'element-plus/dist/locale/zh-cn.mjs'
import { ElMessage } from 'element-plus'
import store from '@/store'
</script>
<script>
export default {
name: 'outTaskRecord',
data() {
return {
pageInfo: {},
tasks: [],
currentPage: 1,
pageSize: 10,
total: 0,
goodsTypeQuery: '',
specificationQuery: '',
loading: true
}
},
mounted() {
this.search()
},
methods: {
dateFormat: (row, column, cellValue, index) => {
return dateFormatter(cellValue)
},
timeFormat: (row, column, cellValue, index) => {
return timeFormatter(cellValue)
},
taskStatusFormat: (row, column, cellValue, index) => {
return taskStatusFormatter(cellValue)
},
taskTypeFormat: (row, column, cellValue, index) => {
switch (cellValue) {
case 1: return '入库'
case 2: return '出库'
case 3: return '盘点'
case 9: return '移库'
default: return '未知'
}
},
search() {
this.loading = true
this.pageInfo.pageNum = this.currentPage
this.pageInfo.pageSize = this.pageSize
const tableRequest = {
page: this.pageInfo,
param: {
taskType: 2,
goodsType: this.goodsTypeQuery.trim(),
specification: this.specificationQuery.trim(),
userName: store.getters.getUserName
}
}
getTaskRecords(tableRequest).then(res => {
const tableResponse = res.data
if (tableResponse.code != 0) {
ElMessage.error(tableResponse.message)
}
this.tasks = tableResponse.rows
this.total = tableResponse.total
}).catch(err => {
console.log(err)
ElMessage.error('查询任务记录错误')
})
this.loading = false
},
reset() {
this.goodsTypeQuery = ''
this.specificationQuery = ''
this.search()
},
},
}
</script>
<style scoped>
.el-pagination {
padding-left: 5px;
}
.el-row .el-button {
width: 72px;
margin-left: 0px;
margin-right: 5px;
}
.table-class {
width: 100%;
}
</style>

300
src/layout/scanConfirm.vue Normal file
View File

@ -0,0 +1,300 @@
<template>
<el-config-provider :locale="zhCn">
<el-container class="content">
<div class="left">
<fieldset class="display-area">
<legend>
入库站台1
</legend>
<el-table :data="stand1ScanInfos" stripe border v-loading="loading" style="width: 100%"
max-height="684px" class="table-class" :header-cell-style="{ 'text-align': 'center' }"
:cell-style="{ 'text-align': 'center' }">
<el-table-column width="65px" fixed="left">
<template v-slot="scope">
<el-radio :label="scope.row.scanId" v-model="scanId1">&nbsp;</el-radio>
</template>
</el-table-column>
<el-table-column prop="standId" label="站台" min-width="120px" />
<el-table-column prop="position" label="位置" min-width="120px" />
<el-table-column prop="requestCode" label="请求条码" min-width="120px" />
<el-table-column prop="scanCode" label="设备扫码" min-width="120px" />
<el-table-column fixed="right" label="操作" width="120px">
<template v-slot="scope">
<el-button plain type="primary" @click="confirmCurrentRow(scope.row)">处理</el-button>
</template>
</el-table-column>
</el-table>
<br />
</fieldset>
</div>
<div class="right">
<fieldset class="display-area">
<legend>
入库站台2
</legend>
<el-table :data="stand2ScanInfos" stripe border v-loading="loading" style="width: 100%"
max-height="684px" class="table-class" :header-cell-style="{ 'text-align': 'center' }"
:cell-style="{ 'text-align': 'center' }">
<el-table-column width="65px" fixed="left">
<template v-slot="scope">
<el-radio :label="scope.row.scanId" v-model="scanId2">&nbsp;</el-radio>
</template>
</el-table-column>
<el-table-column prop="standId" label="站台" min-width="120px" />
<el-table-column prop="position" label="位置" min-width="120px" />
<el-table-column prop="requestCode" label="请求条码" min-width="120px" />
<el-table-column prop="scanCode" label="设备扫码" min-width="120px" />
<el-table-column fixed="right" label="操作" width="120px">
<template v-slot="scope">
<el-button plain type="primary" @click="confirmCurrentRow(scope.row)">处理</el-button>
</template>
</el-table-column>
</el-table>
<br />
</fieldset>
</div>
<el-dialog v-model="dialogVisible" title="确认扫码信息" width="40%" draggable :show-close="false">
<el-form ref="confirmFormRef" :model="confirmFormEntity" :label-position="labelPosition"
label-width="100px" style="max-width: 100%" :rules="rules" status-icon>
<el-row :gutter="16">
<el-col :span="12" :offset="0">
<el-form-item label="站台" prop="standId">
<el-input v-model="confirmFormEntity.standId" disabled />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="位置" prop="position">
<el-input v-model="confirmFormEntity.position" disabled />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="16">
<el-col :span="12" :offset="0">
<el-form-item label="请求条码号" prop="requestCode">
<el-input v-model="confirmFormEntity.requestCode" disabled />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="扫描条码号" prop="scanCode">
<el-input v-model="confirmFormEntity.scanCode" disabled />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="16">
<el-col :span="12" :offset="0">
<el-form-item label="确认条码号" prop="confirmCode">
<el-select-v2 v-model="confirmFormEntity.confirmCode" placeholder="请选择正确的条码号"
:options="confirmFormEntity.barCodeOptions"></el-select-v2>
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="submitConfirmCode(confirmFormEntity)">
确定
</el-button>
</span>
</template>
</el-dialog>
</el-container>
</el-config-provider>
</template>
<script setup>
import { queryNotConfirmScanInfo, solveScanDifference } from '@/api/task'
import { reactive, ref } from 'vue'
import { ElMessage } from 'element-plus'
import zhCn from 'element-plus/dist/locale/zh-cn.mjs'
const confirmFormRef = ref()
</script>
<script>
export default {
name: 'scanConfirm',
data() {
return {
labelPosition: "top",
loading: false,
scanId1: '',
scanId2: '',
stand1ScanInfos: [],
stand2ScanInfos: [],
tempTasks: [],
tempStandId: '',
confirmFormEntity: reactive({
scanId: '',
standId: '',
position: null,
requestCode: '',
scanCode: '',
confirmCode: '',
barCodeOptions: []
}),
rules: reactive({
confirmCode: [
{ required: true, message: '请确认条码' }
]
}),
dialogVisible: false
}
},
mounted() {
this.timer = setInterval(() => {
this.getStand1Codes()
this.getStand2Codes()
}, 2000)
},
beforeUnmount() {
clearInterval(this.timer)
},
methods: {
getStand1Codes() {
const scanRequest = {
standId: 'R1'
}
queryNotConfirmScanInfo(scanRequest).then(res => {
const scanResponse = res.data
if (scanResponse.code != 0) {
ElMessage.error(scanResponse.message)
}
this.stand1ScanInfos = scanResponse.returnData
}).catch(err => {
console.log(err)
ElMessage.error('查询站台1扫码确认信息错误')
})
},
getStand2Codes() {
const scanRequest = {
standId: 'R2'
}
queryNotConfirmScanInfo(scanRequest).then(res => {
const scanResponse = res.data
if (scanResponse.code != 0) {
ElMessage.error(scanResponse.message)
}
this.stand2ScanInfos = scanResponse.returnData
}).catch(err => {
console.log(err)
ElMessage.error('查询站台2扫码确认信息错误')
})
},
confirmCurrentRow(row) {
this.confirmFormEntity.scanId = row.scanId
this.confirmFormEntity.standId = row.standId
this.confirmFormEntity.position = row.position
this.confirmFormEntity.requestCode = row.requestCode
this.confirmFormEntity.scanCode = row.scanCode
this.confirmFormEntity.confirmCode = ''
this.confirmFormEntity.barCodeOptions = [
{ value: row.requestCode, label: row.requestCode },
{ value: row.scanCode, label: row.scanCode }
]
this.dialogVisible = true
},
submitConfirmCode(confirmFormEntity) {
if (confirmFormEntity.scanId == null | undefined || confirmFormEntity.scanId == '') {
ElMessage({ message: '缺少主键', type: 'error' })
return
}
if (confirmFormEntity.confirmCode == null | undefined || confirmFormEntity.confirmCode == '') {
ElMessage({ message: '确认条码不能为空', type: 'error' })
return
}
solveScanDifference(confirmFormEntity).then(res => {
if (res.data.code == 0) {
this.clearFormEntity(confirmFormEntity)
this.dialogVisible = false
ElMessage({
message: '处理扫码信息成功',
type: 'success'
})
} else {
ElMessage.error(res.data.message)
}
}).catch(err => {
ElMessage({
message: '处理扫码信息失败: ' + err,
type: 'error',
showClose: true
})
})
},
clearFormEntity(confirmFormEntity) {
confirmFormEntity.scanId = ''
confirmFormEntity.standId = ''
confirmFormEntity.position = null
confirmFormEntity.requestCode = ''
confirmFormEntity.scanCode = ''
confirmFormEntity.confirmCode = ''
confirmFormEntity.barCodeOptions = []
}
}
}
</script>
<style scoped>
.content {
display: flex;
width: 100%;
}
.el-row .el-form-item {
width: 30% inherit;
justify-content: center;
}
/* .btn-area .el-form-item {
width: 30% inherit;
padding-left: 15px;
} */
.el-row .el-form-item .el-select {
width: 100% !important;
}
.el-row .el-form-item .el-select-v2 {
width: 100% !important;
}
.el-row .el-form-item .el-input-number {
width: 100% !important;
}
.el-row .el-form-item .el-button {
margin: auto;
}
.right {
width: 49%;
padding: 5px;
}
.left {
width: 49%;
padding: 5px;
}
.input-area {
margin: auto;
max-width: inherit;
height: 632px;
border: solid 1px;
border-radius: 10px;
box-shadow: 0px 15px 10px -15px #000;
}
.display-area {
margin: auto;
min-width: inherit;
height: 632px;
border: solid 1px;
border-radius: 10px;
box-shadow: 0px 15px 10px -15px #000;
}
.table-class {
width: 100%;
}
</style>

View File

@ -0,0 +1,260 @@
<template>
<div style="margin-bottom: 10px">
<el-config-provider :locale="zhCn">
<el-row>
<el-input v-model="queryKey" style="width: 256px; margin-right: 10px;" placeholder="箱号" />
<el-button type="primary" @click="search()">搜索</el-button>
<el-button type="warning" @click="reset()">重置</el-button>
<!-- <el-button type="success" @click="refresh()">刷新</el-button> -->
</el-row>
<br />
<el-table :data="vehicles" stripe border v-loading="loading" class="table-class" max-height="650px"
highlight-current-row @row-click="getCurrentRow" :header-cell-style="{ 'text-align': 'center' }" :cell-style="{ 'text-align': 'center' }">
<el-table-column width="65px" fixed="left">
<template v-slot="scope">
<el-radio :label="scope.row.vehicleId" v-model="vehicleId">&nbsp;</el-radio>
</template>
</el-table-column>
<el-table-column prop="vehicleId" label="箱号" fixed="left" min-width="120px" />
<el-table-column prop="currentLocation" label="当前位置" fixed="left" min-width="120px" />
<el-table-column prop="vehicleStatus" label="料箱状态" :formatter="vehicleStatusFormat" min-width="120px" />
<el-table-column prop="isEmpty" label="是否空箱" :formatter="isEmptyFormat" min-width="120px" />
<el-table-column fixed="right" label="操作" width="200px" v-if="selVehicle == null">
<template v-slot="scope">
<el-button plain type="primary" @click="editCurrentRowVehicle(scope.row)">编辑</el-button>
<el-button plain type="danger" @click="deleteCurrentRowVehicle(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<br />
<el-pagination v-model:current-page="currentPage" v-model:page-size="pageSize" :page-sizes="[10, 25, 50]"
:small="false" :disabled="false" :background="false" :default-page-size="10"
layout="total, sizes, prev, pager, next, jumper" :total="total" @size-change="search"
@current-change="search" />
<el-dialog v-model="dialogVisible" title="托盘信息" width="40%" draggable :show-close="false">
<el-form ref="vehicleFormRef" :model="vehicleFormEntity" :label-position="labelPosition" label-width="100px"
style="max-width: 100%" :rules="rules" status-icon>
<el-row :gutter="16">
<el-col :span="12" :offset="0">
<el-form-item label="料箱号" prop="vehicleId">
<el-input v-model="vehicleFormEntity.vehicleId" disabled />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="当前位置" prop="currentLocation">
<el-input v-model="vehicleFormEntity.currentLocation" placeholder="请输入当前位置" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="16">
<el-col :span="12" :offset="0">
<el-form-item label="是否空箱" prop="isEmpty">
<el-select-v2 v-model="vehicleFormEntity.isEmpty" placeholder="请选择托盘状态"
:options="isEmptyOptions"></el-select-v2>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="托盘状态" prop="vehicleStatus">
<el-select-v2 v-model="vehicleFormEntity.vehicleStatus" placeholder="请选择托盘状态"
:options="vehicleStatusOptions"></el-select-v2>
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="submitVehicleInfo(vehicleFormEntity)">
确定
</el-button>
</span>
</template>
</el-dialog>
</el-config-provider>
</div>
</template>
<script setup>
import { getAllVehicles, updateVehicleInfo, deleteCurrentVehicle } from '@/api/vehicle'
import zhCn from 'element-plus/dist/locale/zh-cn.mjs'
import { ElMessage } from 'element-plus'
import { ref, reactive } from 'vue'
import { vehicleStatusFormatter, locationFormatter } from '@/utils/formatter.js'
</script>
<script>
export default {
name: 'stand',
props: ['selVehicle'],
emits: ['update:selVehicle'],
data() {
return {
pageInfo: {},
vehicles: [],
currentPage: 1,
pageSize: 10,
total: 0,
queryKey: '',
loading: true,
dialogVisible: false,
vehicleId: '',
vehicleFormEntity: reactive({}),
labelPosition: 'top',
vehicleFormRef: ref(),
rules: reactive({
locationId: [
{ required: true, message: '请输入库位' }
]
}),
//
isEmptyOptions: [
{
value: 0,
label: '带料'
},
{
value: 1,
label: '空箱'
}
],
//
vehicleStatusOptions: [
{
value: 0,
label: '入库中'
},
{
value: 1,
label: '在库中'
},
{
value: 2,
label: '出库中'
}
]
}
},
mounted() {
this.search()
},
methods: {
search() {
this.loading = true
this.pageInfo.pageNum = this.currentPage
this.pageInfo.pageSize = this.pageSize
const tableRequest = {
page: this.pageInfo,
param: {
vehicleId: this.queryKey.trim()
},
}
getAllVehicles(tableRequest).then(res => {
const tableResponse = res.data
if (tableResponse.code != 0) {
console.log(tableResponse.code + ':' + tableResponse.message)
ElMessage.error(tableResponse.message)
}
this.vehicles = tableResponse.rows
this.total = tableResponse.total
}).catch(err => {
ElMessage.error('查询托盘信息错误' + err.message)
})
this.loading = false
},
vehicleStatusFormat: (row, column, cellValue, index) => {
return vehicleStatusFormatter(cellValue)
},
locationFormat: (row, column, cellValue, index) => {
return locationFormatter(cellValue)
},
isEmptyFormat: (row, column, cellValue, index) => {
if (cellValue == 0) {
return '带料'
}
if (cellValue == 1) {
return '空箱'
}
},
reset() {
this.queryKey = ''
this.search()
},
editCurrentRowVehicle(row) {
this.vehicleFormEntity = row
this.dialogVisible = true
},
deleteCurrentRowVehicle(row) {
this.vehicleId = row.vehicleId
const vehicle = {
vehicleId: row.vehicleId
}
deleteCurrentVehicle(vehicle).then(res => {
if (res.data.code == 0) {
ElMessage({
message: '删除料箱成功',
type: 'success',
})
this.search()
} else {
ElMessage.error(res.data.message)
}
}).catch(err => {
ElMessage.error('删除料箱信息失败:' + err)
})
},
submitVehicleInfo(formData) {
updateVehicleInfo(formData).then(res => {
if (res.data.code == 0) {
this.dialogVisible = false
ElMessage({
message: '更新料箱信息成功',
type: 'success',
})
this.search()
} else {
ElMessage.error(res.data.message)
}
}).catch(err => {
ElMessage.error('更新料箱信息失败:' + err)
})
},
getCurrentRow(row) {
this.vehicleId = row.vehicleId
this.$emit('update:selVehicle', row)
}
}
}
</script>
<style scoped>
.el-pagination {
padding-left: 5px;
}
.el-row .el-button {
width: 72px;
margin-left: 0px;
margin-right: 5px;
}
.table-class {
width: 100%;
/* font-size: 5px; */
}
.el-row .el-form-item {
width: 10% inherit;
justify-content: center;
}
.el-row .el-form-item .el-select-v2 {
width: 100% !important;
}
.el-row .el-form-item .el-input-number {
width: 100% !important;
}
.el-row .el-form-item .el-button {
margin: auto;
}
</style>

397
src/layout/stock.vue Normal file
View File

@ -0,0 +1,397 @@
<template>
<div style="margin-bottom: 10px">
<el-config-provider :locale="zhCn">
<el-row>
<el-input v-model="queryKey" style="width: 256px; margin-right: 10px;" placeholder="型号/规格/批次号"
:suffix-icon="Search" />
<el-button type="primary" @click="search()">搜索</el-button>
<el-button type="warning" @click="reset()">重置</el-button>
<el-button type="success" @click="exportExcel()">导出表格</el-button>
</el-row>
<br />
<el-table id="stock-table" :data="displayStocks" stripe border v-loading="loading" class="table-class"
max-height="650px" highlight-current-row @row-click="getCurrentRow" :row-style="rowStyle"
:header-cell-style="{ 'text-align': 'center' }" :cell-style="{ 'text-align': 'center' }">
<el-table-column width="65px" fixed="left">
<template v-slot="scope">
<el-radio :label="scope.row.stockId" v-model="stockId">&nbsp;</el-radio>
</template>
</el-table-column>
<el-table-column prop="goodsType" label="型号" fixed="left" min-width="120px" />
<el-table-column prop="specification" label="规格" fixed="left" min-width="120px" />
<el-table-column prop="barcode" label="条码号" min-width="120px" />
<el-table-column prop="batchNo" label="批次号" min-width="120px" />
<el-table-column prop="originNum" label="入库数量" min-width="120px" />
<el-table-column prop="realNum" label="实际数量" min-width="120px" />
<el-table-column prop="locationId" label="库位" :formatter="locationFormat" min-width="120px" />
<el-table-column prop="isInventory" label="是否盘点" min-width="120px" />
<el-table-column prop="orderId" label="订单号" min-width="120px" />
<el-table-column prop="createTime" label="上架时间" :formatter="timeFormat" min-width="140px" />
<el-table-column prop="goodsStatus" label="物料状态" :formatter="goodsStatusFormat" min-width="120px" />
<el-table-column prop="stockStatus" label="库存状态" :formatter="stockStatusFormat" fixed="right"
min-width="120px" />
<el-table-column fixed="right" label="操作" width="120px" v-if="selStock == null">
<template v-slot="scope">
<el-button plain type="primary" @click="editCurrentRowStock(scope.row)">编辑</el-button>
</template>
</el-table-column>
</el-table>
<br />
<el-pagination v-model:current-page="currentPage" v-model:page-size="pageSize" :page-sizes="[10, 25, 50]"
:small="false" :disabled="false" :background="false" :default-page-size="10" @size-change="search"
@current-change="search" layout="total, sizes, prev, pager, next, jumper" :total="total" />
<el-dialog v-model="dialogVisible" title="库存信息" width="40%" draggable :show-close="false">
<el-form ref="stockFormRef" :model="stockFormEntity" :label-position="labelPosition" label-width="100px"
style="max-width: 100%" :rules="rules" status-icon>
<el-row :gutter="16">
<el-col :span="12" :offset="0">
<el-form-item label="型号" prop="goodsType">
<el-input v-model="stockFormEntity.goodsType" clearable />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="规格" prop="specification">
<el-input v-model="stockFormEntity.specification" clearable />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="16">
<el-col :span="12" :offset="0">
<el-form-item label="条码号" prop="barcode">
<el-input v-model="stockFormEntity.barcode" clearable />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="批次号" prop="batchNo">
<el-input v-model="stockFormEntity.batchNo" clearable />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="16">
<el-col :span="12" :offset="0">
<el-form-item label="入库数量" prop="originNum">
<el-input-number v-model.number="stockFormEntity.originNum" clearable
controls-position="right" :min="0" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="实际数量为0时删除库存" prop="realNum">
<el-input-number v-model.number="stockFormEntity.realNum" controls-position="right"
:min="0" clearable />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="16">
<el-col :span="12" :offset="0">
<el-form-item label="库位" prop="locationId">
<el-input v-model="stockFormEntity.locationId" clearable />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="订单号" prop="orderId">
<el-input v-model="stockFormEntity.orderId" clearable />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="16">
<el-col :span="12" :offset="0">
<el-form-item label="库存状态" prop="stockStatus">
<el-select-v2 v-model="stockFormEntity.stockStatus" placeholder="请选择库存状态"
:options="stockStatusOptions"></el-select-v2>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="物料状态" prop="goodsStatus">
<el-select-v2 v-model="stockFormEntity.goodsStatus" placeholder="请选择物料状态"
:options="goodsStatusOptions"></el-select-v2>
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="submitStockInfo(stockFormEntity)">
确定
</el-button>
</span>
</template>
</el-dialog>
</el-config-provider>
</div>
</template>
<script setup>
import { getAllStocks, updateStockInfo } from '@/api/stock.js'
import { downloadStockExcel } from '@/api/excel.js'
import { dateFormatter, locationFormatter, timeFormatter } from '@/utils/formatter.js'
import zhCn from 'element-plus/dist/locale/zh-cn.mjs'
import { Search } from '@element-plus/icons-vue'
import { ref, reactive } from 'vue'
import { ElMessage } from 'element-plus'
// import FileSaver from 'file-saver';
// import * as XLSX from 'xlsx';
</script>
<script>
export default {
name: 'stock',
props: ['selStock'],
emits: ['update:selStock'],
data() {
return {
pageInfo: {},
displayStocks: [],
currentPage: 1,
pageSize: 10,
total: 0,
queryKey: '',
loading: true,
stockId: '',
stockFormRef: ref(),
stockFormEntity: reactive({}),
rules: reactive({
goodsType: [
{ required: true, message: '请输入型号' }
],
specification: [
{ required: true, message: '请输入规格' }
],
barcode: [
{ required: true, message: '请输入条码号' }
],
batchNo: [
{ required: true, message: '请输入批次号' }
],
locationId: [
{ required: true, message: '请输入库位' }
],
orderId: [
{ required: true, message: '请输入订单号' }
],
stockStatus: [
{ required: true, message: '请输入库存状态' }
],
goodsStatus: [
{ required: true, message: '请输入物料状态' }
],
originNum: [
{ required: true, message: '请输入入库数量' },
{ type: 'number', message: '请输入数字' }
],
realNum: [
{ required: true, message: '请输入实际数量' },
{ type: 'number', message: '请输入数字' }
]
}),
labelPosition: 'top',
dialogVisible: false,
goodsStatusOptions: [
{
value: 0,
label: '合格'
},
{
value: 1,
label: '不合格'
},
{
value: 2,
label: '延期'
},
{
value: 3,
label: '过期'
},
{
value: 5,
label: '长时间未使用'
}
],
stockStatusOptions: [
{
value: 0,
label: '库存正常'
},
{
value: 1,
label: '出库中'
},
{
value: 2,
label: '盘点中'
},
{
value: 3,
label: '移库中'
},
{
value: 9,
label: '库存锁定'
}
],
}
},
mounted() {
this.search()
},
methods: {
locationFormat: (row, column, cellValue, index) => {
return locationFormatter(cellValue)
},
dateFormat: (row, column, cellValue, index) => {
return dateFormatter(cellValue)
},
timeFormat: (row, column, cellValue, index) => {
return timeFormatter(cellValue)
},
goodsStatusFormat: (row, column, cellValue, index) => {
if (cellValue === 0) {
return '合格'
} else if (cellValue === 1) {
return '不合格'
} else if (cellValue === 2) {
return '延期'
} else if (cellValue === 3) {
return '过期'
} else if (cellValue === 5) {
return '长时间未使用'
}
},
stockStatusFormat: (row, column, cellValue, index) => {
switch (cellValue) {
case 0:
return '库存正常'
case 1:
return '出库中'
case 2:
return '盘点中'
case 3:
return '移库中'
case 9:
return '库存锁定'
default:
return '异常'
}
},
rowStyle: ({ row, rowIndex }) => {
if (row.goodsStatus == 3) {
return { "color": "red" }
}
if (row.goodsStatus == 5) {
return { "color": "yellow" }
}
},
search() {
this.loading = true
this.pageInfo.pageNum = this.currentPage
this.pageInfo.pageSize = this.pageSize
const tableRequest = {
page: this.pageInfo,
param: this.queryKey.trim(),
}
getAllStocks(tableRequest).then(res => {
const tableResponse = res.data
if (tableResponse.code != 0) {
console.log(tableResponse.code + ':' + tableResponse.message)
ElMessage.error(tableResponse.message)
}
this.displayStocks = tableResponse.rows
this.total = tableResponse.total
}).catch(err => {
console.log(err)
ElMessage.error('查询库存错误')
})
this.loading = false
},
reset() {
this.queryKey = ''
this.search()
},
getCurrentRow(row) {
this.stockId = row.stockId
this.$emit('update:selStock', row)
},
exportExcel() {
downloadStockExcel().then(res => {
const link = document.createElement('a');//a
try {
// let blob = new Blob([res.data],{type: 'application/vnd.ms-excel'}); //blobblobtypexls
let blob = res.data //blob
// let _fileName = res.headers['content-disposition'].split(';')[1].split('=')[1]; //
let _fileName = "库存报表" + dateFormatter(new Date) + ".xlsx"
link.style.display = 'none'//
// URL
const url = window.URL || window.webkitURL || window.moxURL
link.href = url.createObjectURL(blob)
link.setAttribute('download', _fileName.substring(_fileName.lastIndexOf('_') + 1))
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
url.revokeObjectURL(link.href)//url
} catch (e) {
ElMessage({
message: '下载文件失败:: ' + e,
type: 'error',
showClose: true
})
}
}).catch(err => {
ElMessage({
message: '导出失败:: ' + err,
type: 'error',
showClose: true
})
})
},
editCurrentRowStock(row) {
this.stockFormEntity = row
this.dialogVisible = true
},
submitStockInfo(stockFormEntity) {
updateStockInfo(stockFormEntity).then(res => {
if (res.data.code == 0) {
this.dialogVisible = false
ElMessage({
message: '更新库存成功',
type: 'success'
})
this.search()
} else {
ElMessage.error(res.data.message)
}
}).catch(err => {
ElMessage({
message: '更新料箱信息失败: ' + err,
type: 'error',
showClose: true
})
})
}
},
}
</script>
<style scoped>
.el-pagination {
padding-left: 5px;
}
.el-row .el-button {
width: 72px;
margin-left: 0px;
margin-right: 5px;
}
.table-class {
width: 100%;
}
.el-row .el-form-item .el-select-v2 {
width: 100% !important;
}
.el-row .el-form-item .el-input-number {
width: 100% !important;
}
</style>

321
src/layout/taskMonitor.vue Normal file
View File

@ -0,0 +1,321 @@
<template>
<div style="margin-bottom: 15px">
<el-config-provider :locale="zhCn">
<el-row>
<el-input v-model="goodsTypeQuery" style="width: 256px; margin-right: 10px;" placeholder="型号" />
<el-input v-model="specificationQuery" style="width: 256px; margin-right: 10px;" placeholder="规格" />
<el-button type="primary" @click="search()">搜索</el-button>
<el-button type="warning" @click="reset()">重置</el-button>
</el-row>
<br />
<el-table :data="tasks" stripe border v-loading="loading" style="width: 100%" max-height="684px"
class="table-class" :header-cell-style="{ 'text-align': 'center' }" :cell-style="{ 'text-align': 'center' }"
@row-click="getCurrentRow">
<el-table-column width="65px" fixed="left">
<template v-slot="scope">
<el-radio :label="scope.row.taskId" v-model="taskId">&nbsp;</el-radio>
</template>
</el-table-column>
<el-table-column prop="taskType" label="任务类型" fixed="left" :formatter="taskTypeFormat" min-width="120px" />
<el-table-column prop="goodsType" label="型号" min-width="120px" />
<el-table-column prop="specification" label="规格" min-width="120px" />
<el-table-column prop="batchNo" label="批次号" min-width="120px" />
<el-table-column prop="taskGroup" label="任务单号" min-width="120px" />
<el-table-column prop="origin" label="起点" min-width="120px" />
<el-table-column prop="destination" label="终点" min-width="120px" />
<el-table-column prop="operateNum" label="操作数量" min-width="120px" />
<el-table-column prop="totalNum" label="库存数量" min-width="120px" />
<el-table-column prop="taskPriority" label="任务优先级" min-width="120px" />
<el-table-column prop="preTask" label="前置任务" min-width="120px" show-overflow-tooltip />
<el-table-column prop="createTime" label="创建时间" :formatter="timeFormat" min-width="120px" />
<el-table-column prop="createTime" label="运行时长" :formatter="dueFormat" min-width="120px" />
<!-- <el-table-column prop="userName" label="操作人员姓名" min-width="120px" /> -->
<el-table-column prop="taskStatus" label="任务状态" fixed="right" :formatter="taskStatusFormat"
min-width="120px" />
<!-- <el-table-column fixed="right" label="操作" width="120px">
<template v-slot="scope">
<el-button plain type="primary" @click="editCurrentRowTask(scope.row)">编辑</el-button>
</template>
</el-table-column> -->
</el-table>
<br />
<el-pagination v-model:current-page="currentPage" v-model:page-size="pageSize" :page-sizes="[10, 25, 50]"
:small="false" :disabled="false" :background="false" :default-page-size="10"
layout="total, sizes, prev, pager, next, jumper" :total="total" @size-change="search"
@current-change="search" />
<el-dialog v-model="dialogVisible" title="任务信息" width="40%" draggable :show-close="false">
<el-form ref="taskFormRef" :model="taskFormEntity" :label-position="labelPosition" label-width="100px"
style="max-width: 100%" :rules="rules" status-icon>
<el-row :gutter="16">
<el-col :span="12" :offset="0">
<el-form-item label="料箱号" prop="vehicleNo">
<el-input v-model="taskFormEntity.vehicleNo" disabled />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="零件号" prop="goodsId">
<el-input v-model="taskFormEntity.goodsId" disabled />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="16">
<el-col :span="12" :offset="0">
<el-form-item label="起点" prop="origin">
<el-input v-model="taskFormEntity.origin" disabled />
</el-form-item>
</el-col>
<el-col :span="12" v-if="taskFormEntity.taskType == 1">
<el-form-item label="终点" prop="destination">
<el-select-v2 v-model="taskFormEntity.destination"
:options="availableLocationOptions"></el-select-v2>
</el-form-item>
</el-col>
<el-col :span="12" v-if="taskFormEntity.taskType == 2">
<el-form-item label="终点" prop="destination">
<el-input v-model="taskFormEntity.destination" disabled />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="16">
<el-col :span="12" :offset="0">
<el-form-item label="任务类型" prop="taskType">
<el-select-v2 v-model="taskFormEntity.taskType" placeholder="请选择任务类型" disabled
:options="taskTypeOptions"></el-select-v2>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="任务状态" prop="taskStatus">
<el-select-v2 v-model="taskFormEntity.taskStatus" placeholder="请选择任务状态"
:options="taskStatusOptions"></el-select-v2>
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="submitTaskInfo(taskFormEntity)">
确定
</el-button>
</span>
</template>
</el-dialog>
</el-config-provider>
</div>
</template>
<script setup>
import { getTasks, changeTaskStatus } from '@/api/task.js'
import { getAvailableLocations } from '@/api/location.js'
import { dateFormatter, locationFormatter, taskStatusFormatter, timeFormatter, dueFormatter } from '@/utils/formatter.js'
import zhCn from 'element-plus/dist/locale/zh-cn.mjs'
import { ElMessage } from 'element-plus'
import store from '@/store'
import { ref, reactive } from 'vue'
</script>
<script>
export default {
name: 'taskMonitor',
data() {
return {
pageInfo: {},
tasks: [],
currentPage: 1,
pageSize: 10,
total: 0,
goodsTypeQuery: '',
specificationQuery: '',
loading: true,
dialogVisible: false,
taskFormEntity: reactive({}),
labelPosition: 'top',
taskFormRef: ref(),
rules: reactive({}),
taskId: '',
taskTypeOptions: [
{
value: 1,
label: '入库'
},
{
value: 2,
label: '出库'
},
{
value: 3,
label: '盘点'
},
{
value: 9,
label: '移库'
},
],
taskStatusOptions: [
{
value: 0,
label: '任务重置'
},
{
value: 1,
label: '任务取消'
},
{
value: 2,
label: '任务完成'
}
],
availableLocationOptions: []
}
},
mounted() {
this.search()
},
methods: {
dateFormat: (row, column, cellValue, index) => {
return dateFormatter(cellValue)
},
timeFormat: (row, column, cellValue, index) => {
return timeFormatter(cellValue)
},
taskStatusFormat: (row, column, cellValue, index) => {
return taskStatusFormatter(cellValue)
},
taskTypeFormat: (row, column, cellValue, index) => {
switch (cellValue) {
case 1: return '入库'
case 2: return '出库'
case 3: return '盘点'
case 9: return '移库'
default: return '未知'
}
},
dueFormat: (row, column, cellValue, index) => {
return dueFormatter(cellValue)
},
search() {
this.loading = true
this.pageInfo.pageNum = this.currentPage
this.pageInfo.pageSize = this.pageSize
const tableRequest = {
page: this.pageInfo,
param: {
goodsType: this.goodsTypeQuery.trim(),
specification: this.specificationQuery.trim(),
userName: store.getters.getUserName
}
}
getTasks(tableRequest).then(res => {
const tableResponse = res.data
if (tableResponse.code != 0) {
console.log(tableResponse.code + ':' + tableResponse.message)
ElMessage.error(tableResponse.message)
}
this.tasks = tableResponse.rows
this.total = tableResponse.total
}).catch(err => {
console.log(err)
ElMessage.error('查询任务错误')
})
this.loading = false
},
reset() {
this.goodsTypeQuery = ''
this.specificationQuery = ''
},
editCurrentRowTask(row) {
if (row.taskType == 1) {
this.availableLocationOptions = []
var currentOption = {
value: row.destination,
label: locationFormatter(row.destination)
}
this.availableLocationOptions.push(currentOption)
//
const requestParam = {
areaId: 1
}
getAvailableLocations(requestParam).then(res => {
if (res.data.code == 0) {
for (const location of res.data.returnData) {
var newOption = {}
newOption.value = location.locationId
newOption.label = locationFormatter(location.locationId)
this.availableLocationOptions.push(newOption)
}
}
}).catch(err => {
ElMessage.error('查找可用库位失败:' + err)
})
}
this.taskFormEntity.taskId = row.taskId
this.taskFormEntity.vehicleNo = row.vehicleNo
this.taskFormEntity.goodsId = row.goodsId
this.taskFormEntity.origin = row.origin
this.taskFormEntity.destination = row.destination
this.taskFormEntity.taskType = row.taskType
this.taskFormEntity.taskStatus = null
this.dialogVisible = true
},
submitTaskInfo(formData) {
if (formData.taskStatus == null || formData.taskStatus == undefined) {
ElMessage.error('请选择任务状态')
return
}
formData.userName = store.getters.getUserName
changeTaskStatus(formData).then(res => {
if (res.data.code == 0) {
this.dialogVisible = false
ElMessage({
message: '更新任务状态成功',
type: 'success',
})
this.taskFormEntity.taskId = ''
this.taskFormEntity.vehicleNo = ''
this.taskFormEntity.goodsId = ''
this.taskFormEntity.origin = ''
this.taskFormEntity.destination = ''
this.taskFormEntity.taskType = null
this.taskFormEntity.taskStatus = null
this.search()
} else {
ElMessage.error(res.data.message)
}
}).catch(err => {
ElMessage.error('更新任务状态失败:' + err)
})
},
getCurrentRow(row) {
this.taskId = row.taskId
}
},
}
</script>
<style scoped>
.el-pagination {
padding-left: 5px;
}
.el-row .el-button {
width: 72px;
margin-left: 0px;
margin-right: 5px;
}
.table-class {
width: 100%;
}
.el-row .el-form-item .el-select-v2 {
width: 100% !important;
}
.el-row .el-form-item .el-input-number {
width: 100% !important;
}
.el-row .el-form-item .el-button {
margin: auto;
}
</style>

214
src/layout/wmsLog.vue Normal file
View File

@ -0,0 +1,214 @@
<template>
<div style="margin-bottom: 10px">
<el-config-provider :locale="zhCn">
<el-row style="margin-top: 10px;">
<el-input v-model="queryKey" style="width: 256px; margin-right: 10px;" placeholder="请输入搜索信息" />
<el-button type="primary" @click="search()">搜索</el-button>
<el-button type="warning" @click="reset()">重置</el-button>
</el-row>
<br />
<el-table :data="wmsLogs" stripe border v-loading="loading" class="table-class" max-height="650px"
highlight-current-row @row-click="getCurrentRow" :header-cell-style="{ 'text-align': 'center' }"
:cell-style="{ 'text-align': 'center' }">
<el-table-column width="65px" fixed="left">
<template v-slot="scope">
<el-radio :label="scope.row.logId" v-model="logId">&nbsp;</el-radio>
</template>
</el-table-column>
<el-table-column prop="logId" label="请求id" fixed="left" min-width="120px" show-overflow-tooltip />
<el-table-column prop="logTitle" label="请求名称" fixed="left" min-width="120px" show-overflow-tooltip />
<el-table-column prop="logMethod" label="请求接口" fixed="left" min-width="120px" show-overflow-tooltip />
<el-table-column prop="logRequest" label="请求信息" min-width="180px" show-overflow-tooltip />
<el-table-column prop="logResponse" label="响应信息" min-width="180px" show-overflow-tooltip />
<el-table-column prop="logIp" label="请求ip" min-width="120px" />
<el-table-column prop="logTime" label="请求时间" :formatter="timeFormat" show-overflow-tooltip min-width="120px" />
<el-table-column prop="logUser" label="请求用户" min-width="120px" />
<el-table-column fixed="right" label="操作" width="120px">
<template v-slot="scope">
<el-button plain type="primary" @click="detailCurrentRowLog(scope.row)">查看详情</el-button>
</template>
</el-table-column>
</el-table>
<br />
<el-pagination v-model:current-page="currentPage" v-model:page-size="pageSize" :page-sizes="[10, 25, 50]"
:small="false" :disabled="false" :background="false" :default-page-size="10"
layout="total, sizes, prev, pager, next, jumper" :total="total" @size-change="search"
@current-change="search" />
<el-dialog v-model="dialogVisible" title="日志详情" width="40%" draggable :show-close="false">
<el-form ref="wmsLogFormRef" :model="wmsLogEntity" :label-position="labelPosition" label-width="100px"
style="max-width: 100%" :rules="rules" status-icon>
<el-row :gutter="16">
<el-col :span="12" :offset="0">
<el-form-item label="请求id" prop="logId">
<el-input v-model="wmsLogEntity.logId" readonly />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="请求名称" prop="logTitle">
<el-input v-model="wmsLogEntity.logTitle" readonly />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="16">
<el-col :span="12" :offset="0">
<el-form-item label="请求接口" prop="logMethod">
<el-input v-model="wmsLogEntity.logMethod" readonly />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="请求ip" prop="logIp">
<el-input v-model="wmsLogEntity.logIp" readonly />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="16">
<el-col :span="12" :offset="0">
<el-form-item label="请求信息" prop="logRequest">
<el-input type="textarea" :rows="2" v-model="wmsLogEntity.logRequest" placeholder=""
:maxlength="-1" :show-word-limit="false" :autosize="{ minRows: 2, maxRows: 4 }"
:formatter="(value) => jsonFormat(value)" readonly>
</el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="响应信息" prop="logResponse">
<el-input type="textarea" :rows="2" v-model="wmsLogEntity.logResponse"
:maxlength="-1" :show-word-limit="false" :autosize="{ minRows: 2, maxRows: 4 }"
:formatter="(value) => jsonFormat(value)" readonly>
</el-input>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="16">
<el-col :span="12" :offset="0">
<el-form-item label="请求时间" prop="logTime">
<el-input v-model="wmsLogEntity.logTime" readonly />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="请求用户" prop="logUser">
<el-input v-model="wmsLogEntity.logUser" readonly />
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogVisible = false">关闭</el-button>
</span>
</template>
</el-dialog>
</el-config-provider>
</div>
</template>
<script setup>
import { queryLogs } from '@/api/wmsLog.js'
import zhCn from 'element-plus/dist/locale/zh-cn.mjs'
import { ElMessage } from 'element-plus'
import { timeFormatter } from '@/utils/formatter.js'
import { ref, reactive } from 'vue'
</script>
<script>
export default {
name: 'wmsLog',
data() {
return {
wmsLogs: [],
pageInfo: {},
currentPage: 1,
pageSize: 10,
total: 0,
queryKey: '',
loading: true,
dialogVisible: false,
logId: '',
wmsLogEntity: reactive({}),
labelPosition: 'top',
wmsLogFormRef: ref(),
rules: reactive({})
}
},
mounted() {
this.search()
},
methods: {
search() {
this.loading = true
this.pageInfo.pageNum = this.currentPage
this.pageInfo.pageSize = this.pageSize
const tableRequest = {
page: this.pageInfo,
param: {
logRequest: this.queryKey.trim()
},
}
queryLogs(tableRequest).then(res => {
const tableResponse = res.data
if (tableResponse.code != 0) {
ElMessage.error(tableResponse.message)
}
this.wmsLogs = tableResponse.rows
this.total = tableResponse.total
}).catch(err => {
ElMessage.error('查询物料错误' + err.message)
})
this.loading = false
},
timeFormat: (row, column, cellValue, index) => {
return timeFormatter(cellValue)
},
reset() {
this.queryKey = ''
this.search
},
detailCurrentRowLog(row) {
this.wmsLogEntity = row
this.dialogVisible = true
},
getCurrentRow(row) {
this.logId = row.logId
},
jsonFormat(value) {
try {
return JSON.stringify(JSON.parse(value), null, 4)
} catch(e) {
return value
}
}
},
}
</script>
<style scoped>
.el-pagination {
padding-left: 5px;
}
.el-row .el-button {
width: 72px;
margin-left: 0px;
margin-right: 5px;
}
.table-class {
width: 100%;
}
.el-row .el-form-item {
width: 10% inherit;
justify-content: center;
}
.el-row .el-form-item .el-select-v2 {
width: 100% !important;
}
.el-row .el-form-item .el-input-number {
width: 100% !important;
}
.el-row .el-form-item .el-button {
margin: auto;
}
</style>

19
src/main.js Normal file
View File

@ -0,0 +1,19 @@
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import './styles/index.scss'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
import print from 'vue3-print-nb'
const app = createApp(App)
app.use(store)
app.use(router)
app.use(ElementPlus)
app.use(print)
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.component(key, component)
}
app.mount('#app')

85
src/print/printDemo.vue Normal file
View File

@ -0,0 +1,85 @@
<template>
<el-config-provider :locale="zhCn">
<el-row>
<el-button style="margin-left: 25px;" color="#626aef" v-print="'#printArea'"><el-icon>
<Printer />
</el-icon></el-button>
</el-row>
<el-container class="content">
<!-- <div style="width: 0;height: 0;overflow: hidden"> -->
<div>
<div id="printArea" class="objectDialogFlowPrint">
<div class="myPrint">
<div class="pageWarp" v-for="item in printTabs">
<el-card :body-style="{ padding: '0px', display: 'flex' }">
<qrcode-vue :value="item.name + '&' + item.quantity" :size="size" :foreground="color"
level="H" style="margin: 15px" />
<div style="padding: 14px; text-align: left;">
<span style="font-size: 27pt;font-weight: bold; margin-left: 0;">零件号{{ item.name
}}</span>
<br />
<span style="font-size: 27pt;font-weight: bold; margin-left: 0;">&#8195;{{
item.quantity }}</span>
</div>
</el-card>
</div>
</div>
</div>
</div>
</el-container>
</el-config-provider>
</template>
<script setup>
import zhCn from 'element-plus/dist/locale/zh-cn.mjs'
import { ref } from 'vue'
import QrcodeVue from 'qrcode.vue'
</script>
<script>
export default {
name: 'printDemo',
components: {
},
data() {
return {
printTabs: [
{
name: '测试物料1',
quantity: 1
},
{
name: '测试物料2',
quantity: 2
}
],
size: ref(200),
color: ref('#000000')
}
},
methods: {
}
}
</script>
<style scoped>
.content {
display: flex;
width: 100%;
}
.objectDialogFlowPrint .pageWarp {
/*这句话很重要,控制时候强制分页 https://www.w3school.com.cn/cssref/pr_page-break-after.asp*/
page-break-after: always;
height: 100%;
width: 100%;
text-align: center;
margin: 5px auto;
padding: auto;
}
.objectDialogFlowPrint .myPrint {
/* 打印的时候是否显示底色 */
print-color-adjust: exact;
}</style>

57
src/router/index.js Normal file
View File

@ -0,0 +1,57 @@
import { createRouter, createWebHashHistory } from 'vue-router'
import HomeView from '@/views/HomeView.vue'
import stock from '@/layout/stock.vue'
import login from '@/views/login.vue'
import systemCenter from'@/views/SystemCenter.vue'
const routes = [
{
path: '/home',
name: 'home',
component: HomeView,
redirect: '/stock',
children: [
{ path: '/stock', component: stock },// 库存
{ path: '/goodsIn', component: () => import('@/layout/goodsIn.vue') },// 入库
{ path: '/goodsOut', component: () => import('@/layout/goodsOut.vue') },// 出库
{ path: '/inTaskRecord', component: () => import('@/layout/inTaskRecord.vue') },// 入库记录
{ path: '/outTaskRecord', component: () => import('@/layout/outTaskRecord.vue') },// 出库记录
{ path: '/location', component: () => import('@/layout/location.vue') },// 库位
{ path: '/goods', component: () => import('@/layout/goods.vue') },// 物料
{ path: '/standSettings', component: () => import('@/layout/standSettings.vue') },// 站台(库口)设置
{ path: '/config', component: () => import('@/layout/config.vue') },// 系统配置
{ path: '/taskMonitor', component: () => import('@/layout/taskMonitor.vue') },// 任务监控
{ path: '/inventory', component: () => import('@/layout/inventory.vue') },// 盘点
{ path: '/inventoryRecord', component: () => import('@/layout/inventoryRecord.vue') },// 盘点
{ path: '/wmsLog', component: () => import('@/layout/wmsLog.vue') },// 日志
{ path: '/scanConfirm', component: () => import('@/layout/scanConfirm.vue') },// 扫码确认
]
},
{
path: '/',
name: 'login',
component: login
},
{
path: '/systemCenter',
name: 'systemCenter',
component: systemCenter
}
]
const router = createRouter({
base: '/',
history: createWebHashHistory(),
routes
})
// 挂载路由导航守卫
router.beforeEach((to, from, next) => {
if (to.path === '/') return next()
// 获取token
const user = sessionStorage.getItem('user')
if (!user) return next('/')
next()
})
export default router

82
src/store/index.js Normal file
View File

@ -0,0 +1,82 @@
import { createStore } from 'vuex'
export default createStore({
state: {
stateTagsList: [{
id: '21',
labelName: '库存',
path: '/stock'
}],
user: {},
menuList: [],
token: '',
verifier: ''
},
getters: {
getUserName(state) {
return state.user.userName
},
getMenuList(state) {
return state.menuList
},
getToken(state) {
return state.token
},
getVerify(state) {
return state.verifier
},
},
mutations: {
/**
* 选择tag标签
*/
mutationSelectTags(state, data) {
let result = false
for (let i = 0; i < state.stateTagsList.length; i++) {
if (state.stateTagsList[i].path == data.path) {
return result = true
}
}
result === false ? state.stateTagsList.push(data) : ''
},
/**
* 关闭tag标签
*/
mutationCloseTag(state, data) {
let result = state.stateTagsList.findIndex(item => item.path === data.path)
state.stateTagsList.splice(result, 1)
},
/**
* 存储用户信息
*/
mutationUser(state, data) {
state.user = data
sessionStorage.setItem("user", state.user)
},
/**
* 存储菜单信息
*/
mutationMenu(state, data) {
state.menuList = data
sessionStorage.setItem("menuList", state.menuList)
},
/**
* 存储认证
*/
mutationAddVerify(state, data) {
state.verifier = data
},
mutationClearVerify(state) {
state.verifier = ''
},
},
actions: {
},
modules: {
}
})

9
src/styles/index.scss Normal file
View File

@ -0,0 +1,9 @@
/* 只需要重写你需要的即可 */
@forward 'element-plus/theme-chalk/src/common/var.scss' with ($colors: ('primary': ('base': green,
),
),
);
// 如果只是按需导入则可以忽略以下内容
// 如果你想导入所有样式:
// @use "element-plus/theme-chalk/src/index.scss" as *;

23
src/utils/dateUtils.js Normal file
View File

@ -0,0 +1,23 @@
function revertSeconds(originSeconds) {
const dayRule = 60*60*24
const hourRule = 60*60
const days = Math.floor(originSeconds/dayRule)
const hours = Math.floor((originSeconds%dayRule)/hourRule)
const minutes = Math.floor((originSeconds%hourRule)/60)
const seconds = originSeconds%60
var dueTime = ''
if (days > 0) {
dueTime = dueTime.concat(days).concat('天')
}
if (hours > 0) {
dueTime = dueTime.concat(hours).concat('小时')
}
if (minutes > 0) {
dueTime = dueTime.concat(minutes).concat('分')
}
return dueTime.concat(seconds).concat('秒')
}
export {
revertSeconds
}

132
src/utils/formatter.js Normal file
View File

@ -0,0 +1,132 @@
import moment from "moment";
import { revertSeconds } from '@/utils/dateUtils'
function timeFormatter(date) {
if (date === null || date === undefined) {
return ''
}
return moment(date).format('yyyy-MM-DD HH:mm:ss');
}
function dateFormatter(date) {
if (date === null || date === undefined) {
return ''
}
return moment(date).format('yyyy-MM-DD');
}
function dueFormatter(date) {
if (date === null || date === undefined) {
return ''
}
return revertSeconds(moment(new Date().getTime()).diff(moment(date), 'seconds'));
}
function taskStatusFormatter(value) {
switch (value) {
case 0:
return '任务新建,待下发'
case 1:
return '任务已下发'
case 2:
return '任务开始执行'
case 3:
return '出库完成'
case 4:
return '输送线转运中'
case 5:
return '到达拣选站台'
case 6:
return '正在拣货'
case 7:
return '正在回库'
case 8:
return '盘点中'
case 100:
return '任务完成'
case 998:
return '任务取消'
case 999:
return '任务异常'
default:
return '未完成'
}
}
function locationFormatter(locationId) {
if (locationId === null || locationId == undefined) {
return ''
}
return locationId.substr(0, 2) + '排' + locationId.substr(2, 2) + '列' + locationId.substr(4, 2) + '层' + locationId.substr(7, 3) + '深度'
}
function kateTaskStatusFormatter(value) {
switch (value) {
case 0:
return '待下发'
case 1:
return '已下发'
case 2:
return '执行中'
case 3:
return '正在拣货'
case 5:
return '任务完成'
default:
return '异常状态'
}
}
function pickingStatusFormatter(value) {
switch (value) {
case 0:
return '待拣货'
case 1:
return '正在拣货'
case 2:
return '拣货完成'
default:
return '未拣货'
}
}
function vehicleStatusFormatter(value) {
switch (value) {
case 1:
return '入库中'
case 2:
return '在库中'
case 3:
return '出库中'
default:
return '异常状态'
}
}
function kateTaskTypeFormatter(value) {
switch (value) {
case 1:
return '配件'
case 2:
return '合件'
case 3:
return '紧急'
case 4:
return '原包装'
default:
return '任务类型异常'
}
}
export {
timeFormatter,
dateFormatter,
taskStatusFormatter,
locationFormatter,
kateTaskStatusFormatter,
pickingStatusFormatter,
vehicleStatusFormatter,
kateTaskTypeFormatter,
dueFormatter
}

9
src/utils/loading.js Normal file
View File

@ -0,0 +1,9 @@
import { ElLoading } from 'element-plus'
const loading = ElLoading.service({
lock: true,
text: 'Loading',
background: 'rgba(0, 0, 0, 0.7)',
})
export default loading

146
src/utils/request.js Normal file
View File

@ -0,0 +1,146 @@
import axios from 'axios'
import { ElNotification, ElMessageBox, ElMessage, ElLoading } from 'element-plus'
import { getToken } from '@/utils/auth'
import errorCode from '@/utils/errorCode'
import { tansParams, blobValidate } from '@/utils/ruoyi'
import cache from '@/plugins/cache'
import { saveAs } from 'file-saver'
import useUserStore from '@/store/modules/user'
let downloadLoadingInstance;
// 是否显示重新登录
export let isRelogin = { show: false };
axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8'
// 创建axios实例
const service = axios.create({
// axios中请求配置有baseURL选项表示请求URL公共部分
baseURL: import.meta.env.VITE_APP_BASE_API,
// 超时
timeout: 10000
})
// request拦截器
service.interceptors.request.use(config => {
// 是否需要设置 token
const isToken = (config.headers || {}).isToken === false
// 是否需要防止数据重复提交
const isRepeatSubmit = (config.headers || {}).repeatSubmit === false
if (getToken() && !isToken) {
config.headers['Authorization'] = 'Bearer ' + getToken() // 让每个请求携带自定义token 请根据实际情况自行修改
}
// get请求映射params参数
if (config.method === 'get' && config.params) {
let url = config.url + '?' + tansParams(config.params);
url = url.slice(0, -1);
config.params = {};
config.url = url;
}
if (!isRepeatSubmit && (config.method === 'post' || config.method === 'put')) {
const requestObj = {
url: config.url,
data: typeof config.data === 'object' ? JSON.stringify(config.data) : config.data,
time: new Date().getTime()
}
const sessionObj = cache.session.getJSON('sessionObj')
if (sessionObj === undefined || sessionObj === null || sessionObj === '') {
cache.session.setJSON('sessionObj', requestObj)
} else {
const s_url = sessionObj.url; // 请求地址
const s_data = sessionObj.data; // 请求数据
const s_time = sessionObj.time; // 请求时间
const interval = 1000; // 间隔时间(ms),小于此时间视为重复提交
if (s_data === requestObj.data && requestObj.time - s_time < interval && s_url === requestObj.url) {
const message = '数据正在处理,请勿重复提交';
console.warn(`[${s_url}]: ` + message)
return Promise.reject(new Error(message))
} else {
cache.session.setJSON('sessionObj', requestObj)
}
}
}
return config
}, error => {
console.log(error)
Promise.reject(error)
})
// 响应拦截器
service.interceptors.response.use(res => {
// 未设置状态码则默认成功状态
const code = res.data.code || 200;
// 获取错误信息
const msg = errorCode[code] || res.data.msg || errorCode['default']
// 二进制数据则直接返回
if (res.request.responseType === 'blob' || res.request.responseType === 'arraybuffer') {
return res.data
}
if (code === 401) {
if (!isRelogin.show) {
isRelogin.show = true;
ElMessageBox.confirm('登录状态已过期,您可以继续留在该页面,或者重新登录', '系统提示', { confirmButtonText: '重新登录', cancelButtonText: '取消', type: 'warning' }).then(() => {
isRelogin.show = false;
useUserStore().logOut().then(() => {
location.href = '/index';
})
}).catch(() => {
isRelogin.show = false;
});
}
return Promise.reject('无效的会话,或者会话已过期,请重新登录。')
} else if (code === 500) {
ElMessage({ message: msg, type: 'error' })
return Promise.reject(new Error(msg))
} else if (code === 601) {
ElMessage({ message: msg, type: 'warning' })
return Promise.reject(new Error(msg))
} else if (code !== 200) {
ElNotification.error({ title: msg })
return Promise.reject('error')
} else {
return Promise.resolve(res.data)
}
},
error => {
console.log('err' + error)
let { message } = error;
if (message == "Network Error") {
message = "后端接口连接异常";
} else if (message.includes("timeout")) {
message = "系统接口请求超时";
} else if (message.includes("Request failed with status code")) {
message = "系统接口" + message.substr(message.length - 3) + "异常";
}
ElMessage({ message: message, type: 'error', duration: 5 * 1000 })
return Promise.reject(error)
}
)
// 通用下载方法
export function download(url, params, filename, config) {
downloadLoadingInstance = ElLoading.service({ text: "正在下载数据,请稍候", background: "rgba(0, 0, 0, 0.7)", })
return service.post(url, params, {
transformRequest: [(params) => { return tansParams(params) }],
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
responseType: 'blob',
...config
}).then(async (data) => {
const isLogin = await blobValidate(data);
if (isLogin) {
const blob = new Blob([data])
saveAs(blob, filename)
} else {
const resText = await data.text();
const rspObj = JSON.parse(resText);
const errMsg = errorCode[rspObj.code] || rspObj.msg || errorCode['default']
ElMessage.error(errMsg);
}
downloadLoadingInstance.close();
}).catch((r) => {
console.error(r)
ElMessage.error('下载文件出现错误,请联系管理员!')
downloadLoadingInstance.close();
})
}
export default service

7
src/utils/stringUtils.js Normal file
View File

@ -0,0 +1,7 @@
import moment from "moment";
function genTaskId(code) {
return code + moment(new Date()).format("YYYYMMDDHHmmssSSS");
}
export {
genTaskId
}

165
src/views/HomeView.vue Normal file
View File

@ -0,0 +1,165 @@
<template>
<el-container class="wms-home">
<el-header class="header">
<div class="icon-img">
<el-image :src="icon_img_url" :fit="'fill'" />
</div>
<div class="title">
<span>WMS仓库管理系统</span>
</div>
<div class="user">{{ userName }}</div>
</el-header>
<el-container class="content">
<el-aside class="aside">
<el-scrollbar style="max-height: 95%;">
<sideMenu></sideMenu>
</el-scrollbar>
<div :class="$route.path === '/location' ? 'locationsSel' : 'locations'" @click="openLocations">
<el-icon>
<View />
</el-icon>
<span>库位监控</span>
</div>
</el-aside>
<el-container>
<el-header class="tag">
<appTag></appTag>
</el-header>
<el-main>
<!-- 路由占位符 -->
<el-scrollbar>
<router-view v-slot="{ Component }">
<!-- // TODO keepAlive -->
<keep-alive>
<component :is="Component" />
</keep-alive>
</router-view>
</el-scrollbar>
</el-main>
<el-footer class="footer">© 1970-2024 江苏菲达宝开电气股份有限公司</el-footer>
</el-container>
</el-container>
</el-container>
</template>
<script setup>
// @ is an alias to /src
import sideMenu from '@/components/sideMenu.vue'
import appTag from '@/components/appTag.vue'
const icon_img_url = require('@/assets/fdbk_log.png')
</script>
<script>
export default {
name: 'HomeView',
components: {
sideMenu,
appTag
},
computed: {
userName() {
return this.$store.state.user.userName
}
},
methods: {
openLocations() {
this.$store.commit('mutationSelectTags', {
id: '25',
labelName: '库位监控',
path: '/location'
})
this.$router.push('/location')
}
}
}
</script>
<style scoped>
.wms-home {
height: 100%;
background-color: #CCCCCC;
}
.content {
height: 95%;
background-color: #FFFFFF;
}
.header {
/* background-color: #66CCFF; */
background-color: #D9E3EE;
display: flex;
align-items: center;
height: 5%;
/* font-size: 15px; */
}
.aside {
background-color: #fff;
height: 100%;
width: 10%;
border-right: 1px solid #66CCFF;
}
.footer {
background-color: #F0FFFF;
display: flex;
justify-content: center;
flex-direction: column;
height: 3%;
}
.tag {
padding-left: 1px;
display: flex;
align-items: center;
height: 30px;
border-bottom: solid 1px;
}
.icon-img {
height: 100%;
width: 3%;
}
.icon-img .el-image {
height: 100%;
width: 100%;
}
.title {
margin-left: 2px;
}
.user {
margin-left: auto;
}
.locations {
padding: 5px;
display: flex;
justify-content: center;
align-items: center;
height: 3.8%;
border-top: solid 1px;
cursor: pointer;
color: #5A9CF8;
}
.locationsSel {
padding: 5px;
display: flex;
justify-content: center;
align-items: center;
height: 3.8%;
border-top: solid 1px;
cursor: pointer;
background-color: #5A9CF8;
color: #000;
}
.locations:hover {
background-color: #5A9CF8;
color: #000;
}
</style>

112
src/views/SystemCenter.vue Normal file
View File

@ -0,0 +1,112 @@
<template>
<body id="login-page">
<el-form class="login-container" label-position="left" label-width="0px">
<h3 class="login_title">请选择系统</h3>
<el-form-item style="width: 100%">
<el-button color="#87CEFA" style="width: 100%; border: none" @click="loginToWms">WMS系统</el-button>
</el-form-item>
<!-- <el-form-item style="width: 100%">
<el-button color="#87CEEB" style="width: 100%; border: none" @click="loginToPda">手持入库系统</el-button>
</el-form-item>
<el-form-item style="width: 100%">
<el-button color="#AFEEEE" style="width: 100%; border: none" @click="loginToSideScan">线边扫码系统</el-button>
</el-form-item>
<el-form-item style="width: 100%">
<el-button color="#ADD8E6" style="width: 100%; border: none" @click="loginToWcs">WCS系统</el-button>
</el-form-item>
<el-form-item style="width: 100%">
<el-button color="#B0E0E6" style="width: 100%; border: none" @click="loginToMonitor">监控系统</el-button>
</el-form-item> -->
</el-form>
</body>
</template>
<script setup>
import { getUser } from '@/api/login.js'
import { ElMessage, ElLoading } from 'element-plus'
import { onMounted } from 'vue';
import store from '@/store'
import router from '@/router'
const user = store.getters.getUserNam//
const token = store.getters.getToken//
// WMS
const loginToWms = () => {
router.replace({ path: '/home' })
}
const loginToPda = () => {
router.replace({ path: '/goodsInPda' })
}
const loginToSideScan = () => {
router.replace({ path: '/sideScan' })
}
// WCS
const loginToWcs = () => {
const wcsUrl = `https://cxlasrs.ecorp.cat.com/wcs/#/login?user=user&pwd=user`
window.location.href = wcsUrl//
}
const loginToMonitor = () => {
const mpnitorUrl = `https://cxlasrs.ecorp.cat.com?user=${user}&token=${token}`
window.location.href = mpnitorUrl//
}
onMounted(() => {
if (router.currentRoute.value.query.code != undefined) {
console.log(router.currentRoute.value.query.code)
const loading = ElLoading.service({
lock: true,
text: 'Loading',
background: 'rgba(0, 0, 0, 0.7)',
})
const codeInfo = {
code: router.currentRoute.value.query.code,
state: router.currentRoute.value.query.state
}
getUser(codeInfo).then(res => {
loading.close()
if (res.data.code == 0) {
store.commit('mutationUser', res.data.returnData.user)//
store.commit('mutationMenu', res.data.returnData.menuList)//
router.replace({ path: '/' })
} else {
ElMessage.error(res.data.message)
}
}).catch(err => {
console.log(err)
loading.close()
ElMessage.error('登录失败!')
})
}
})
</script>
<style scoped>
#login-page {
/* background: url("../assets/img/bg.jpg") no-repeat; */
background-position: center;
height: 100%;
width: 100%;
background-size: cover;
position: fixed;
}
body {
margin: 0px;
}
.login-container {
border-radius: 15px;
background-clip: padding-box;
margin: 90px auto;
width: 350px;
padding: 35px 35px 15px 35px;
background: #fff;
border: 1px solid #eaeaea;
box-shadow: 0 0 25px #cac6c6;
}
.login_title {
margin: 0px auto 40px auto;
text-align: center;
color: #505458;
}
</style>

83
src/views/login.vue Normal file
View File

@ -0,0 +1,83 @@
<template>
<body id="login-page">
<el-form class="login-container" :model="loginForm" label-position="left" label-width="0px">
<h3 class="login_title">系统登录</h3>
<el-form-item>
<el-input type="text" v-model="loginForm.loginAccount" auto-complete="off" placeholder="账号"></el-input>
</el-form-item>
<el-form-item>
<el-input type="password" v-model="loginForm.loginPassword" auto-complete="off" placeholder="密码"></el-input>
</el-form-item>
<el-form-item style="width: 100%">
<el-button type="primary" style="width: 100%; border: none" @click="login">登录</el-button>
</el-form-item>
</el-form>
</body>
</template>
<script setup>
import { loginWithAuth } from '@/api/login.js'
import { ElMessage, ElLoading } from 'element-plus'
import { reactive } from 'vue';
import store from '@/store'
import router from '@/router'
const loginForm = reactive({
loginAccount: "",
loginPassword: "",
})
//
const login = () => {
const loading = ElLoading.service({
lock: true,
text: 'Loading',
background: 'rgba(0, 0, 0, 0.7)',
})
loginWithAuth(loginForm).then(res => {
loading.close()
if (res.data.code == 0) {
store.commit('mutationUser', res.data.returnData.user)//
store.commit('mutationMenu', res.data.returnData.menuList)//
// router.replace({ path: '/' })//
router.replace({ path: '/systemCenter' })//
} else {
ElMessage.error(res.data.message)
}
}).catch(err => {
console.log(err)
loading.close()
ElMessage.error('登录失败!')
})
}
</script>
<style scoped>
#login-page {
/* background: url("../assets/img/bg.jpg") no-repeat; */
background-position: center;
height: 100%;
width: 100%;
background-size: cover;
position: fixed;
}
body {
margin: 0px;
}
.login-container {
border-radius: 15px;
background-clip: padding-box;
margin: 90px auto;
width: 350px;
padding: 35px 35px 15px 35px;
background: #fff;
border: 1px solid #eaeaea;
box-shadow: 0 0 25px #cac6c6;
}
.login_title {
margin: 0px auto 40px auto;
text-align: center;
color: #505458;
}
</style>

20
vue.config.js Normal file
View File

@ -0,0 +1,20 @@
const { defineConfig } = require('@vue/cli-service')
const NodePolyfillPlugin = require('node-polyfill-webpack-plugin')
module.exports = defineConfig({
transpileDependencies: true,
configureWebpack: {
plugins: [new NodePolyfillPlugin()]
},
devServer: {
port: 12306, // 端口
proxy: {
'/authorize': { // 若请求的前缀不是这个'/wms',那请求就不会走代理服务器
target: "https://login.microsoftonline.com/caterpillar.onmicrosoft.com/", //这里写路径
pathRewrite: { '^/authorize': '' }, //将所有含/wms路径的去掉/wms转发给服务器
ws: true, //用于支持websocket
changeOrigin: true //用于控制请求头中的host值
},
}
},
})