This commit is contained in:
朱菊兰 2023-01-03 09:33:30 +08:00
commit bd4d0e897b
190 changed files with 48628 additions and 0 deletions

3
.browserslistrc Normal file
View File

@ -0,0 +1,3 @@
> 1%
last 2 versions
not dead

7
.env.development Normal file
View File

@ -0,0 +1,7 @@
# just a flag
ENV = 'development'
# base api
# 这里修改成api服务器地址
VUE_APP_BASE_API = '/api'
VUE_APP_VIEW_PIC = 'http://tft.mes.picaiba.com/api/common/attachment/downloadFile?type=0&attachmentId='

7
.env.production Normal file
View File

@ -0,0 +1,7 @@
# just a flag
ENV = 'production'
# base api
# 这里修改成api服务器地址
VUE_APP_BASE_API = '/api'
VUE_APP_VIEW_PIC = 'http://tft.mes.picaiba.com/api/common/attachment/downloadFile?type=0&attachmentId='

18
.eslintrc.js Normal file
View File

@ -0,0 +1,18 @@
module.exports = {
root: true,
env: {
node: true
},
extends: [
'plugin:vue/essential',
'eslint:recommended',
'plugin:prettier/recommended'
],
parserOptions: {
parser: '@babel/eslint-parser'
},
rules: {
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off'
}
}

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?

5
.prettierrc.json Normal file
View File

@ -0,0 +1,5 @@
{
"semi": false,
"singleQuote": true,
"trailingComma": "none"
}

38
README.en.md Normal file
View File

@ -0,0 +1,38 @@
# tft-mes
#### Description
1tft项目的框架搭建
2封装UI组件
3测试封装的UI组件
#### Software Architecture
Software architecture description
#### Installation
1. xxxx
2. xxxx
3. xxxx
#### Instructions
1. xxxx
2. xxxx
3. xxxx
#### Contribution
1. Fork the repository
2. Create Feat_xxx branch
3. Commit your code
4. Create Pull Request
#### Gitee Feature
1. You can use Readme\_XXX.md to support different languages, such as Readme\_en.md, Readme\_zh.md
2. Gitee blog [blog.gitee.com](https://blog.gitee.com)
3. Explore open source project [https://gitee.com/explore](https://gitee.com/explore)
4. The most valuable open source project [GVP](https://gitee.com/gvp)
5. The manual of Gitee [https://gitee.com/help](https://gitee.com/help)
6. The most popular members [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/)

24
README.md Normal file
View File

@ -0,0 +1,24 @@
# tft-mes
## 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/).

3
babel.config.js Normal file
View File

@ -0,0 +1,3 @@
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"
]
}
}

23032
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

40
package.json Normal file
View File

@ -0,0 +1,40 @@
{
"name": "tft-mes",
"version": "0.1.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint"
},
"dependencies": {
"axios": "^0.27.2",
"core-js": "^3.8.3",
"echarts": "^5.4.0",
"element-ui": "^2.15.8",
"js-cookie": "^3.0.1",
"moment": "^2.29.4",
"nprogress": "^0.2.0",
"vue": "^2.6.14",
"vue-router": "^3.5.1",
"vuex": "^3.6.2"
},
"devDependencies": {
"@babel/core": "^7.12.16",
"@babel/eslint-parser": "^7.12.16",
"@vue/cli-plugin-babel": "~5.0.0",
"@vue/cli-plugin-eslint": "~5.0.0",
"@vue/cli-plugin-router": "~5.0.0",
"@vue/cli-plugin-vuex": "~5.0.0",
"@vue/cli-service": "~5.0.0",
"eslint": "^7.32.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^4.0.0",
"eslint-plugin-vue": "^8.0.3",
"prettier": "^2.4.1",
"sass": "^1.32.7",
"sass-loader": "^12.0.0",
"svg-sprite-loader": "^6.0.11",
"vue-template-compiler": "^2.6.14"
}
}

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 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>

14
src/App.vue Normal file
View File

@ -0,0 +1,14 @@
<template>
<div id="app">
<router-view />
</div>
</template>
<style lang="scss">
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
background: #f2f4f9;
}
</style>

61
src/api/app.js Normal file
View File

@ -0,0 +1,61 @@
import request from './request'
export function getPublicList() {
return request({
url: '/common/getList',
method: 'post'
})
}
export function getTreeData() {
return request({
url: '/common/getTreeData',
method: 'post'
})
}
export function updateTreeData(data) {
return request({
url: '/common/updateEqSeq',
method: 'post',
data
})
}
export function downLoadFile(params) {
return request({
url: '/common/attachment/downloadFile',
method: 'get',
responseType: 'blob',
params,
timeout: 60000
})
}
export function getAlarmData(data) {
return request({
url: '/common/getAlarmData',
method: 'post',
data
})
}
export function getProductData(data) {
return request({
url: '/common/getProductData',
method: 'post',
data
})
}
export function getMaterialData(data) {
return request({
url: '/common/getMaterialData',
method: 'post',
data
})
}
export const commonUploadPath = '/api/common/attachment/uploadFileFormData'
export const commonUploadPathTwo = '/api/common/attachment/uploadFile'
export const commonDownLoadPath = '/api/common/attachment/downloadFile'

223
src/api/basicConfig.js Normal file
View File

@ -0,0 +1,223 @@
import request from './request'
// 账号配置
export function getAccountPage(data) {
return request({
url: '/basic/account/page',
method: 'post',
data
})
}
export function addAccount(data) {
return request({
url: '/basic/account/add',
method: 'post',
data
})
}
export function getAccount(data) {
return request({
url: '/basic/account/get',
method: 'post',
data
})
}
export function accountUpdate(data) {
return request({
url: '/basic/account/update',
method: 'post',
data
})
}
// 备件配置
export function getSparePartsPage(data) {
return request({
url: '/basic/sparePartsConfiguration/page',
method: 'post',
data
})
}
export function addSpareParts(data) {
return request({
url: '/basic/sparePartsConfiguration/add',
method: 'post',
data
})
}
export function getSpareParts(data) {
return request({
url: '/basic/sparePartsConfiguration/get',
method: 'post',
data
})
}
export function sparePartsUpdate(data) {
return request({
url: '/basic/sparePartsConfiguration/update',
method: 'post',
data
})
}
export function getNameList(data) {
return request({
url: '/basic/sparePartsConfiguration/getNameList',
method: 'post',
data
})
}
export function getModelList(data) {
return request({
url: '/basic/sparePartsConfiguration/getModelList',
method: 'post',
data
})
}
// 耗材配置
export function getConsumablePage(data) {
return request({
url: '/basic/consumableConfiguration/page',
method: 'post',
data
})
}
export function addConsumable(data) {
return request({
url: '/basic/consumableConfiguration/add',
method: 'post',
data
})
}
export function getConsumable(data) {
return request({
url: '/basic/consumableConfiguration/get',
method: 'post',
data
})
}
export function consumableUpdate(data) {
return request({
url: '/basic/consumableConfiguration/update',
method: 'post',
data
})
}
export function materialList(data) {
return request({
url: '/basic/consumableConfiguration/materialAndUnit',
method: 'post',
data
})
}
export function getSpecList(data) {
return request({
url: '/basic/consumableConfiguration/specList',
method: 'post',
data
})
}
// 线边库配置
export function getSideLibraryPage(data) {
return request({
url: '/basic/sideLibraryConfiguration/page',
method: 'post',
data
})
}
export function addSideLibrary(data) {
return request({
url: '/basic/sideLibraryConfiguration/add',
method: 'post',
data
})
}
export function getSideLibrary(data) {
return request({
url: '/basic/sideLibraryConfiguration/get',
method: 'post',
data
})
}
export function sideLibraryUpdate(data) {
return request({
url: '/basic/sideLibraryConfiguration/update',
method: 'post',
data
})
}
export function sideLibraryList(data) {
return request({
url: '/basic/sideLibraryConfiguration/list',
method: 'post',
data
})
}
// 设备配置
export function getEquipmentPage(data) {
return request({
url: '/basic/equipmentConfiguration/page',
method: 'post',
data
})
}
export function addEquipment(data) {
return request({
url: '/basic/equipmentConfiguration/add',
method: 'post',
data
})
}
export function getEquipment(data) {
return request({
url: '/basic/equipmentConfiguration/get',
method: 'post',
data
})
}
export function equipmentUpdate(data) {
return request({
url: '/basic/equipmentConfiguration/update',
method: 'post',
data
})
}
export function getUnitList(val) {
return request({
url: '/basic/equipmentConfiguration/getUnitList',
method: 'post',
formState: true,
data: val
})
}
export function getEqCode(data) {
return request({
url: '/basic/equipmentConfiguration/getEqCode',
method: 'post',
data
})
}

View File

@ -0,0 +1,95 @@
import request from './request'
// 出入库管理
export function getMaterialManagePage(data) {
return request({
url: '/material/MaterialManage/selectAll',
method: 'post',
data
})
}
export function getMaterialDetail(data) {
return request({
url: '/material/MaterialManage/detail',
method: 'post',
data
})
}
export function getOutInRecordRecord(data) {
return request({
url: '/material/OutInRecord/record',
method: 'post',
data
})
}
export function outInRecordRecordExport(data) {
return request({
url: '/material/MaterialManage/export',
method: 'post',
responseType: 'blob',
data,
timeout: 60000
})
}
export function getMaterialManageByid(data) {
return request({
url: '/material/MaterialManage/updateGet',
method: 'post',
data
})
}
export function materialManageUpdate(data) {
return request({
url: '/material/MaterialManage/update',
method: 'post',
data
})
}
export function materialManageInsert(data) {
return request({
url: '/material/MaterialManage/insert',
method: 'post',
data
})
}
export function materialManageDelete(data) {
return request({
url: '/material/MaterialManage/delete',
method: 'post',
data
})
}
export function setAlarmValueGet(data) {
return request({
url: '/material/MaterialManage/SetAlarmValueGet',
method: 'post',
data
})
}
export function setAlarmValue(data) {
return request({
url: '/material/MaterialManage/SetAlarmValue',
method: 'post',
data
})
}
// 出入库记录
// 在线耗材跟踪
export function onlineMaterialTrackSelect(data) {
return request({
url: '/material/MaterialManage/onlineMaterialTrackSelect',
method: 'post',
data
})
}

255
src/api/deviceManagement.js Normal file
View File

@ -0,0 +1,255 @@
import request from './request'
// 绩效分析
// 设备OEE
export function performanceAnalysisGet(data) {
return request({
url: '/equipment/PerformanceAnalysis/get',
method: 'post',
data
})
}
export function performanceAnalysisPlan(data) {
return request({
url: '/equipment/PerformanceAnalysis/getPlan',
method: 'post',
data
})
}
// 托盘指标分析
export function palletIndicatorAnalysisPage(data) {
return request({
url: '/equipment/PalletIndicatorAnalysis/page',
method: 'post',
data
})
}
// 托盘指标分析
export function palletIndicatorAnalysisType(data) {
return request({
url: '/equipment/PalletIndicatorAnalysis/getType',
method: 'post',
data
})
}
// 备品备件
export function getSparePartStockPage(data) {
return request({
url: '/equipment/SparePartStock/page',
method: 'post',
data
})
}
export function sparePartStockInStock(data) {
return request({
url: '/equipment/SparePartStock/inStock',
method: 'post',
data
})
}
export function getSparePartStock(data) {
return request({
url: '/equipment/SparePartStock/get',
method: 'post',
data
})
}
export function sparePartStockUpdate(data) {
return request({
url: '/equipment/SparePartStock/update',
method: 'post',
data
})
}
export function sparePartStockOutStock(data) {
return request({
url: '/equipment/SparePartStock/outStock',
method: 'post',
data
})
}
export function getSparePartStockDetail(data) {
return request({
url: '/equipment/SparePartStock/stockDetail',
method: 'post',
data
})
}
export function getBatchList(data) {
return request({
url: '/equipment/SparePartStock/getBatchList',
method: 'post',
data
})
}
export function sparePartStockExport(data) {
return request({
url: '/equipment/SparePartStock/export',
method: 'post',
responseType: 'blob',
data,
timeout: 60000
})
}
export function getRecordPage(data) {
return request({
url: '/equipment/SparePartStock/PageHis',
method: 'post',
data
})
}
export function exportHis(data) {
return request({
url: '/equipment/SparePartStock/exportHis',
method: 'post',
responseType: 'blob',
data,
timeout: 60000
})
}
// 维护管理(计划维护)
export function getMaintainManagePage(data) {
return request({
url: '/equipment/MaintainManage/pageSelect',
method: 'post',
data
})
}
export function getMaintainManage(data) {
return request({
url: '/equipment/MaintainManage/unDistributeGet',
method: 'post',
data
})
}
export function maintainManageAdd(data) {
return request({
url: '/equipment/MaintainManage/create',
method: 'post',
data
})
}
export function maintainManageUUnDistribute(data) {
return request({
url: '/equipment/MaintainManage/unDistribute',
method: 'post',
data
})
}
export function maintainManageUCompleted(data) {
return request({
url: '/equipment/MaintainManage/completedDistribute',
method: 'post',
data
})
}
export function maintainManageDispatch(data) {
return request({
url: '/equipment/MaintainManage/dispatch',
method: 'post',
data
})
}
export function maintainManageConfirm(data) {
return request({
url: '/equipment/MaintainManage/confirm',
method: 'post',
data
})
}
export function maintainManageDelete(data) {
return request({
url: '/equipment/MaintainManage/PlainMaintainDelete',
method: 'post',
data
})
}
export function maintainManageExport(data) {
return request({
url: '/equipment/MaintainManage/PlanMaintainExport',
method: 'post',
responseType: 'blob',
data,
timeout: 60000
})
}
// 维护管理(自主维护)
export function getAutoMaintainPage(data) {
return request({
url: '/equipment/MaintainManage/AutoMaintainPageSelect',
method: 'post',
data
})
}
export function autoMaintainAdd(data) {
return request({
url: '/equipment/MaintainManage/AutoMaintainAdd',
method: 'post',
data
})
}
export function autoMaintainUpdate(data) {
return request({
url: '/equipment/MaintainManage/AutoMaintainModify',
method: 'post',
data
})
}
export function autoMaintainDelete(data) {
return request({
url: '/equipment/MaintainManage/AUtoMaintainDelete',
method: 'post',
data
})
}
export function autoMaintainExport(data) {
return request({
url: '/equipment/MaintainManage/AutoMaintainExport',
method: 'post',
responseType: 'blob',
data,
timeout: 60000
})
}
// 实时报警查询
export function nowAlarmMessage(data) {
return request({
url: '/equipment/AlarmMessage/nowAlertSelect',
method: 'post',
data
})
}
// 历史报警查询
export function historyAlarmMessage(data) {
return request({
url: '/equipment/AlarmMessage/historyAlertSelect',
method: 'post',
data
})
}

View File

@ -0,0 +1,68 @@
import request from './request'
// 工艺监测
export function getParamList(data) {
return request({
url: '/process/processMonitor/paramList',
method: 'post',
data
})
}
export function getPageReal(data) {
return request({
url: '/process/processMonitor/pageReal',
method: 'post',
data
})
}
export function getPageHistory(data) {
return request({
url: '/process/processMonitor/history',
method: 'post',
data
})
}
// 异常警示
export function getAlarmReal(data) {
return request({
url: '/process/alram/pageReal',
method: 'post',
data
})
}
export function getAlarmHis(data) {
return request({
url: '/process/alram/pageHis',
method: 'post',
data
})
}
// 工艺配置
export function getParamSet(data) {
return request({
url: '/process/paramSet/page',
method: 'post',
data
})
}
export function getParamSetByid(data) {
return request({
url: '/process/paramSet/get',
method: 'post',
data
})
}
export function updateParam(data) {
return request({
url: '/process/paramSet/update',
method: 'post',
data
})
}

View File

@ -0,0 +1,212 @@
import request from './request'
// 工单进度
export function getProcessInfo(data) {
return request({
url: '/order/workOrderProcess/getProcessInfo',
method: 'post',
data
})
}
export function lineGlassCount(data) {
return request({
url: '/order/workOrderProcess/lineGlassCount',
method: 'post',
data
})
}
export function grindGlassCount(data) {
return request({
url: '/order/workOrderProcess/grindGlassCount',
method: 'post',
data
})
}
export function finalClassCount(data) {
return request({
url: '/order/workOrderProcess/FinalClassCount',
method: 'post',
data
})
}
// 工单管理
export function getWorkOrderPage(data) {
return request({
url: '/order/workOrder/page',
method: 'post',
data
})
}
export function workOrderAdd(data) {
return request({
url: '/order/workOrder/add',
method: 'post',
data
})
}
export function workOrderDelete(data) {
return request({
url: '/order/workOrder/delete',
method: 'post',
data
})
}
export function workOrderUpdate(data) {
return request({
url: '/order/workOrder/update',
method: 'post',
data
})
}
export function getWorkOrder(data) {
return request({
url: '/order/workOrder/get',
method: 'post',
data
})
}
export function workOrderRelease(data) {
return request({
url: '/order/workOrder/release',
method: 'post',
data
})
}
export function workOrderFinish(data) {
return request({
url: '/order/workOrder/finish',
method: 'post',
data
})
}
export function workOrderImportTemplate(data) {
return request({
url: '/order/workOrder/importTemplate',
method: 'post',
data
})
}
export const uploadPath = '/api/order/workOrder/import'
// 生产报表
export function pageThickness(data) {
return request({
url: '/order/proReport/pageThickness',
method: 'post',
data
})
}
export function proReport(data) {
return request({
url: '/order/proReport/genTable',
method: 'post',
formState: true,
data
})
}
// 下架包装
export function unloadPalletPage(data) {
return request({
url: '/order/unloadPallet/page',
method: 'post',
data
})
}
export function unloadPalletAdd(data) {
return request({
url: '/order/unloadPallet/add',
method: 'post',
data
})
}
export function unloadPalletGet(data) {
return request({
url: '/order/unloadPallet/get',
method: 'post',
data
})
}
export function unloadPalletUpdate(data) {
return request({
url: '/order/unloadPallet/update',
method: 'post',
data
})
}
// 验证玻璃架id
export function unloadPalletVerify(data) {
return request({
url: '/order/unloadPallet/verify',
method: 'post',
formState: true,
data
})
}
export function unloadPalletFinish(data) {
return request({
url: '/order/unloadPallet/finish',
method: 'post',
data
})
}
export function unloadPalletContinuePack(data) {
return request({
url: '/order/unloadPallet/continuePack',
method: 'post',
data
})
}
export function getDetail(data) {
return request({
url: '/order/unloadPallet/getDetail',
method: 'post',
data
})
}
// 玻璃ID追溯
export function glassIdTrace(data) {
return request({
url: '/order/unloadPallet/glassIdTrace',
method: 'post',
formState: true,
data
})
}
// 复投上架
export function reloadRack(data) {
return request({
url: '/order/reloadRack/reload',
method: 'post',
data
})
}
export function reloadRackEnd(data) {
return request({
url: '/order/reloadRack/end',
method: 'post',
data
})
}

View File

@ -0,0 +1,54 @@
import request from './request'
// 过程抽检
export function getProcessDetectPage(data) {
return request({
url: '/quality/processDetect/page',
method: 'post',
data
})
}
export function getProcessDetectList(data) {
return request({
url: '/quality/processDetect/list',
method: 'post',
data
})
}
export function getProcessDetectAdd(data) {
return request({
url: '/quality/processDetect/add',
method: 'post',
formState: true,
data
})
}
export function detectInput(data) {
return request({
url: '/quality/processDetect/detectInput',
method: 'post',
formState: true,
data
})
}
export function processDetectExport(data) {
return request({
url: '/quality/processDetect/export',
method: 'post',
responseType: 'blob',
data,
timeout: 60000
})
}
// 颗粒折线图
export function getProcessFull(data) {
return request({
url: '/quality/ProcessFull/confirm',
method: 'post',
data
})
}

121
src/api/request.js Normal file
View File

@ -0,0 +1,121 @@
import axios from 'axios'
import { MessageBox, Message, Loading } from 'element-ui'
import store from '@/store'
import router from '@/router'
import { getToken } from '@/utils/auth'
const service = axios.create({
baseURL: process.env.VUE_APP_BASE_API,
timeout: 15000 // request timeout
})
/*
当页面有两个接口时第一个接口loading的close事件会直接将第二个接口的loading实例也close
每次创建Loading实例的时候判断当前是否存在如果当前还没有Loading实例就创建一个
如果有就不会再创建而是计数每次关闭的时候判断当前的计数
如果是0了就关闭否则也计数减一直到为0的时候表示当前所有页面所有接口都返回结束了
此时执行关闭Loading.close()操作
*/
let loadingInstance = null
function startLoading() {
loadingInstance = Loading.service({
fullscreen: false,
text: '拼命加载中......',
background: 'rgba(0, 0, 0, 0.1)'
})
}
function endLoading() {
loadingInstance.close()
}
let needLoadingRequestCount = 0
function showFullScreenLoading() {
if (needLoadingRequestCount === 0) {
startLoading()
}
needLoadingRequestCount++
}
function tryHideFullScreenLoading() {
if (needLoadingRequestCount <= 0) return
needLoadingRequestCount--
if (needLoadingRequestCount === 0) {
endLoading()
}
}
// request interceptor
service.interceptors.request.use(
(config) => {
// do something before request is sent
showFullScreenLoading()
if (store.getters.token) {
config.headers['token'] = getToken()
}
if (config.formState) {
config.headers['Content-Type'] = 'application/json; charset=utf-8'
}
return config
},
(error) => {
// do something with request error
tryHideFullScreenLoading()
console.log(error) // for debug
return Promise.reject(error)
}
)
// response interceptor
service.interceptors.response.use(
(response) => {
// console.log(response)
tryHideFullScreenLoading()
const res = response.data
// if the custom code is not 20000, it is judged as an error.
if (response.config.responseType === 'blob') {
return response
}
// 具体看框架对响应码的定义
if (res.code === 401) {
console.log('111')
// to re-login
MessageBox.confirm('登录信息失效,请重新登录', '提示', {
confirmButtonText: '去登录',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
store.dispatch('user/logout').then(() => {
router.push({ path: '/login' })
})
})
}
if (res.code !== 0 && res.code !== 401) {
Message({
message: res.msg || 'Error',
type: 'error',
duration: 5 * 1000
})
return Promise.reject(new Error(res.message || 'Error'))
} else {
return res
}
},
(error) => {
tryHideFullScreenLoading()
console.log('err' + error) // for debug
if ('err' + error === 'errError: timeout of 15000ms exceeded') {
Message({
message: '网络连接超时',
type: 'error',
duration: 5 * 1000
})
} else {
Message({
message: error.message,
type: 'error',
duration: 5 * 1000
})
}
return Promise.reject(error)
}
)
export default service

10
src/api/siteMonitoring.js Normal file
View File

@ -0,0 +1,10 @@
import request from './request'
// 设备状态
export function eqMonitorGet(data) {
return request({
url: '/monitor/eqMonitor/get',
method: 'post',
data
})
}

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

@ -0,0 +1,20 @@
import request from './request'
export function login(data) {
const dto = Object.assign(data, {
appType: 1,
userType: 1
})
return request({
url: '/passport/login',
method: 'post',
data: dto
})
}
export function getUserInfo() {
return request({
url: '/passport/getLoginUser',
method: 'post'
})
}

BIN
src/assets/home/avatar.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

BIN
src/assets/home/home.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

BIN
src/assets/login-back.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

BIN
src/assets/login.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 MiB

BIN
src/assets/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 KiB

View File

@ -0,0 +1,71 @@
<template>
<div class="baseDialog">
<el-dialog
v-el-drag-dialog
:visible.sync="dialogShow"
v-bind="$attrs"
v-on="$listeners"
:close-on-click-modal="false"
>
<slot />
<template #footer>
<!--对外继续暴露footer插槽有个别弹框按钮需要自定义-->
<slot name="footer">
<!--将取消与确定按钮集成到内部-->
<el-row slot="footer" type="flex" justify="end">
<el-col :span="6">
<el-button size="small" @click="$_handleCancel">取消</el-button>
<el-button type="primary" size="small" @click="$_handleConfirm">
确定
</el-button>
</el-col>
</el-row>
</slot>
</template>
</el-dialog>
</div>
</template>
<script>
import elDragDialog from '@/directive/el-drag-dialog'
export default {
inheritAttrs: false,
name: 'BaseDialog',
directives: { elDragDialog },
props: {
dialogVisible: {
type: Boolean,
default: false
}
},
computed: {
dialogShow: {
get() {
return this.dialogVisible
},
set(val) {
this.$emit('update:dialogVisible', val)
}
}
},
methods: {
$_handleConfirm() {
this.$emit('confirm')
},
$_handleCancel() {
this.$emit('cancel')
}
}
}
</script>
<style lang="scss">
.baseDialog {
.el-dialog__title {
font-size: 16px;
font-weight: 500;
}
.el-dialog__header {
padding: 12px 20px;
border-bottom: 1px solid #e9e9e9;
}
}
</style>

View File

@ -0,0 +1,243 @@
<template>
<div class="baseTable">
<el-table
border
:ref="id"
:data="renderData"
v-bind="$attrs"
@current-change="currentChange"
@selection-change="handleSelectionChange"
style="width: 100%"
:header-cell-style="{
background: '#F2F4F9',
color: '#606266'
}"
>
<!-- 多选 -->
<el-table-column
v-if="selectWidth"
type="selection"
:width="selectWidth"
/>
<!-- 序号 -->
<el-table-column
v-if="page && limit"
prop="_pageIndex"
:width="pageWidth"
align="center"
fixed
>
<template slot="header">
<el-popover placement="bottom-start" width="300" trigger="click">
<div
class="setting-box"
style="max-height: 400px; overflow-y: auto"
>
<el-checkbox
v-for="(item, index) in tableProps"
:key="'cb' + index"
v-model="selectedBox[index]"
:label="item.label"
/>
</div>
<i slot="reference" class="el-icon-s-tools" />
</el-popover>
</template>
</el-table-column>
<el-table-column
v-for="item in renderTableHeadList"
:key="item.prop"
v-bind="item"
:label="item.label"
:prop="item.prop"
:fixed="item.fixed || false"
:showOverflowTooltip="true"
:sortable="item.sortable || false"
>
<template slot="header">
<span>{{ item.label }}</span>
</template>
<!-- 多表头 -->
<template v-if="item.children">
<el-table-column
v-for="sub in item.children"
:prop="sub.prop"
:key="sub.prop"
v-bind="sub"
:label="sub.label"
>
<template slot-scope="scopeInner">
<component
:is="sub.subcomponent"
v-if="sub.subcomponent"
:key="scopeInner.row.id"
:inject-data="{ ...scopeInner.row, ...sub }"
@emitData="emitData"
/>
<span v-else>{{
scopeInner.row[sub.prop] | commonFilter(sub.filter)
}}</span>
</template>
</el-table-column>
</template>
<template slot-scope="scope">
<component
:is="item.subcomponent"
v-if="item.subcomponent"
:key="scope.row.id"
:itemProp="item.prop"
:inject-data="{ ...scope.row, ...item }"
@emitData="emitData"
/>
<span v-else>{{
scope.row[item.prop] | commonFilter(item.filter)
}}</span>
</template>
</el-table-column>
<slot name="handleBtn" />
</el-table>
<el-button
v-if="addButtonShow"
class="addButton"
icon="el-icon-plus"
@click="emitButtonClick"
>{{ addButtonShow }}</el-button
>
</div>
</template>
<script>
export default {
name: 'BaseTable',
filters: {
commonFilter: (source, filterType = (a) => a) => {
return filterType(source)
}
},
props: {
tableData: {
type: Array,
required: true,
default: () => {
return []
}
},
tableProps: {
type: Array,
default: () => {
return []
}
},
id: {
type: String,
required: false,
default: ''
},
page: {
type: Number,
required: false,
default: 0
},
pageWidth: {
type: Number,
required: false,
default: 70
},
limit: {
type: Number,
required: false,
default: 0
},
selectWidth: {
type: Number,
required: false,
default: 0
},
addButtonShow: {
type: String,
required: false,
default: ''
}
},
data() {
return {
selectedBox: new Array(100).fill(true)
}
},
computed: {
renderTableHeadList() {
return this.tableProps.filter((item, index) => {
return this.selectedBox[index]
})
},
renderData() {
return this.tableData.map((item, index) => {
return {
...item,
_pageIndex: (this.page - 1) * this.limit + index + 1
}
})
}
},
beforeMount() {
this.selectedBox = new Array(100).fill(true)
},
methods: {
currentChange(newVal, oldVal) {
this.$emit('current-change', { newVal, oldVal })
},
emitData(val) {
this.$emit('emitFun', val)
},
emitButtonClick() {
this.$emit('emitButtonClick')
},
handleSelectionChange(val) {
this.$emit('selection-change', val)
},
setCurrent(name, index) {
let _this = this
let obj = _this.$refs[name].data[index]
_this.$refs[name].setCurrentRow(obj)
}
}
}
</script>
<style lang="scss" scoped>
.baseTable {
.show-col-btn {
margin-right: 5px;
line-height: inherit;
cursor: pointer;
}
.el-icon-refresh {
cursor: pointer;
}
}
</style>
<style lang="scss">
.el-table .el-table__cell {
padding: 0;
height: 35px;
}
.baseTable {
.addButton {
width: 100%;
height: 35px;
border-top: none;
color: #0b58ff;
border-color: #ebeef5;
border-radius: 0;
}
.addButton:hover {
color: #0b58ff;
border-color: #ebeef5;
background-color: #fff;
}
.addButton:focus {
border-color: #ebeef5;
background-color: #fff;
}
}
</style>

View File

@ -0,0 +1,37 @@
<template>
<div class="tableInner">
<el-input v-model="list[itemProp]" @blur="changeInput" />
</div>
</template>
<script>
export default {
name: 'inputArea',
props: {
injectData: {
type: Object,
default: () => ({})
},
itemProp: {
type: String
}
},
data() {
return {
list: this.injectData
}
},
methods: {
changeInput() {
console.log(this.list)
this.$emit('emitData', this.list)
}
}
}
</script>
<style lang="css">
.tableInner .el-input__inner {
border: none;
padding: 0;
height: 33px;
}
</style>

View File

@ -0,0 +1,147 @@
<template>
<el-table-column
v-if="methodList.length > 0"
slot="handleBtn"
prop="operation"
v-bind="$attrs"
>
<template slot-scope="scope">
<span v-for="(item, index) in methodList" :key="'btn' + index">
<span
v-show="index === 0 ? false : true"
style="margin: 0 4px; font-size: 18px; color: #e5e7eb"
>|</span
>
<el-button
:disabled="
item.showParam ? !showFilter(item.showParam, scope.row) : false
"
type="text"
style="margin: 5px 0; padding: 0"
@click="
clickButton({
data: scope.row,
type: item.type
})
"
>
<i
v-if="
item.type === 'delete' ||
item.type === 'edit' ||
item.type === 'detail'
"
:class="
item.type === 'delete'
? 'el-icon-delete' +
(item.showParam
? !showFilter(item.showParam, scope.row)
? ''
: ' delete-color'
: ' delete-color')
: item.type === 'edit'
? 'el-icon-edit-outline'
: item.type === 'detail'
? 'el-icon-view'
: ''
"
style="font-size: 16px"
></i>
<span v-else>{{ item.btnName }}</span>
</el-button>
</span>
</template>
</el-table-column>
</template>
<script>
/**
* 传入的组件prop格式
* methodList<MethodItem> = []
* Interface MethodItem = {
* btnName: string,
* type: string
* }
*
*/
export default {
props: {
methodList: {
type: Array,
default: () => {
return []
}
}
},
methods: {
clickButton(raw) {
this.$emit('clickBtn', {
data: raw.data,
type: raw.type
})
},
showFilter(showParam, row) {
let showStatus = true
const showStatusArr = showParam.data.map((item) => {
let showStatusItem = true
if (item.type === 'unequal') {
if (row[item.name] !== item.value) {
showStatusItem = true
} else {
showStatusItem = false
}
}
if (item.type === 'equal') {
if (row[item.name] === item.value) {
showStatusItem = true
} else {
showStatusItem = false
}
}
if (item.type === 'more') {
if (row[item.name] >= item.value) {
showStatusItem = true
} else {
showStatusItem = false
}
}
if (item.type === 'less') {
if (row[item.name] <= item.value) {
showStatusItem = true
} else {
showStatusItem = false
}
}
if (item.type === 'indexof') {
if (item.value.indexOf(row[item.name]) >= 0) {
showStatusItem = true
} else {
showStatusItem = false
}
}
if (item.type === 'unindexof') {
if (item.value.indexOf(row[item.name]) < 0) {
showStatusItem = true
} else {
showStatusItem = false
}
}
return showStatusItem
})
if (showStatusArr.indexOf(false) >= 0 && showParam.type === '&') {
showStatus = false
}
if (showStatusArr.indexOf(true) < 0 && showParam.type === '|') {
showStatus = false
}
return showStatus
}
}
}
</script>
<style>
.delete-color {
color: #ff5454;
}
</style>

View File

@ -0,0 +1,86 @@
<template>
<el-breadcrumb class="app-breadcrumb" separator="/">
<transition-group name="breadcrumb">
<el-breadcrumb-item v-for="(item, index) in levelList" :key="item.path">
<span
v-if="item.redirect === 'noRedirect' || index == levelList.length - 1"
class="isredirect"
>{{ item.meta.title }}</span
>
<span v-else class="no-redirect">{{ item.meta.title }}</span>
</el-breadcrumb-item>
</transition-group>
</el-breadcrumb>
</template>
<script>
import pathToRegexp from 'path-to-regexp'
export default {
name: 'BreadCrumb',
data() {
return {
levelList: null
}
},
watch: {
$route(route) {
if (route.path.startsWith('/redirect/')) {
return
}
this.getBreadcrumb()
}
},
created() {
this.getBreadcrumb()
},
methods: {
getBreadcrumb() {
// only show routes with meta.title
const matched = this.$route.matched.filter(
(item) => item.meta && item.meta.title
)
this.levelList = matched.filter(
(item) => item.meta && item.meta.title && item.meta.breadcrumb !== false
)
},
isDashboard(route) {
const name = route && route.name
if (!name) {
return false
}
return name.trim().toLocaleLowerCase() === 'Dashboard'.toLocaleLowerCase()
},
pathCompile(path) {
const { params } = this.$route
var toPath = pathToRegexp.compile(path)
return toPath(params)
},
handleLink(item) {
const { redirect, path } = item
if (redirect) {
this.$router.push(redirect)
return
}
this.$router.push(this.pathCompile(path))
}
}
}
</script>
<style lang="scss" scoped>
.app-breadcrumb.el-breadcrumb {
display: inline-block;
font-size: 14px;
line-height: 64px;
margin-left: 16px;
.isredirect {
color: rgba(0, 0, 0, 0.65);
}
.no-redirect {
color: #8c8c8c;
cursor: text;
}
}
</style>

View File

@ -0,0 +1,102 @@
<template>
<div v-show="total > 0" class="pagination-container">
<el-pagination
small
:current-page.sync="currentPage"
:page-size.sync="pageSize"
:layout="layout"
:page-sizes="pageSizes"
:total="total"
v-bind="$attrs"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
</template>
<script>
export default {
name: 'PaginationPage',
props: {
total: {
required: true,
type: Number
},
page: {
type: Number,
default: 1
},
limit: {
type: Number,
default: 20
},
pageSizes: {
type: Array,
default() {
return [10, 20, 30, 50]
}
},
layout: {
type: String,
default: 'total, sizes, prev, pager, next, jumper'
}
},
computed: {
currentPage: {
get() {
return this.page
},
set(val) {
this.$emit('update:page', val)
}
},
pageSize: {
get() {
return this.limit
},
set(val) {
this.$emit('update:limit', val)
}
}
},
methods: {
handleSizeChange(val) {
this.$emit('pagination', { page: this.currentPage, limit: val })
},
handleCurrentChange(val) {
this.$emit('pagination', { page: val, limit: this.pageSize })
}
}
}
</script>
<style scoped>
.pagination-container {
background: #fff;
padding-top: 20px;
text-align: right;
}
</style>
<style>
.el-pagination {
position: relative;
}
.el-pagination__jump {
margin-left: 125px;
}
.el-pagination__sizes {
position: absolute;
right: 100px;
}
.el-pager li.active {
background-color: #0b58ff;
color: #fff;
}
.el-input--mini .el-input__inner {
height: 22px;
}
</style>

View File

@ -0,0 +1,235 @@
<template>
<el-form
:inline="true"
ref="searchBarForm"
:model="formInline"
class="searchBar"
>
<template v-for="item in formConfig">
<el-form-item
v-if="item.type !== ''"
:key="item.param"
:label="item.label ? item.label : ''"
:required="item.required ? item.required : false"
:style="{ float: item.float ? item.float : 'left' }"
>
<el-input
v-if="item.type === 'input'"
v-model="formInline[item.param]"
:size="item.size ? item.size : 'small'"
clearable
:disabled="item.disabled ? item.disabled : false"
:style="item.width ? 'width:' + item.width + 'px' : 'width:200px'"
:placeholder="item.placeholder ? item.placeholder : ''"
/>
<el-select
v-if="item.type === 'select'"
v-model="formInline[item.param]"
:size="item.size ? item.size : 'small'"
:filterable="item.filterable ? item.filterable : false"
:multiple="item.multiple ? item.multiple : false"
:clearable="item.clearable === false ? false : true"
:style="item.width ? 'width:' + item.width + 'px' : 'width:200px'"
:placeholder="item.label"
@change="
item.onchange
? $emit('select-changed', {
param: item.param,
value: formInline[item.param]
})
: null
"
>
<el-option
v-for="(sub, i) in item.selectOptions"
:key="i"
:label="item.labelField ? sub[item.labelField] : sub['name']"
:value="item.valueField ? sub[item.valueField] : sub['id']"
/>
</el-select>
<el-date-picker
v-if="item.type === 'datePicker'"
:key="item.param"
:size="item.size ? item.size : 'small'"
v-model="formInline[item.param]"
:type="item.dateType"
:format="item.format ? item.format : 'yyyy-MM-dd'"
:value-format="item.valueFormat ? item.valueFormat : null"
:default-time="item.defaultTime || null"
:range-separator="item.rangeSeparator || null"
:start-placeholder="item.startPlaceholder || null"
:end-placeholder="item.endPlaceholder || null"
:placeholder="item.placeholder"
:picker-options="item.pickerOptions ? item.pickerOptions : null"
:style="item.width ? 'width:' + item.width + 'px' : 'width:250px'"
/>
<el-autocomplete
v-if="item.type === 'autocomplete'"
v-model="formInline[item.param]"
:value-key="item.valueKey ? item.valueKey : 'value'"
:fetch-suggestions="item.querySearch"
:placeholder="item.placeholder"
:clearable="item.clearable === false ? false : true"
:style="item.width ? 'width:' + item.width + 'px' : 'width:200px'"
filterable
/>
<el-cascader
v-if="item.type === 'cascader'"
v-model="formInline[item.param]"
:options="item.selectOptions"
:props="item.cascaderProps"
:clearable="item.clearable === false ? false : true"
:show-all-levels="item.showAllLevels === false ? false : true"
:collapse-tags="item.collapseTags === true ? true : false"
:style="item.width ? 'width:' + item.width + 'px' : 'width:200px'"
@change="
item.onChange
? $emit('cascader-change', {
param: item.param,
value: formInline[item.param]
})
: null
"
></el-cascader>
<el-button
v-if="item.type === 'button'"
:type="item.color"
:size="item.size ? item.size : 'small'"
:plain="item.plain ? item.plain : false"
:round="item.round ? item.round : false"
@click="headBtnClick(item.name)"
>{{ item.btnName }}</el-button
>
<!-- 可用于显示其他按钮 -->
</el-form-item>
</template>
<el-form-item>
<slot></slot>
</el-form-item>
</el-form>
</template>
<script>
export default {
name: 'SearchBar',
props: {
formConfigs: {
type: Array,
default: () => {
return []
}
}
},
data() {
const formInline = {}
const formConfig = this.formConfigs
let hasExtraOptions = false
for (const obj of formConfig) {
if (obj.type !== 'button') {
if (obj.defaultSelect === false || obj.defaultSelect === 0) {
formInline[obj.param] = obj.defaultSelect
} else {
formInline[obj.param] = obj.defaultSelect || '' // defaultSelect
}
}
if (obj.extraOptions) {
hasExtraOptions = true
}
}
return {
formInline,
formConfig,
hasExtraOptions
}
},
watch: {
formConfig: {
handler() {
for (const obj of this.formConfig) {
if (obj.defaultSelect) {
this.formInline[obj.param] = obj.defaultSelect
} else if (obj.defaultSelect === null) {
// null
this.formInline[obj.param] = ''
}
}
},
deep: true,
immediate: true
},
formInline: {
handler: function () {
this.$forceUpdate()
},
deep: true,
immediate: true
}
},
mounted() {
this.$nextTick(() => {
this.init()
})
},
methods: {
init() {
if (this.hasExtraOptions) {
//
for (const obj of this.formConfig) {
if (obj.extraOptions) {
// : obj.extraOptions!
this.$watch(
`formInline.${obj.param}`,
function (newVal) {
let deleteCount = 0
if (obj.index + 1 < this.formConfig.length) {
// obj
const nextConfig = this.formConfig[obj.index + 1]
if (nextConfig.parent && nextConfig.parent === obj.param)
deleteCount = 1
}
const currentConfig = Object.assign(
{},
obj.extraOptions[newVal]
)
this.formConfig.splice(
obj.index + 1,
deleteCount,
currentConfig
)
// formInline
this.$set(this.formInline, currentConfig.param, '')
},
{ immediate: true }
)
}
}
}
},
headBtnClick(btnName) {
this.formInline.btnName = btnName
this.$emit('headBtnClick', this.formInline)
},
resetForm() {
this.$refs.searchBarForm.resetFields()
const formInline = {}
const formConfig = this.formConfigs
for (const obj of formConfig) {
if (obj.type !== 'button') {
if (obj.defaultSelect === false || obj.defaultSelect === 0) {
formInline[obj.param] = obj.defaultSelect
} else {
formInline[obj.param] = obj.defaultSelect || '' // defaultSelect
}
}
}
this.formInline = formInline
}
}
}
</script>
<style lang="scss">
.searchBar {
.el-form-item {
margin-bottom: 10px;
}
}
</style>

View File

@ -0,0 +1,63 @@
<template>
<div
v-if="isExternal"
:style="styleExternalIcon"
class="svg-external-icon svg-icon"
v-on="$listeners"
/>
<svg v-else :class="svgClass" aria-hidden="true" v-on="$listeners">
<use :xlink:href="iconName" />
</svg>
</template>
<script>
import { isExternal } from '@/utils/index'
export default {
name: 'SvgIcon',
props: {
iconClass: {
type: String,
required: true
},
className: {
type: String,
default: ''
}
},
computed: {
isExternal() {
return isExternal(this.iconClass)
},
iconName() {
return `#icon-${this.iconClass}`
},
svgClass() {
if (this.className) {
return 'svg-icon ' + this.className
} else {
return 'svg-icon'
}
},
styleExternalIcon() {
return {
mask: `url(${this.iconClass}) no-repeat 50% 50%`,
'-webkit-mask': `url(${this.iconClass}) no-repeat 50% 50%`
}
}
}
}
</script>
<style scoped>
.svg-icon {
width: 1em;
height: 1em;
vertical-align: -0.15em;
fill: currentColor;
overflow: hidden;
}
.svg-external-icon {
background-color: currentColor;
mask-size: cover !important;
display: inline-block;
}
</style>

34
src/components/index.js Normal file
View File

@ -0,0 +1,34 @@
import Vue from 'vue'
// 将字符串首字母大写 返回当前字符串
function changeStr(str) {
return str.charAt(0).toUpperCase() + str.slice(1)
}
/**
* require.context webpack 的API, 作用是可以动态引入文件
* 第一个参数是指当前要引入文件的路径
* 第二个参数是指是否匹配子级
* 第三个参数是指匹配 .vue 文件
*/
// 所有的组件集合
const requireComponent = require.context('./', true, /.vue$/)
requireComponent.keys().forEach((fileName) => {
// 当前组件
const config = requireComponent(fileName)
let componentName = ''
if (fileName.indexOf('subcomponents') === -1) {
componentName = changeStr(
fileName.substring(fileName.indexOf('/') + 1, fileName.indexOf('/', 2))
)
// console.log(componentName, '组件名')
} else {
componentName = changeStr(
fileName.substring(
fileName.indexOf('/subcomponents') + 15,
fileName.indexOf('.', 2)
)
)
console.log(componentName, '组件名')
}
// 注册组件
Vue.component(componentName, config.default || config)
})

View File

@ -0,0 +1,77 @@
export default {
bind(el, binding, vnode) {
const dialogHeaderEl = el.querySelector('.el-dialog__header')
const dragDom = el.querySelector('.el-dialog')
dialogHeaderEl.style.cssText += ';cursor:move;'
dragDom.style.cssText += ';top:0px;'
// 获取原有属性 ie dom元素.currentStyle 火狐谷歌 window.getComputedStyle(dom元素, null);
const getStyle = (function () {
if (window.document.currentStyle) {
return (dom, attr) => dom.currentStyle[attr]
} else {
return (dom, attr) => getComputedStyle(dom, false)[attr]
}
})()
dialogHeaderEl.onmousedown = (e) => {
// 鼠标按下,计算当前元素距离可视区的距离
const disX = e.clientX - dialogHeaderEl.offsetLeft
const disY = e.clientY - dialogHeaderEl.offsetTop
const dragDomWidth = dragDom.offsetWidth
const dragDomHeight = dragDom.offsetHeight
const screenWidth = document.body.clientWidth
const screenHeight = document.body.clientHeight
const minDragDomLeft = dragDom.offsetLeft
const maxDragDomLeft = screenWidth - dragDom.offsetLeft - dragDomWidth
const minDragDomTop = dragDom.offsetTop
const maxDragDomTop = screenHeight - dragDom.offsetTop - dragDomHeight
// 获取到的值带px 正则匹配替换
let styL = getStyle(dragDom, 'left')
let styT = getStyle(dragDom, 'top')
if (styL.includes('%')) {
styL = +document.body.clientWidth * (+styL.replace(/%/g, '') / 100)
styT = +document.body.clientHeight * (+styT.replace(/%/g, '') / 100)
} else {
styL = +styL.replace(/\px/g, '')
styT = +styT.replace(/\px/g, '')
}
document.onmousemove = function (e) {
// 通过事件委托,计算移动的距离
let left = e.clientX - disX
let top = e.clientY - disY
// 边界处理
if (-left > minDragDomLeft) {
left = -minDragDomLeft
} else if (left > maxDragDomLeft) {
left = maxDragDomLeft
}
if (-top > minDragDomTop) {
top = -minDragDomTop
} else if (top > maxDragDomTop) {
top = maxDragDomTop
}
// 移动当前元素
dragDom.style.cssText += `;left:${left + styL}px;top:${top + styT}px;`
// emit onDrag event
vnode.child.$emit('dragDialog')
}
document.onmouseup = function () {
document.onmousemove = null
document.onmouseup = null
}
}
}
}

View File

@ -0,0 +1,13 @@
import drag from './drag'
const install = function (Vue) {
Vue.directive('el-drag-dialog', drag)
}
if (window.Vue) {
window['el-drag-dialog'] = drag
Vue.use(install); // eslint-disable-line
}
drag.install = install
export default drag

10
src/icons/index.js Normal file
View File

@ -0,0 +1,10 @@
const req = require.context('./svg', false, /\.svg$/)
const requireAll = (requireContext) => requireContext.keys().map(requireContext)
requireAll(req)
export default {
// 获取图标icon-(*).svg名称列表, 例如[shouye, xitong, zhedie, ...]
getNameList() {
return requireAll(req).map((item) => item.default.id.replace('icon-', ''))
}
}

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="14px" height="14px" viewBox="0 0 14 14" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>icon/编辑_蓝</title>
<g id="页面-2" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="icon" transform="translate(-839.000000, -258.000000)" fill-rule="nonzero">
<g id="编组" transform="translate(837.000000, 256.000000)">
<rect id="矩形" fill="#000000" opacity="0" x="0" y="0" width="18" height="18"></rect>
<path d="M5.60956445,13.1513132 C5.87393882,13.1702648 6.14494625,13.0897206 6.36099413,12.9115759 L6.43395766,12.8878864 L4.13513244,10.5890612 L4.11239056,10.6620248 C3.93329824,10.8780726 3.85370165,11.1490801 3.87265322,11.4134544 L3.05678821,13.9662307 L5.60956445,13.1513132 L5.60956445,13.1513132 L5.60956445,13.1513132 Z M13.9321456,3.84325052 L13.1797684,3.09087326 C12.764729,2.67583391 12.0910008,2.67583391 11.6759614,3.09087326 L10.9472736,3.81956106 L13.2025103,6.07479767 L13.931198,5.34610988 C14.347185,4.93201811 14.347185,4.25828986 13.9321456,3.84325052 L13.9321456,3.84325052 L13.9321456,3.84325052 Z M12.4283387,6.85086439 L10.1731021,4.59562777 L4.93299346,9.83573639 L7.18823008,12.0919206 L12.4283387,6.85086439 L12.4283387,6.85086439 L12.4283387,6.85086439 Z M2.36695112,14.6570153 L15.6330489,14.6570153 L15.6330489,15.6045937 L2.36695112,15.6045937 L2.36695112,14.6570153 Z" id="形状" fill="#0B58FF"></path>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="32px" height="37px" viewBox="0 0 32 37" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<path d="M25.1111111,27.945816 L7.33333334,27.945816 C6.35149379,27.945816 5.55555556,27.1498778 5.55555556,26.1680382 L5.55555556,24.3902604 L26.8888889,24.3902604 L26.8888889,26.1680382 C26.8888889,27.1498778 26.0929507,27.945816 25.1111111,27.945816 L25.1111111,27.945816 Z M16.2222222,5.57630555 C21.1315556,5.57630555 25.1111111,9.38985381 25.1111111,14.0943941 L25.1111111,22.6124827 L7.33333334,22.6124827 L7.33333334,14.0943941 C7.33333334,9.38985378 11.3128889,5.57630555 16.2222222,5.57630555 Z M25.1111111,27.945816 L7.33333334,27.945816 C6.35149379,27.945816 5.55555556,27.1498778 5.55555556,26.1680382 L5.55555556,24.3902604 L26.8888889,24.3902604 L26.8888889,26.1680382 C26.8888889,27.1498778 26.0929507,27.945816 25.1111111,27.945816 L25.1111111,27.945816 Z M16.2222222,6.34859754 C21.1315556,6.34859754 25.1111111,9.98926823 25.1111111,14.4805401 L25.1111111,22.6124827 L7.33333334,22.6124827 L7.33333334,14.4805401 C7.33333334,9.9892682 11.3128889,6.34859754 16.2222222,6.34859754 Z M15.9973293,1.6 C18.8248067,1.6 20.9672084,2.83950116 22.3037373,5.2047258 C22.5399148,5.62268371 22.3265599,6.1217535 21.8271957,6.31942939 C21.3278316,6.51710527 20.7315573,6.33853141 20.4953798,5.92057351 C19.477056,4.11846919 18.0179715,3.27430588 15.9973293,3.27430588 C13.9770609,3.27430588 12.5200814,4.11808572 11.5051206,5.91968804 C11.2695274,6.33787688 10.6735031,6.5170344 10.173863,6.31984754 C9.67422293,6.12266069 9.46017065,5.62380012 9.69576386,5.20561127 C11.0285329,2.83988464 13.1694781,1.6 15.9973293,1.6 Z" id="path-1"></path>
<filter x="-39.8%" y="-24.7%" width="179.7%" height="164.5%" filterUnits="objectBoundingBox" id="filter-2">
<feOffset dx="0" dy="2" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
<feGaussianBlur stdDeviation="2.5" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
<feColorMatrix values="0 0 0 0 0.31372549 0 0 0 0 0.823529412 0 0 0 0 0.466666667 0 0 0 0.6 0" type="matrix" in="shadowBlurOuter1"></feColorMatrix>
</filter>
</defs>
<g id="1首页" stroke="none" stroke-width="1" fill="" fill-rule="">
<g id="TFT首页" transform="translate(-1199.000000, -204.000000)" fill-rule="">
<g id="编组-23" transform="translate(1199.000000, 184.000000)">
<g id="报警" transform="translate(0.000000, 22.000000)">
<rect id="矩形" fill="" opacity="0" x="0" y="0" width="32" height="32"></rect>
<g id="形状">
<use fill="" fill-opacity="1" filter="" xlink:href="#path-1"></use>
<use fill="" xlink:href="#path-1"></use>
</g>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="32px" height="32px" viewBox="0 0 32 32" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>编组 8</title>
<g id="1首页" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="TFT首页" transform="translate(-56.000000, -232.000000)">
<g id="编组-8" transform="translate(56.000000, 232.000000)">
<circle id="椭圆形" fill="#FF9D50" cx="16" cy="16" r="16"></circle>
<g id="file-copy" transform="translate(4.000000, 4.000000)" fill-rule="nonzero">
<rect id="矩形" fill="#97BC45" opacity="0" x="0" y="0" width="24" height="24"></rect>
<path d="M19.2727273,2 L7.09090909,2 C6.99090909,2 6.90909091,2.08035714 6.90909091,2.17857143 L6.90909091,3.42857143 C6.90909091,3.52678571 6.99090909,3.60714286 7.09090909,3.60714286 L18.3636364,3.60714286 L18.3636364,18.9642857 C18.3636364,19.0625 18.4454545,19.1428571 18.5454545,19.1428571 L19.8181818,19.1428571 C19.9181818,19.1428571 20,19.0625 20,18.9642857 L20,2.71428571 C20,2.31919643 19.675,2 19.2727273,2 Z M16.3636364,4.85714286 L4.72727273,4.85714286 C4.325,4.85714286 4,5.17633929 4,5.57142857 L4,17.4174107 C4,17.6071429 4.07727273,17.7879464 4.21363636,17.921875 L8.15227273,21.7901786 C8.20227273,21.8392857 8.25909091,21.8794643 8.32045455,21.9129464 L8.32045455,21.9553571 L8.41590909,21.9553571 C8.49545455,21.984375 8.57954545,22 8.66590909,22 L16.3636364,22 C16.7659091,22 17.0909091,21.6808036 17.0909091,21.2857143 L17.0909091,5.57142857 C17.0909091,5.17633929 16.7659091,4.85714286 16.3636364,4.85714286 Z M8.31818182,19.6830357 L6.36136364,17.7589286 L8.31818182,17.7589286 L8.31818182,19.6830357 Z M15.4545455,20.3928571 L9.77272727,20.3928571 L9.77272727,17.2232143 C9.77272727,16.7299107 9.36590909,16.3303571 8.86363636,16.3303571 L5.63636364,16.3303571 L5.63636364,6.46428571 L15.4545455,6.46428571 L15.4545455,20.3928571 Z M13.1730128,10.3734619 L12.3271338,11.2383597 C12.2425085,11.3245814 12.2043455,11.4475956 12.2248911,11.5679284 L12.4246113,12.7897255 C12.474872,13.0935528 12.1698778,13.3266437 11.9088505,13.1819534 L10.8633027,12.6064493 C10.7595186,12.5501729 10.6353116,12.5501729 10.5330817,12.6064493 L9.48749537,13.1835557 C9.22649375,13.3266569 8.92146096,13.0951419 8.97182451,12.7913277 L9.17144193,11.5694913 C9.1919547,11.448927 9.15261179,11.3267473 9.06927624,11.2398832 L8.22339726,10.3749854 C8.01268213,10.1595981 8.12902821,9.78340601 8.41995767,9.73841111 L9.5881454,9.55995492 C9.70447865,9.542238 9.80512868,9.46509261 9.85540232,9.35575735 L10.3774312,8.24487157 C10.4353818,8.11820496 10.5598532,8.03733602 10.6966187,8.03749549 C10.8333843,8.03733602 10.9578556,8.11820496 11.0158062,8.24487157 L11.5377452,9.35575735 C11.5896887,9.46509263 11.6902616,9.54223802 11.8050792,9.55995492 L12.9732798,9.73842424 C13.2673433,9.78183002 13.3836637,10.158009 13.1730128,10.3734619 L13.1730128,10.3734619 Z" id="形状结合" fill="#FFFFFF"></path>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="32px" height="32px" viewBox="0 0 32 32" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>编组 8</title>
<g id="1首页" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="TFT首页" transform="translate(-56.000000, -362.000000)">
<g id="编组-8" transform="translate(56.000000, 362.000000)">
<circle id="椭圆形" fill="#50D277" cx="16" cy="16" r="16"></circle>
<g id="file-copy" transform="translate(4.000000, 4.000000)" fill-rule="nonzero">
<rect id="矩形" fill="#97BC45" opacity="0" x="0" y="0" width="24" height="24"></rect>
<path d="M16.3636364,4.85714286 C16.7659091,4.85714286 17.0909091,5.17633929 17.0909091,5.57142857 L17.0909091,5.57142857 L17.0909091,21.2857143 C17.0909091,21.6808036 16.7659091,22 16.3636364,22 L16.3636364,22 L8.66590909,22 C8.57954545,22 8.49545455,21.984375 8.41590909,21.9553571 L8.41590909,21.9553571 L8.32045455,21.9553571 L8.32045455,21.9129464 C8.25909091,21.8794643 8.20227273,21.8392857 8.15227273,21.7901786 L8.15227273,21.7901786 L4.21363636,17.921875 C4.07727273,17.7879464 4,17.6071429 4,17.4174107 L4,17.4174107 L4,5.57142857 C4,5.17633929 4.325,4.85714286 4.72727273,4.85714286 L4.72727273,4.85714286 Z M15.4545455,6.46428571 L5.63636364,6.46428571 L5.63636364,16.3303571 L8.86363636,16.3303571 C9.36590909,16.3303571 9.77272727,16.7299107 9.77272727,17.2232143 L9.77272727,17.2232143 L9.77272727,20.3928571 L15.4545455,20.3928571 L15.4545455,6.46428571 Z M8.31818182,17.7589286 L6.36136364,17.7589286 L8.31818182,19.6830357 L8.31818182,17.7589286 Z M19.2727273,2 C19.675,2 20,2.31919643 20,2.71428571 L20,2.71428571 L20,18.9642857 C20,19.0625 19.9181818,19.1428571 19.8181818,19.1428571 L19.8181818,19.1428571 L18.5454545,19.1428571 C18.4454545,19.1428571 18.3636364,19.0625 18.3636364,18.9642857 L18.3636364,18.9642857 L18.3636364,3.60714286 L7.09090909,3.60714286 C6.99090909,3.60714286 6.90909091,3.52678571 6.90909091,3.42857143 L6.90909091,3.42857143 L6.90909091,2.17857143 C6.90909091,2.08035714 6.99090909,2 7.09090909,2 L7.09090909,2 Z M7.29514457,7.95744681 C7.64947456,7.95744681 7.93671577,8.2432225 7.93671577,8.59574468 C7.93671577,8.94826686 7.64947456,9.23404255 7.29514457,9.23404255 C6.94081458,9.23404255 6.65357338,8.94826686 6.65357338,8.59574468 C6.65357338,8.2432225 6.94081458,7.95744681 7.29514457,7.95744681 Z M12.8781431,7.95744681 C13.2306653,7.95744681 13.516441,8.2432225 13.516441,8.59574468 C13.516441,8.94826686 13.2306653,9.23404255 12.8781431,9.23404255 L9.02216932,9.23404255 C8.66964714,9.23404255 8.38387145,8.94826686 8.38387145,8.59574468 C8.38387145,8.2432225 8.66964714,7.95744681 9.02216932,7.95744681 L12.8781431,7.95744681 Z" id="形状结合" fill="#FFFFFF"></path>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>编组 6</title>
<g id="1首页" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="TFT首页" transform="translate(-32.000000, -144.000000)">
<g id="编组-13" transform="translate(32.000000, 144.000000)">
<rect id="矩形" x="0" y="0" width="24" height="24"></rect>
<path d="M20.1712942,15.1940878 C20.469528,15.1940878 20.716941,15.4116774 20.7634413,15.6967646 L20.7712942,15.7940878 L20.7712942,16.9122388 C20.7712942,17.1130042 20.6711555,17.2980374 20.5085637,17.4084976 L20.4222379,17.4572409 L12.4604763,21.1231952 C12.2098816,21.2380551 11.9270552,21.2544636 11.6667289,21.1721808 L11.5386942,21.1228139 L3.5797274,17.4572236 C3.39738895,17.3732456 3.27121939,17.2049072 3.23889214,17.0110363 L3.23072312,16.9122455 L3.23072312,15.7940878 C3.23072312,15.7074024 3.24950648,15.6217463 3.28578033,15.5430153 C3.41057745,15.2721481 3.71173156,15.1384895 3.99011687,15.2155521 L4.08179554,15.249145 L12.0009564,18.897 L19.9202218,15.249145 L20.0010428,15.2187492 L20.0010428,15.2187492 L20.0851703,15.200301 L20.1712942,15.1940878 Z M20.1712942,11.1940878 C20.469528,11.1940878 20.716941,11.4116774 20.7634413,11.6967646 L20.7712942,11.7940878 L20.7712942,12.9122388 C20.7712942,13.1130042 20.6711555,13.2980374 20.5085637,13.4084976 L20.4222379,13.4572409 L12.4604763,17.1231952 C12.2098816,17.2380551 11.9270552,17.2544636 11.6667289,17.1721808 L11.5386942,17.1228139 L3.5797274,13.4572236 C3.39738895,13.3732456 3.27121939,13.2049072 3.23889214,13.0110363 L3.23072312,12.9122455 L3.23072312,11.7940878 C3.23072312,11.7074024 3.24950648,11.6217463 3.28578033,11.5430153 C3.41057745,11.2721481 3.71173156,11.1384895 3.99011687,11.2155521 L4.08179554,11.249145 L12.0009564,14.897 L19.9202218,11.249145 L20.0010428,11.2187492 L20.0010428,11.2187492 L20.0851703,11.200301 L20.1712942,11.1940878 Z M12.252143,4.05547142 L20.8249999,8.0040285 C21.0395775,8.10283189 21.1770436,8.31746777 21.1770436,8.55369992 C21.1770436,8.78993206 21.0395775,9.00456794 20.8249999,9.10337133 L12.252143,13.0519284 C12.1733732,13.0881996 12.0877278,13.1071173 12.0010087,13.1073998 C11.9136092,13.1073998 11.8272451,13.088483 11.7478572,13.0519284 L3.17500027,9.10337133 C2.96042274,9.00456794 2.82295661,8.78993206 2.82295661,8.55369992 C2.82295661,8.31746777 2.96042274,8.10283189 3.17500027,8.0040285 L11.7478572,4.05547142 C11.907945,3.9820953 12.0920552,3.9820953 12.252143,4.05547142 Z" id="形状结合" fill="#0B58FF"></path>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>当前位置</title>
<g id="1首页" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="TFT首页" transform="translate(-976.000000, -144.000000)" fill-rule="nonzero">
<g id="当前位置" transform="translate(976.000000, 144.000000)">
<rect id="矩形" fill="#000000" opacity="0" x="0" y="0" width="24" height="24"></rect>
<path d="M14,9 L14,4 L6,4 C4.8954305,4 4,4.8954305 4,6 L4,14 L4,14 L9,14 L9,9 L14,9 Z M15,10 L15,15 L10,15 L10,20 L18,20 C19.1045695,20 20,19.1045695 20,18 L20,10 L20,10 L15,10 Z" id="形状" fill="#0B58FF"></path>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 855 B

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>列表</title>
<g id="1首页" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="TFT首页" transform="translate(-32.000000, -584.000000)" fill-rule="nonzero">
<g id="列表" transform="translate(32.000000, 584.000000)">
<rect id="矩形" fill="#000000" opacity="0" x="0" y="0" width="24" height="24"></rect>
<path d="M6.07942516,4.45196857 C6.42861125,4.45196857 6.72479588,4.57044243 6.96797905,4.80739013 C7.21116222,5.04433783 7.3327538,5.33740473 7.3327538,5.68659082 L7.3327538,6.64061709 C7.3327538,6.98980318 7.21116222,7.28598781 6.96797905,7.52917098 C6.72479588,7.77235415 6.42861125,7.89394573 6.07942516,7.89394573 L5.08798609,7.89394573 C4.75127094,7.89394573 4.46132177,7.77235415 4.21813861,7.52917098 C3.97495544,7.28598781 3.85336385,6.98980318 3.85336385,6.64061709 L3.85336385,5.68659082 C3.85336385,5.33740473 3.97495544,5.04433783 4.21813861,4.80739013 C4.46132177,4.57044243 4.75127094,4.45196857 5.08798609,4.45196857 L6.07942516,4.45196857 Z M18.9120139,4.45196857 C19.2487291,4.45196857 19.5386782,4.57044243 19.7818614,4.80739013 C20.0250446,5.04433783 20.1466361,5.33740473 20.1466361,5.68659082 L20.1466361,6.64061709 C20.1466361,6.98980318 20.0250446,7.28598781 19.7818614,7.52917098 C19.5386782,7.77235415 19.2487291,7.89394573 18.9120139,7.89394573 L10.9243821,7.89394573 C10.5751961,7.89394573 10.2790114,7.77235415 10.0358283,7.52917098 C9.79264509,7.28598781 9.6710535,6.98980318 9.6710535,6.64061709 L9.6710535,5.68659082 C9.6710535,5.33740473 9.79264509,5.04433783 10.0358283,4.80739013 C10.2790114,4.57044243 10.5751961,4.45196857 10.9243821,4.45196857 L18.9120139,4.45196857 Z M6.07942516,10.2696582 C6.42861125,10.2696582 6.72479588,10.3912498 6.96797905,10.634433 C7.21116222,10.8776161 7.3327538,11.1738008 7.3327538,11.5229869 L7.3327538,12.4770131 C7.3327538,12.8137283 7.21116222,13.1036775 6.96797905,13.3468606 C6.72479588,13.5900438 6.42861125,13.7116354 6.07942516,13.7116354 L5.08798609,13.7116354 C4.75127094,13.7116354 4.46132177,13.5900438 4.21813861,13.3468606 C3.97495544,13.1036775 3.85336385,12.8137283 3.85336385,12.4770131 L3.85336385,11.5229869 C3.85336385,11.1738008 3.97495544,10.8776161 4.21813861,10.634433 C4.46132177,10.3912498 4.75127094,10.2696582 5.08798609,10.2696582 L6.07942516,10.2696582 Z M18.9120139,10.2696582 C19.2487291,10.2696582 19.5386782,10.3912498 19.7818614,10.634433 C20.0250446,10.8776161 20.1466361,11.1738008 20.1466361,11.5229869 L20.1466361,12.4770131 C20.1466361,12.8137283 20.0250446,13.1036775 19.7818614,13.3468606 C19.5386782,13.5900438 19.2487291,13.7116354 18.9120139,13.7116354 L10.9243821,13.7116354 C10.5751961,13.7116354 10.2790114,13.5900438 10.0358283,13.3468606 C9.79264509,13.1036775 9.6710535,12.8137283 9.6710535,12.4770131 L9.6710535,11.5229869 C9.6710535,11.1738008 9.79264509,10.8776161 10.0358283,10.634433 C10.2790114,10.3912498 10.5751961,10.2696582 10.9243821,10.2696582 L18.9120139,10.2696582 Z M6.07942516,16.1060543 C6.42861125,16.1060543 6.72479588,16.2276459 6.96797905,16.470829 C7.21116222,16.7140122 7.3327538,17.0039614 7.3327538,17.3406765 L7.3327538,18.2947028 C7.3327538,18.6438889 7.21116222,18.9400735 6.96797905,19.1832567 C6.72479588,19.4264398 6.42861125,19.5480314 6.07942516,19.5480314 L5.08798609,19.5480314 C4.75127094,19.5480314 4.46132177,19.4264398 4.21813861,19.1832567 C3.97495544,18.9400735 3.85336385,18.6438889 3.85336385,18.2947028 L3.85336385,17.3406765 C3.85336385,17.0039614 3.97495544,16.7140122 4.21813861,16.470829 C4.46132177,16.2276459 4.75127094,16.1060543 5.08798609,16.1060543 L6.07942516,16.1060543 Z M18.9120139,16.1060543 C19.2487291,16.1060543 19.5386782,16.2276459 19.7818614,16.470829 C20.0250446,16.7140122 20.1466361,17.0039614 20.1466361,17.3406765 L20.1466361,18.2947028 C20.1466361,18.6438889 20.0250446,18.9400735 19.7818614,19.1832567 C19.5386782,19.4264398 19.2487291,19.5480314 18.9120139,19.5480314 L10.9243821,19.5480314 C10.5751961,19.5480314 10.2790114,19.4264398 10.0358283,19.1832567 C9.79264509,18.9400735 9.6710535,18.6438889 9.6710535,18.2947028 L9.6710535,17.3406765 C9.6710535,17.0039614 9.79264509,16.7140122 10.0358283,16.470829 C10.2790114,16.2276459 10.5751961,16.1060543 10.9243821,16.1060543 L18.9120139,16.1060543 Z" id="形状" fill="#0B58FF"></path>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.5 KiB

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>信息、语音信息、提示信息-08</title>
<defs>
<linearGradient x1="50%" y1="-112.49775%" x2="50%" y2="460.456258%" id="linearGradient-1">
<stop stop-color="#0B58FF" offset="0%"></stop>
<stop stop-color="#1FACFD" offset="100%"></stop>
</linearGradient>
</defs>
<g id="1首页" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="TFT首页" transform="translate(-976.000000, -584.000000)" fill-rule="nonzero">
<g id="信息、语音信息、提示信息-08" transform="translate(976.000000, 584.000000)">
<rect id="矩形" fill="#000000" opacity="0" x="0" y="0" width="24" height="24"></rect>
<polygon id="路径" fill="url(#linearGradient-1)" points="7.96871116 7.7766487 10.439589 7.7766487 10.439589 10.4721518 7.96871116 10.4721518"></polygon>
<path d="M17.8569712,3.72480032 L6.15347452,3.72480032 C5.26684598,3.72480032 4.52798886,4.46365744 4.52798886,5.35028598 L4.52798886,18.649714 C4.52798886,19.5363426 5.26684598,20.2751997 6.15347452,20.2751997 L17.8865255,20.2751997 C18.773154,20.2751997 19.5120111,19.5363426 19.5120111,18.649714 L19.5120111,5.35028598 C19.4824568,4.46365744 18.773154,3.72480032 17.8569712,3.72480032 Z M7.01054876,8.01017159 C7.01054876,7.35997732 7.54252588,6.8280002 8.19272015,6.8280002 L10.1433029,6.8280002 C10.7934972,6.8280002 11.3254743,7.35997732 11.3254743,8.01017159 L11.3254743,10.1380801 C11.3254743,10.7882743 10.7934972,11.3202515 10.1433029,11.3202515 L8.19272015,11.3202515 C7.54252588,11.3202515 7.01054876,10.7882743 7.01054876,10.1380801 L7.01054876,8.01017159 Z M16.4088112,17.1719998 L7.60163446,17.1719998 C7.27653734,17.1719998 7.01054876,16.9060112 7.01054876,16.5809141 C7.01054876,16.255817 7.27653732,15.9898284 7.60163446,15.9898284 L16.379257,15.9898284 C16.7043541,15.9898284 16.9703427,16.255817 16.9703427,16.5809141 C16.9703427,16.9060112 16.7339084,17.1719998 16.4088112,17.1719998 Z M16.4088112,14.5121142 L7.60163446,14.5121142 C7.27653734,14.5121142 7.01054876,14.2461256 7.01054876,13.9210285 C7.01054876,13.5959313 7.27653732,13.3299428 7.60163446,13.3299428 L16.379257,13.3299428 C16.7043541,13.3299428 16.9703427,13.5959314 16.9703427,13.9210285 C16.9703427,14.2461256 16.7339084,14.5121142 16.4088112,14.5121142 Z M16.4088112,10.9360458 L13.4533828,10.9360458 C13.1282857,10.9360458 12.8622971,10.6700572 12.8622971,10.3449601 C12.8622971,10.0198629 13.1282857,9.75387437 13.4533828,9.75387437 L16.4088112,9.75387437 C16.7339084,9.75387437 16.9998969,10.0198629 16.9998969,10.3449601 C16.9998969,10.6700572 16.7339084,10.9360458 16.4088112,10.9360458 Z M16.4088112,8.39437729 L13.4533828,8.39437729 C13.1282857,8.39437729 12.8622971,8.12838873 12.8622971,7.80329159 C12.8622971,7.47819445 13.1282857,7.2122059 13.4533828,7.2122059 L16.4088112,7.2122059 C16.7339084,7.2122059 16.9998969,7.47819446 16.9998969,7.80329159 C16.9998969,8.12838871 16.7339084,8.39437729 16.4088112,8.39437729 Z" id="形状" fill="#0B58FF"></path>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.2 KiB

22
src/icons/svgo.yml Normal file
View File

@ -0,0 +1,22 @@
# replace default config
# multipass: true
# full: true
plugins:
# - name
#
# or:
# - name: false
# - name: true
#
# or:
# - name:
# param1: 1
# param2: 2
- removeAttrs:
attrs:
- 'fill'
- 'fill-rule'

View File

@ -0,0 +1,10 @@
<template>
<section>
<router-view />
</section>
</template>
<script>
export default {
name: 'AppMain'
}
</script>

View File

@ -0,0 +1,16 @@
<template>
<div class="footer">
<span>© 中建材智能自动化研究院有限公司</span>
</div>
</template>
<script>
export default {
name: 'footerPage'
}
</script>
<style scoped>
.footer {
font-size: 12px;
color: #c7c7c7;
}
</style>

View File

@ -0,0 +1,186 @@
<template>
<div class="navbar-container">
<div v-show="activeModule === 'home'" class="logo-box">
<img src="./../../assets/logo.png" alt="中建材" class="logo-img" />
<span class="title">G8.5 TFT-LCD 玻璃基板后工程段制造执行系统</span>
</div>
<breadcrumb v-show="activeModule !== 'home'" />
<div class="fr">
<div class="fr avatar-area">
<el-dropdown>
<div style="cursor: pointer">
<img :src="require('./../../assets/home/avatar.png')" alt="" />
<p>{{ userName }}</p>
</div>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item @click.native="logout">退出</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
<div class="top-time fr">
<p>{{ topDate }}</p>
<p>{{ topTime }}</p>
</div>
<ul class="topMenu-box">
<li
v-for="(item, i) in topModuleList"
:key="i"
@click="toggleModule(item)"
>
<div class="icon">
<img
:src="require('./../../assets/home/' + item.name + '.png')"
:alt="item.label"
/>
</div>
<span
class="module"
:class="{ active: activeModule === item.name }"
>{{ item.label }}</span
>
</li>
</ul>
</div>
</div>
</template>
<script>
import { mapGetters } from 'vuex'
import moment from 'moment'
export default {
name: 'NavbarPage',
data() {
return {
topDate: '',
topTime: '',
timer: '',
userName: this.$store.getters.username
}
},
computed: {
...mapGetters(['topModuleList']),
activeModule: {
get: function () {
return this.$store.getters.activeModule
},
set: function () {}
}
},
mounted() {
this.$store.dispatch('menu/getTopModuleList')
this.getTime()
},
beforeDestroy() {
clearInterval(this.timer)
},
methods: {
getTime() {
let _this = this
this.timer = setInterval(function () {
_this.topDate = moment().format('YYYY.MM.DD')
_this.topTime = moment().format('HH : mm : ss')
}, 1000)
},
toggleModule(item) {
//
const activeBox = { activeModule: item.name, activeMenu: '' }
this.$store.dispatch('menu/setActiveModule', activeBox)
},
logout() {
this.$store.dispatch('user/logout').then(() => {
this.$router.push({ path: '/login' })
})
}
}
}
</script>
<style lang="scss">
@import '@/styles/variables.module.scss';
.navbar-container {
height: 64px;
background: #fff;
box-shadow: 0px 1px 4px 0px rgba(0, 21, 41, 0.12);
padding-right: 24px;
.logo-box {
display: inline-block;
position: relative;
.logo-img {
height: 36px;
width: 36px;
margin: 16px 8px 12px 32px;
}
.title {
font-size: 18px;
font-weight: 600;
width: 390px;
color: #131415;
position: absolute;
top: 22px;
}
}
.topMenu-box {
display: flex;
margin-top: 8px;
li {
margin-right: 24px;
margin-left: 24px;
text-align: center;
cursor: pointer;
.icon {
height: 32px;
img {
width: 32px;
height: 32px;
}
}
.module {
font-size: 10px;
color: #000000;
}
.active {
color: #0b58ff;
}
}
}
.top-time {
display: inline-block;
font-size: 16px;
color: #000000;
margin: 12px 18px 0 0;
p:first-child {
margin-bottom: 9px;
}
p {
width: 81px;
text-align-last: justify;
text-align: justify;
}
}
.avatar-area {
text-align: center;
padding-top: 5px;
div {
display: inline-block;
}
img {
width: 32px;
height: 32px;
}
p {
font-size: 12px;
color: #000000;
width: 50px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
.avatar-area::before {
display: inline-block;
content: '';
margin-right: 15px;
width: 1px;
height: 47px;
background-color: #dcdfe6;
}
}
</style>

View File

@ -0,0 +1,3 @@
export { default as AppMain } from './AppMain'
export { default as MenuList } from './menu/MenuList'
export { default as Navbar } from './Navbar'

View File

@ -0,0 +1,35 @@
<template>
<div class="logo-box">
<img src="./../../../assets/logo.png" alt="中建材" class="logo-img" />
<div class="title">
<p>G8.5 TFT-LCD</p>
<p>玻璃基板后工程段制造执行系统</p>
</div>
</div>
</template>
<script>
export default {
name: 'LogoVue'
}
</script>
<style lang="scss" scoped>
.logo-box {
display: inline-block;
width: 247px;
height: 64px;
background-color: #001529;
position: relative;
.logo-img {
height: 36px;
margin: 14px 8px 0 16px;
}
.title {
display: inline-block;
font-size: 12px;
font-weight: 500;
color: #fff;
position: absolute;
top: 20px;
}
}
</style>

View File

@ -0,0 +1,91 @@
<template>
<div class="menu-container">
<logo-vue />
<el-menu :default-active="activeMenu">
<template v-for="(item, i) in leftMenuList">
<el-menu-item
v-if="
!(
(item.name === 'offShelfPackaging' && role === '加工人员') ||
(item.name === 'reLaunching' && role === '质检人员')
)
"
:key="item.name + i"
:index="item.name"
@click="toggleMenu(item)"
>
<!-- <el-menu-item
:key="item.name + i"
:index="item.name"
@click="toggleMenu(item)"
> -->
<i class="el-icon-s-operation"></i>
<span slot="title">{{ item.meta.title }}</span>
</el-menu-item>
</template>
</el-menu>
</div>
</template>
<script>
import variables from '@/styles/variables.module.scss'
import LogoVue from './Logo.vue'
export default {
name: 'MenuList',
computed: {
variables() {
return variables
},
leftMenuList() {
return this.$store.state.menu.leftMenuList
},
activeModule() {
return this.$store.state.menu.activeModule
},
activeMenu: {
get: function () {
return this.$store.getters.activeMenu
},
set: function () {}
},
role() {
return this.$store.getters.roles
}
},
components: {
LogoVue
},
mounted() {},
methods: {
toggleMenu(e) {
this.$store.dispatch('menu/setActiveMenu', e.name)
}
},
watch: {
activeModule: function (newVal, oldVal) {
console.log(newVal)
console.log(oldVal)
},
activeMenu: function (newVal) {
this.$router.push({ name: newVal })
}
}
}
</script>
<style lang="scss" scoped>
.menu-container {
height: 100%;
.el-menu {
height: calc(100vh - 64px);
background-color: #001529;
.el-menu-item,
.el-menu-item i {
color: #fff;
}
.el-menu-item:focus,
.el-menu-item:hover,
.el-menu-item.is-active {
background-color: #0b58ff;
}
}
}
</style>

View File

@ -0,0 +1,104 @@
<template>
<el-scrollbar
ref="scrollContainer"
:vertical="false"
class="scroll-container"
@wheel.native.prevent="handleScroll"
>
<slot />
</el-scrollbar>
</template>
<script>
const tagAndTagSpacing = 4 // tagAndTagSpacing
export default {
name: 'ScrollPane',
data() {
return {
left: 0
}
},
computed: {
scrollWrapper() {
return this.$refs.scrollContainer.$refs.wrap
}
},
mounted() {
this.scrollWrapper.addEventListener('scroll', this.emitScroll, true)
},
beforeDestroy() {
this.scrollWrapper.removeEventListener('scroll', this.emitScroll)
},
methods: {
handleScroll(e) {
const eventDelta = e.wheelDelta || -e.deltaY * 40
const $scrollWrapper = this.scrollWrapper
$scrollWrapper.scrollLeft = $scrollWrapper.scrollLeft + eventDelta / 4
},
emitScroll() {
this.$emit('scroll')
},
moveToTarget(currentTag) {
const $container = this.$refs.scrollContainer.$el
const $containerWidth = $container.offsetWidth
const $scrollWrapper = this.scrollWrapper
const tagList = this.$parent.$refs.tag
let firstTag = null
let lastTag = null
// find first tag and last tag
if (tagList.length > 0) {
firstTag = tagList[0]
lastTag = tagList[tagList.length - 1]
}
if (firstTag === currentTag) {
$scrollWrapper.scrollLeft = 0
} else if (lastTag === currentTag) {
$scrollWrapper.scrollLeft = $scrollWrapper.scrollWidth - $containerWidth
} else {
// find preTag and nextTag
const currentIndex = tagList.findIndex((item) => item === currentTag)
const prevTag = tagList[currentIndex - 1]
const nextTag = tagList[currentIndex + 1]
// the tag's offsetLeft after of nextTag
const afterNextTagOffsetLeft =
nextTag.$el.offsetLeft + nextTag.$el.offsetWidth + tagAndTagSpacing
// the tag's offsetLeft before of prevTag
const beforePrevTagOffsetLeft =
prevTag.$el.offsetLeft - tagAndTagSpacing
if (
afterNextTagOffsetLeft >
$scrollWrapper.scrollLeft + $containerWidth
) {
$scrollWrapper.scrollLeft = afterNextTagOffsetLeft - $containerWidth
} else if (beforePrevTagOffsetLeft < $scrollWrapper.scrollLeft) {
$scrollWrapper.scrollLeft = beforePrevTagOffsetLeft
}
}
}
}
}
</script>
<style lang="scss" scoped>
.scroll-container {
white-space: nowrap;
position: relative;
overflow: hidden;
width: 100%;
::v-deep {
.el-scrollbar__bar {
bottom: 0px;
}
.el-scrollbar__wrap {
height: 49px;
}
}
}
</style>

View File

@ -0,0 +1,116 @@
<template>
<div class="tags-view-container">
<scroll-pane ref="scrollPane" class="tags-view-wrapper">
<router-link
v-for="tag in visitedViews"
ref="tag"
:key="tag.path"
:class="isActive(tag) ? 'active' : ''"
:to="{ path: tag.path, query: tag.query, fullPath: tag.fullPath }"
class="tags-view-item"
>
{{ tag.title }}
<span
class="el-icon-close"
@click.prevent.stop="closeSelectedTag(tag)"
/>
</router-link>
</scroll-pane>
</div>
</template>
<script>
import ScrollPane from './ScrollPane'
export default {
components: { ScrollPane },
name: 'TagesView',
computed: {
visitedViews() {
return this.$store.state.tagsView.visitedViews
}
},
watch: {
$route() {
this.addTags()
this.moveToCurrentTag()
}
},
mounted() {
this.addTags()
},
methods: {
isActive(route) {
return route.path === this.$route.path
},
addTags() {
const { name } = this.$route
if (name) {
this.$store.dispatch('tagsView/addView', this.$route)
}
return false
},
moveToCurrentTag() {
const tags = this.$refs.tag
this.$nextTick(() => {
for (const tag of tags) {
if (tag.to.path === this.$route.path) {
this.$refs.scrollPane.moveToTarget(tag)
// when query is different then update
if (tag.to.fullPath !== this.$route.fullPath) {
this.$store.dispatch('tagsView/updateVisitedView', this.$route)
}
break
}
}
})
},
closeSelectedTag(view) {
this.$store
.dispatch('tagsView/delView', view)
.then(({ visitedViews }) => {
if (this.isActive(view)) {
this.toLastView(visitedViews, view)
}
})
}
}
}
</script>
<style lang="scss" scoped>
.tags-view-container {
height: 40px;
width: 100%;
background: #fff;
.tags-view-wrapper {
.tags-view-item {
display: inline-block;
position: relative;
cursor: pointer;
height: 24px;
line-height: 24px;
border: 1px solid #f4f4f4;
color: rgba(0, 0, 0, 0.45);
background: #f4f4f4;
border-radius: 4px;
padding: 0 8px;
font-size: 12px;
margin-left: 5px;
margin-top: 8px;
&:first-of-type {
margin-left: 32px;
}
&:last-of-type {
margin-right: 15px;
}
&:hover {
color: rgba(89, 89, 89, 1);
background: #efefef;
}
&.active {
background-color: #3e8ef7;
color: #fff;
border-color: #3e8ef7;
}
}
}
}
</style>

70
src/layout/index.vue Normal file
View File

@ -0,0 +1,70 @@
<template>
<el-container class="itemContainer">
<el-aside
width="248px"
style="background-color: rgb(238, 241, 246)"
v-show="activeModule !== 'home'"
>
<menu-list />
</el-aside>
<el-container class="sectionBox">
<el-header>
<navbar />
</el-header>
<el-container>
<div class="app-container">
<tags-view v-show="activeModule !== 'home'" />
<app-main class="app-main" />
</div>
<el-footer height="30px">
<div class="footerFont">©中建材智能自动化研究院有限公司</div>
</el-footer>
</el-container>
</el-container>
</el-container>
</template>
<script>
import { AppMain, MenuList, Navbar } from './components'
import TagsView from './components/tagsView'
import { mapGetters } from 'vuex'
export default {
name: 'LayoutPage',
data() {
return {
sHome: true
}
},
computed: {
...mapGetters(['activeModule'])
},
components: { AppMain, MenuList, Navbar, TagsView }
}
</script>
<style lang="scss" scoped>
@import '@/styles/variables.module.scss';
.itemContainer {
min-width: 1500px;
.el-header {
padding: 0;
}
.sectionBox {
height: 100%;
background-color: $background-color-secondary;
.app-container {
height: calc(100vh - #{$navbarHeight} - #{$footerHeight} - 10px);
margin-top: 7px;
.app-main {
height: calc(100vh - #{$navbarHeight} - #{$footerHeight} - 50px);
}
}
.footerFont {
font-size: 14px;
line-height: $footerHeight;
color: $color-text-secondary;
}
}
.el-footer {
text-align: center;
}
}
</style>

20
src/main.js Normal file
View File

@ -0,0 +1,20 @@
import Vue from 'vue'
import '../theme/index.css' // 自定义主题包
import ElementUI from 'element-ui'
import App from './App.vue'
import router from './router'
import store from './store'
import '@/styles/index.scss' // global css
import './icons' // icon
import './premission' // 路由守卫
Vue.config.productionTip = false
Vue.use(ElementUI)
import './components/index'
new Vue({
router,
store,
render: (h) => h(App)
}).$mount('#app')

30
src/premission.js Normal file
View File

@ -0,0 +1,30 @@
import router from './router'
import NProgress from 'nprogress'
import 'nprogress/nprogress.css'
import { getToken } from './utils/auth'
import store from '@/store'
router.beforeEach(async (to, from, next) => {
NProgress.start()
const hasToken = getToken()
if (hasToken) {
if (to.path === '/login') {
next()
NProgress.done()
} else {
let temp = to.path.split('/')
let activeBox = { activeModule: temp[1], activeMenu: temp[2] }
store.dispatch('menu/setActiveModule', activeBox)
next()
NProgress.done()
}
} else {
if (to.path === '/login') {
next()
NProgress.done()
} else {
next(`/login?redirect=${to.path}`)
NProgress.done()
}
}
})

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

@ -0,0 +1,592 @@
import Vue from 'vue'
import VueRouter from 'vue-router'
// import store from '@/store'
Vue.use(VueRouter)
const originalPush = VueRouter.prototype.push
VueRouter.prototype.push = function push(location) {
return originalPush.call(this, location).catch((err) => err)
}
import Layout from '@/layout'
/**
* hidden: true if set true, item will not show in the top menuModule(default is false)
*/
export const routes = [
{
path: '/login',
name: 'login',
hidden: true,
meta: {
title: '登录'
},
component: () => import('../views/login')
},
{
path: '/home',
component: Layout,
name: 'home',
meta: {
title: '首页'
},
children: [
{
path: 'index',
name: 'index',
meta: {
title: '产线状态'
},
component: () => import('../views/home')
}
]
},
{
path: '/siteMonitoring',
component: Layout,
name: 'siteMonitoring',
meta: {
title: '现场监控'
},
children: [
{
path: 'productionLineStatus',
name: 'productionLineStatus',
meta: {
title: '产线状态'
},
component: () => import('../views/siteMonitoring/productionLineStatus')
},
{
path: 'deviceStatus',
name: 'deviceStatus',
meta: {
title: '设备状态'
},
component: () => import('../views/siteMonitoring/deviceStatus')
}
]
},
{
path: '/productionScheduling',
component: Layout,
name: 'productionScheduling',
meta: {
title: '生产调度'
},
children: [
{
path: 'workOrderProgress',
name: 'workOrderProgress',
meta: {
title: '工单进度'
},
component: () =>
import('../views/productionScheduling/workOrderProgress')
},
{
path: 'workOrderManagement',
name: 'workOrderManagement',
meta: {
title: '工单管理'
},
component: () =>
import('../views/productionScheduling/workOrderManagement')
},
{
path: 'workReportManagement',
name: 'workReportManagement',
meta: {
title: '报工管理'
},
component: () =>
import('../views/productionScheduling/workReportManagement')
},
{
path: 'taskDcheduling',
name: 'taskDcheduling',
meta: {
title: '任务调度'
},
component: () => import('../views/productionScheduling/taskDcheduling')
},
{
path: 'generateReports',
name: 'generateReports',
meta: {
title: '生产报表'
},
component: () =>
import('../views/productionScheduling/generateReports'),
redirect: '/productionScheduling/generateReports/productionReport',
children: [
{
path: 'productionReport',
name: 'productionReport',
meta: {
title: '生产报表'
},
component: () =>
import('../views/productionScheduling/productionReport')
},
{
path: 'substrateGlassQuality',
name: 'substrateGlassQuality',
meta: {
title: '基板玻璃品质'
},
component: () =>
import('../views/productionScheduling/substrateGlassQuality')
},
{
path: 'thicknessDetection',
name: 'thicknessDetection',
meta: {
title: '厚度检测'
},
component: () =>
import('../views/productionScheduling/thicknessDetection')
}
]
},
{
path: 'productionData',
name: 'productionData',
meta: {
title: '生产数据'
},
component: () => import('../views/productionScheduling/productionData')
},
{
path: 'offShelfPackaging',
name: 'offShelfPackaging',
meta: {
title: '下架包装'
},
component: () =>
import('../views/productionScheduling/offShelfPackaging')
},
{
path: 'reLaunching',
name: 'reLaunching',
meta: {
title: '复投上架'
},
component: () => import('../views/productionScheduling/reLaunching')
}
]
},
{
path: '/deviceManagement',
component: Layout,
name: 'deviceManagement',
meta: {
title: '设备管理'
},
children: [
{
path: 'vehicleManagement',
name: 'vehicleManagement',
meta: {
title: '载具管理'
},
component: () => import('../views/deviceManagement/vehicleManagement')
},
{
path: 'performanceAnalysis',
name: 'performanceAnalysis',
meta: {
title: '绩效分析'
},
component: () =>
import('../views/deviceManagement/performanceAnalysis'),
redirect: '/deviceManagement/performanceAnalysis/devicePage',
children: [
{
path: 'devicePage',
name: 'devicePage',
meta: {
title: '设备OEE'
},
component: () =>
import('../views/deviceManagement/performanceAnalysis/devicePage')
},
{
path: 'utilizationRateAnalysis',
name: 'utilizationRateAnalysis',
meta: {
title: '利用率分析'
},
component: () =>
import(
'../views/deviceManagement/performanceAnalysis/utilizationRateAnalysis'
)
},
{
path: 'mtbrf',
name: 'mtbrf',
meta: {
title: 'MTBR-F'
},
component: () =>
import('../views/deviceManagement/performanceAnalysis/mtbrf')
},
{
path: 'analysisOfPallet',
name: 'analysisOfPallet',
meta: {
title: '托盘指标分析'
},
component: () =>
import(
'../views/deviceManagement/performanceAnalysis/analysisOfPallet'
)
}
]
},
{
path: 'spareParts',
name: 'spareParts',
meta: {
title: '备品备件'
},
component: () => import('../views/deviceManagement/spareParts'),
redirect: '/deviceManagement/spareParts/sparePartsStock',
children: [
{
path: 'sparePartsStock',
name: 'sparePartsStock',
meta: {
title: '备件库存'
},
component: () =>
import('../views/deviceManagement/spareParts/sparePartsStock')
},
{
path: 'sparePartsRecord',
name: 'sparePartsRecord',
meta: {
title: '备件记录'
},
component: () =>
import('../views/deviceManagement/spareParts/sparePartsRecord')
}
]
},
{
path: 'maintenanceManagement',
name: 'maintenanceManagement',
meta: {
title: '维护管理'
},
component: () =>
import('../views/deviceManagement/maintenanceManagement'),
redirect: '/deviceManagement/maintenanceManagement/plannedMaintenance',
children: [
{
path: 'plannedMaintenance',
name: 'plannedMaintenance',
meta: {
title: '计划维护'
},
component: () =>
import(
'../views/deviceManagement/maintenanceManagement/plannedMaintenance'
)
},
{
path: 'selfMaintenance',
name: 'selfMaintenance',
meta: {
title: '自主维护'
},
component: () =>
import(
'../views/deviceManagement/maintenanceManagement/selfMaintenance'
)
}
]
},
{
path: 'alarmInformation',
name: 'alarmInformation',
meta: {
title: '报警信息'
},
component: () => import('../views/deviceManagement/alarmInformation'),
redirect: '/deviceManagement/alarmInformation/realTimeAlarm',
children: [
{
path: 'realTimeAlarm',
name: 'realTimeAlarm',
meta: {
title: '实时报警'
},
component: () =>
import('../views/deviceManagement/alarmInformation/realTimeAlarm')
},
{
path: 'historyAlarm',
name: 'historyAlarm',
meta: {
title: '历史报警'
},
component: () =>
import('../views/deviceManagement/alarmInformation/historyAlarm')
}
]
},
{
path: 'deviceRegistration',
name: 'deviceRegistration',
meta: {
title: '设备登记'
},
component: () => import('../views/deviceManagement/deviceRegistration')
}
]
},
{
path: '/consumablesManagement',
component: Layout,
name: 'consumablesManagement',
meta: {
title: '耗材管理'
},
children: [
{
path: 'inAndOutManagement',
name: 'inAndOutManagement',
meta: {
title: '出入库管理'
},
component: () =>
import('../views/consumablesManagement/inAndOutManagement')
},
{
path: 'inAndOutRecords',
name: 'inAndOutRecords',
meta: {
title: '出入库记录'
},
component: () =>
import('../views/consumablesManagement/inAndOutRecords')
},
{
path: 'materialTracking',
name: 'materialTracking',
meta: {
title: '在线耗材跟踪'
},
component: () =>
import('../views/consumablesManagement/materialTracking')
}
]
},
{
path: '/processManagement',
component: Layout,
name: 'processManagement',
meta: {
title: '工艺管理'
},
children: [
{
path: 'processMonitoring',
name: 'processMonitoring',
meta: {
title: '工艺监测'
},
component: () => import('../views/processManagement/processMonitoring')
},
{
path: 'abnormalAlert',
name: 'abnormalAlert',
meta: {
title: '异常警示'
},
component: () => import('../views/processManagement/abnormalAlert')
},
{
path: 'processConfiguration',
name: 'processConfiguration',
meta: {
title: '工艺配置'
},
component: () =>
import('../views/processManagement/processConfiguration')
}
]
},
{
path: '/qualityManagement',
component: Layout,
name: 'qualityManagement',
meta: {
title: '质量管理'
},
children: [
{
path: 'processSampling',
name: 'processSampling',
meta: {
title: '过程抽检'
},
component: () => import('../views/qualityManagement/processSampling')
},
{
path: 'processFullInspection',
name: 'processFullInspection',
meta: {
title: '过程全检'
},
component: () =>
import('../views/qualityManagement/processFullInspection'),
redirect: '/qualityManagement/processFullInspection/fullInspection',
children: [
{
path: 'fullInspection',
name: 'fullInspection',
meta: {
title: '过程全检'
},
component: () =>
import(
'../views/qualityManagement/processFullInspection/fullInspection'
)
},
{
path: 'defectScatterPlot',
name: 'defectScatterPlot',
meta: {
title: '缺陷散点图'
},
component: () =>
import(
'../views/qualityManagement/processFullInspection/defectScatterPlot'
)
},
{
path: 'particleLineChart',
name: 'particleLineChart',
meta: {
title: '颗粒折线图'
},
component: () =>
import(
'../views/qualityManagement/processFullInspection/particleLineChart'
)
},
{
path: 'thicknessDistributionMap',
name: 'thicknessDistributionMap',
meta: {
title: '厚度分布图'
},
component: () =>
import(
'../views/qualityManagement/processFullInspection/thicknessDistributionMap'
)
}
]
},
{
path: 'statisticalAnalysis',
name: 'statisticalAnalysis',
meta: {
title: '统计分析'
},
component: () =>
import('../views/qualityManagement/statisticalAnalysis')
},
{
path: 'productTraceability',
name: 'productTraceability',
meta: {
title: '产品追溯'
},
component: () =>
import('../views/qualityManagement/productTraceability')
},
{
path: 'finalInspectionData',
name: 'finalInspectionData',
meta: {
title: '终检数据'
},
component: () =>
import('../views/qualityManagement/finalInspectionData')
},
{
path: 'finalInspectionDailyReport',
name: 'finalInspectionDailyReport',
meta: {
title: '终检日报表'
},
component: () =>
import('../views/qualityManagement/finalInspectionDailyReport')
}
]
},
{
path: '/basicConfig',
component: Layout,
name: 'basicConfig',
meta: {
title: '基础配置'
},
children: [
{
path: 'accountConfig',
name: 'accountConfig',
meta: {
title: '账号配置'
},
component: () => import('../views/basicConfig/accountConfig')
},
{
path: 'sparePartsConfig',
name: 'sparePartsConfig',
meta: {
title: '备件配置'
},
component: () => import('../views/basicConfig/sparePartsConfig')
},
{
path: 'consumablesConfig',
name: 'consumablesConfig',
meta: {
title: '耗材配置'
},
component: () => import('../views/basicConfig/consumablesConfig')
},
{
path: 'lineSideLibraryConfig',
name: 'lineSideLibraryConfig',
meta: {
title: '线边库配置'
},
component: () => import('../views/basicConfig/lineSideLibraryConfig')
},
{
path: 'deviceConfig',
name: 'deviceConfig',
meta: {
title: '设备配置'
},
component: () => import('../views/basicConfig/deviceConfig')
}
]
},
{ path: '*', redirect: '/home/index', hidden: true }
]
const router = new VueRouter({
mode: 'hash',
base: process.env.BASE_URL,
routes
})
export default router

72
src/settings.js Normal file
View File

@ -0,0 +1,72 @@
// TFT项目的一些配置比如角色等
module.exports = {
roleList: [
{ id: '1', name: '加工人员' },
{ id: '2', name: '质检人员' }
],
productionLineList: [
{ id: '1', name: '产线1' },
{ id: '2', name: '产线2' }
],
sparePartsTypeList: [
{ id: '1', name: '边磨备品备件' },
{ id: '2', name: '清洗机备品备件' },
{ id: '3', name: '前清洗备品备件' },
{ id: '4', name: '后清洗备品备件' },
{ id: '5', name: '面磨区备品备件' },
{ id: '6', name: '终检区备品备件' },
{ id: '7', name: '动力区备品备件' },
{ id: '8', name: '辅材备品备件' }
],
statusList: [
{ id: '1', name: '正常' },
{ id: '2', name: '禁用' }
],
unitModuleList: [
{ id: '1', name: '精切磨边' },
{ id: '2', name: '磨边后清洗' },
{ id: '3', name: '边检' },
{ id: '4', name: '面研磨' },
{ id: '5', name: '面研磨辅助设备' },
{ id: '6', name: '面研磨后清洗' },
{ id: '7', name: '终检' },
{ id: '8', name: '内包区' },
{ id: '9', name: '外包区' }
],
testItemList: [
{ id: '1', name: '研磨幅' },
{ id: '2', name: '外观尺寸' },
{ id: '3', name: '角尺寸' },
{ id: '4', name: '直线性' },
{ id: '5', name: '翘曲' },
{ id: '6', name: '成品厚度' },
{ id: '7', name: '应力' },
{ id: '8', name: '条纹/磨痕度数' }
],
orderTypeList: [
{ id: '1', name: '全部' },
{ id: '2', name: '正常' },
{ id: '3', name: '待再加工' }
],
orderStatusList: [
{ id: '1', name: '全部' },
{ id: '2', name: '已下发' },
{ id: '3', name: '未下发' },
{ id: '4', name: '已结束' }
],
planTypeList: [
{ id: '1', name: '全部' },
{ id: '2', name: '年计划' },
{ id: '3', name: '月计划' },
{ id: '4', name: '日计划' }
],
regrindList: [
{ id: '1', name: '无' },
{ id: '2', name: '锡面' },
{ id: '3', name: '空气面' }
],
productionSpecList: [
{ id: '1', name: 'G8.5' },
{ id: '2', name: 'G8.6' }
]
}

12
src/store/getters.js Normal file
View File

@ -0,0 +1,12 @@
const getters = {
topModuleList: (state) => state.menu.topModuleList,
leftMenuList: (state) => state.menu.leftMenuList,
activeModule: (state) => state.menu.activeModule,
activeMenu: (state) => state.menu.activeMenu,
token: (state) => state.user.token,
visitedViews: (state) => state.tagsView.visitedViews,
cachedViews: (state) => state.tagsView.cachedViews,
username: (state) => state.user.username,
roles: (state) => state.user.roles
}
export default getters

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

@ -0,0 +1,24 @@
import Vue from 'vue'
import Vuex from 'vuex'
import getters from './getters'
Vue.use(Vuex)
const modulesFiles = require.context('./modules', true, /\.js$/)
// you do not need `import app from './modules/app'`
// it will auto require all vuex module from modules file
const modules = modulesFiles.keys().reduce((modules, modulePath) => {
// set './app.js' => 'app'
const moduleName = modulePath.replace(/^\.\/(.*)\.\w+$/, '$1')
const value = modulesFiles(modulePath)
modules[moduleName] = value.default
return modules
}, {})
const store = new Vuex.Store({
modules,
getters
})
export default store

80
src/store/modules/menu.js Normal file
View File

@ -0,0 +1,80 @@
import { routes } from '@/router'
const state = {
topModuleList:
sessionStorage.getItem('topModuleList') &&
sessionStorage.getItem('topModuleList') !== 'undefined'
? JSON.parse(sessionStorage.getItem('topModuleList'))
: [],
leftMenuList:
sessionStorage.getItem('leftMenuList') &&
sessionStorage.getItem('leftMenuList') !== 'undefined'
? JSON.parse(sessionStorage.getItem('leftMenuList'))
: [],
activeModule: sessionStorage.getItem('activeModule'), // 顶部菜单模块
activeMenu: sessionStorage.getItem('activeMenu') // 左侧菜单
}
const mutations = {
SET_TOPMODULE: (state, menuModule) => {
state.topModuleList = menuModule
sessionStorage.setItem('topModuleList', JSON.stringify(menuModule))
},
SET_LEFTMENU: (state, menu) => {
state.leftMenuList = menu
sessionStorage.setItem('leftMenuList', JSON.stringify(menu))
},
SET_ACTIVEMODULE: (state, activeModule) => {
state.activeModule = activeModule
sessionStorage.setItem('activeModule', activeModule)
},
SET_ACTIVEMENU: (state, activeMenu) => {
state.activeMenu =
activeMenu || (state.leftMenuList[0] ? state.leftMenuList[0].name : '')
sessionStorage.setItem('activeMenu', state.activeMenu)
}
}
const actions = {
// 获取顶部菜单模块
getTopModuleList({ commit }) {
let temp = []
routes &&
routes.map((item) => {
if (item.hidden) {
return true
}
let obj = {}
obj.name = item.name
obj.label = item.meta.title
temp.push(obj)
})
commit('SET_TOPMODULE', temp)
},
// 设置菜单模块
setActiveModule({ commit, dispatch }, activeBox) {
const { activeModule } = activeBox
dispatch('getLeftMenuList', activeBox)
commit('SET_ACTIVEMODULE', activeModule)
},
// 获取左侧菜单
getLeftMenuList({ commit, dispatch }, activeBox) {
const { activeModule, activeMenu } = activeBox
let temp = []
for (let i = 0; i < routes.length; i++) {
if (activeModule === routes[i].name) {
temp = routes[i].children
}
}
commit('SET_LEFTMENU', temp)
dispatch('setActiveMenu', activeMenu)
},
setActiveMenu({ commit }, activeMenu) {
commit('SET_ACTIVEMENU', activeMenu)
}
}
export default {
namespaced: true,
state,
mutations,
actions
}

View File

@ -0,0 +1,76 @@
const state = {
visitedViews: [],
cachedViews: []
}
const mutations = {
ADD_VISITED_VIEW: (state, view) => {
if (state.visitedViews.some((v) => v.path === view.path)) return
state.visitedViews.push(
Object.assign({}, view, {
title: view.meta.title || 'no-name'
})
)
},
ADD_CACHED_VIEW: (state, view) => {
if (state.cachedViews.includes(view.name)) return
if (!view.meta.noCache) {
state.cachedViews.push(view.name)
}
},
DEL_VISITED_VIEW: (state, view) => {
for (const [i, v] of state.visitedViews.entries()) {
if (v.path === view.path) {
state.visitedViews.splice(i, 1)
break
}
}
},
DEL_CACHED_VIEW: (state, view) => {
const index = state.cachedViews.indexOf(view.name)
index > -1 && state.cachedViews.splice(index, 1)
}
}
const actions = {
addView({ dispatch }, view) {
dispatch('addVisitedView', view)
dispatch('addCachedView', view)
},
addVisitedView({ commit }, view) {
commit('ADD_VISITED_VIEW', view)
},
addCachedView({ commit }, view) {
commit('ADD_CACHED_VIEW', view)
},
delView({ dispatch, state }, view) {
return new Promise((resolve) => {
dispatch('delVisitedView', view)
dispatch('delCachedView', view)
resolve({
visitedViews: [...state.visitedViews],
cachedViews: [...state.cachedViews]
})
})
},
delVisitedView({ commit, state }, view) {
return new Promise((resolve) => {
commit('DEL_VISITED_VIEW', view)
resolve([...state.visitedViews])
})
},
delCachedView({ commit, state }, view) {
return new Promise((resolve) => {
commit('DEL_CACHED_VIEW', view)
resolve([...state.cachedViews])
})
}
}
export default {
namespaced: true,
state,
mutations,
actions
}

121
src/store/modules/user.js Normal file
View File

@ -0,0 +1,121 @@
import { login, getUserInfo } from '@/api/user'
import { getToken, setToken, removeToken } from '@/utils/auth'
import Cookies from 'js-cookie'
const state = {
token: getToken(),
name: '',
avatar: '',
introduction: '',
roles: JSON.parse(localStorage.getItem('roles')),
menus: [],
username: Cookies.get('username') || ''
}
const mutations = {
SET_TOKEN: (state, token) => {
state.token = token
},
SET_USERNAME: (state, username) => {
state.username = username
},
SET_INTRODUCTION: (state, introduction) => {
state.introduction = introduction
},
SET_NAME: (state, name) => {
state.name = name
},
SET_AVATAR: (state, avatar) => {
state.avatar = avatar
},
SET_MENUS: (state, menus) => {
state.menus = menus
},
SET_ROLES: (state, roles) => {
state.roles = roles
}
}
const actions = {
// user login
login({ commit }, userInfo) {
const { mobile, password } = userInfo
return new Promise((resolve, reject) => {
login({ mobile: mobile.trim(), password: password })
.then((res) => {
console.log(res)
if (res.code === 0) {
const { token, name } = res.data
commit('SET_TOKEN', token)
commit('SET_USERNAME', name)
Cookies.set('username', name)
setToken(token)
resolve()
} else {
reject(res)
}
})
.catch((error) => {
reject(error)
})
})
},
// get user info
getInfo({ commit }) {
return new Promise((resolve, reject) => {
getUserInfo()
.then((res) => {
console.log(res)
const { data } = res
if (!data) {
reject('获取用户信息失败,重新登录')
}
// const { roleName, name, avatar, introduction, menus } = data
const { role } = data
// // roles must be a non-empty array
// if (!menus || menus.length <= 0) {
// reject('getInfo: menus must be a non-null array!')
// }
commit('SET_ROLES', role || '')
localStorage.setItem('roles', JSON.stringify(role))
// commit('SET_NAME', name)
// commit('SET_AVATAR', avatar)
// commit('SET_MENUS', menus)
// commit('SET_INTRODUCTION', introduction)
resolve(data)
})
.catch((error) => {
reject(error)
})
})
},
// user logout
logout() {
return new Promise((resolve) => {
removeToken()
// dispatch('tagsView/delAllViews', null, { root: true })
// commit('SET_TOKEN', '')
// commit('SET_ROLES', [])
// commit('SET_MENUS', [])
// commit('SET_USERNAME')
// Cookies.set('username', '')
// localStorage.clear()
resolve()
})
}
// remove token
// resetToken({ commit }) {
// return new Promise((resolve) => {
// commit('SET_TOKEN', '')
// commit('SET_ROLES', [])
// removeToken()
// resolve()
// })
// }
}
export default {
namespaced: true,
state,
mutations,
actions
}

47
src/styles/common.scss Normal file
View File

@ -0,0 +1,47 @@
.app-container {
.app-main {
.page-box { // 一般常规的表格页面
margin: 8px 16px 0px;
padding: 12px 16px;
height: calc(100vh - #{$navbarHeight} - #{$footerHeight} - 58px);
background-color: #fff;
border-radius: 8px;
}
}
}
// radio按钮组背景色
.el-radio-button__inner {
width: 90px;
height: 30px;
padding-top: 8px;
padding-bottom: 5px;
background-color: rgba(242, 244, 249, 1);
border-top: 1px solid rgba(242, 244, 249, 1);
border-bottom: 1px solid rgba(242, 244, 249, 1);
border-left: none !important;
border-right: none !important;
}
// 抽屉head区域---start
.el-drawer__header {
padding-bottom: 20px;
margin-bottom: 23px;
font-size: 20px;
font-weight: 500;
color: rgba(0,0,0,0.85);
border-bottom: 1px solid rgba(233, 233, 233, 1);
}
.el-drawer__header > :first-child::before {
content: '';
display: inline-block;
width: 4px;
height: 24px;
background-color: #0b58ff;
margin-right: 8px;
vertical-align: middle;
}
// 抽屉head区域---end
// 表格红色报警
.el-table .danger-row {
background: #FEE1E1;
}

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

@ -0,0 +1,103 @@
@import './variables.module.scss';
@import './common.scss';
// css reset
html {
height: 100%;
box-sizing: border-box;
}
body {
height: 100%;
margin: 0;
-moz-osx-font-smoothing: grayscale;
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility;
font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, Arial, sans-serif;
}
#app {
height: 100%;
color: $color-text-primary;
}
ul,li {
list-style-type: none;
padding: 0;
margin: 0;
}
*,
*:before,
*:after {
box-sizing: inherit;
}
a:focus,
a:active {
outline: none;
}
a,
a:focus,
a:hover {
cursor: pointer;
color: inherit;
text-decoration: none;
}
p {
margin: 0;
}
.fr {
float: right;
}
.fl {
float: left;
}
.clearfix {
&:after {
visibility: hidden;
display: block;
font-size: 0;
content: " ";
clear: both;
height: 0;
}
}
// global style
::-webkit-scrollbar {
width: 8px;
height: 8px;
background-color: transparent;
}
::-webkit-scrollbar-track-piece {
background-color:rgba(144,147,153,0);
}
::-webkit-scrollbar-corner {
background-color:rgba(144,147,153,0);
}
::-webkit-scrollbar-track {
width: 6px;
background: rgba(144,147,153,0);
-webkit-border-radius: 2em;
-moz-border-radius: 2em;
border-radius: 2em;
}
::-webkit-scrollbar-thumb {
background-color: rgba(144,147,153,.5);
background-clip: padding-box;
min-height: 28px;
-webkit-border-radius: 2em;
-moz-border-radius: 2em;
border-radius: 2em;
transition: background-color .3s;
cursor: pointer;
}
::-webkit-scrollbar-thumb:hover {
background-color: rgba(144,147,153,.3);
}

View File

@ -0,0 +1,55 @@
// color
// Brand Color
$color-primary: #0B58FF;
// Functional Color
$color-success: #34E88E;
$color-warning: #FFBD02;
$color-danger: #FF5454;
$color-info: #909399;
// Font Color
$color-text-primary: #161616;
$color-text-regular: #606266;
$color-text-secondary: #909399;
$color-text-placeholder: #C0C4CC;
// Border Color
$border-color-base: #DCDFE6;
$border-color-light: #E4E7ED;
$border-color-lighter: #EBEEF5;
$border-color-extra-light: #F2F6FC;
// Background Color
$color-white: #FFFFFF;
$color-black: #000000;
$background-color-base: #F0F2F5;
$background-color-secondary: #F2F4F9;
// mix color-primary
$color-primary-light-1: mix($color-white, $color-primary, 10%) !default;
$color-primary-light-2: mix($color-white, $color-primary, 20%) !default;
$color-primary-light-3: mix($color-white, $color-primary, 30%) !default;
$color-primary-light-4: mix($color-white, $color-primary, 40%) !default;
$color-primary-light-5: mix($color-white, $color-primary, 50%) !default;
$color-primary-light-6: mix($color-white, $color-primary, 60%) !default;
$color-primary-light-7: mix($color-white, $color-primary, 70%) !default;
$color-primary-light-8: mix($color-white, $color-primary, 80%) !default;
$color-primary-light-9: mix($color-white, $color-primary, 90%) !default;
// mix color-success
$color-success-light-1: mix($color-white, $color-success, 10%) !default;
$color-success-light-2: mix($color-white, $color-success, 20%) !default;
$color-success-light-3: mix($color-white, $color-success, 30%) !default;
$color-success-light-4: mix($color-white, $color-success, 40%) !default;
$color-success-light-5: mix($color-white, $color-success, 50%) !default;
$color-success-light-6: mix($color-white, $color-success, 60%) !default;
$color-success-light-7: mix($color-white, $color-success, 70%) !default;
$color-success-light-8: mix($color-white, $color-success, 80%) !default;
$color-success-light-9: mix($color-white, $color-success, 90%) !default;
// size
$navbarHeight: 60px; // 导航栏高度
$menuWidth: 160px;
$footerHeight: 30px;
:export {
navbarHeight: $navbarHeight;
menuWidth: $menuWidth;
footerHeight: $footerHeight;
}

15
src/utils/auth.js Normal file
View File

@ -0,0 +1,15 @@
import Cookies from 'js-cookie'
const TokenKey = 'Admin-Token'
export function getToken() {
return Cookies.get(TokenKey)
}
export function setToken(token) {
return Cookies.set(TokenKey, token)
}
export function removeToken() {
return Cookies.remove(TokenKey)
}

View File

@ -0,0 +1,64 @@
import { debounce } from '@/utils/debounce'
export default {
data() {
return {
$_sidebarElm: null,
$_resizeHandler: null
}
},
mounted() {
this.$_resizeHandler = debounce(() => {
if (this.chart) {
this.chart.resize()
}
}, 100)
this.$_initResizeEvent()
this.$_initSidebarResizeEvent()
},
beforeDestroy() {
this.$_destroyResizeEvent()
this.$_destroySidebarResizeEvent()
},
// to fixed bug when cached by keep-alive
// https://github.com/PanJiaChen/vue-element-admin/issues/2116
activated() {
this.$_initResizeEvent()
this.$_initSidebarResizeEvent()
},
deactivated() {
this.$_destroyResizeEvent()
this.$_destroySidebarResizeEvent()
},
methods: {
// use $_ for mixins properties
// https://vuejs.org/v2/style-guide/index.html#Private-property-names-essential
$_initResizeEvent() {
window.addEventListener('resize', this.$_resizeHandler)
},
$_destroyResizeEvent() {
window.removeEventListener('resize', this.$_resizeHandler)
},
$_sidebarResizeHandler(e) {
if (e.propertyName === 'width') {
this.$_resizeHandler()
}
},
$_initSidebarResizeEvent() {
this.$_sidebarElm =
document.getElementsByClassName('sidebar-container')[0]
this.$_sidebarElm &&
this.$_sidebarElm.addEventListener(
'transitionend',
this.$_sidebarResizeHandler
)
},
$_destroySidebarResizeEvent() {
this.$_sidebarElm &&
this.$_sidebarElm.removeEventListener(
'transitionend',
this.$_sidebarResizeHandler
)
}
}
}

40
src/utils/debounce.js Normal file
View File

@ -0,0 +1,40 @@
/**
* @param {Function} func
* @param {number} wait
* @param {boolean} immediate
* @return {*}
*/
export function debounce(func, wait, immediate) {
let timeout, args, context, timestamp, result
const later = function () {
// 据上一次触发时间间隔
const last = +new Date() - timestamp
// 上次被包装函数被调用时间间隔 last 小于设定时间间隔 wait
if (last < wait && last > 0) {
timeout = setTimeout(later, wait - last)
} else {
timeout = null
// 如果设定为immediate===true因为开始边界已经调用过了此处无需调用
if (!immediate) {
result = func.apply(context, args)
if (!timeout) context = args = null
}
}
}
return function (...args) {
context = this
timestamp = +new Date()
const callNow = immediate && !timeout
// 如果延时不存在,重新设定延时
if (!timeout) timeout = setTimeout(later, wait)
if (callNow) {
result = func.apply(context, args)
context = args = null
}
return result
}
}

8
src/utils/dict.js Normal file
View File

@ -0,0 +1,8 @@
// 前端写死的list
export default {
packageState: [
{ dataCode: 0, dataName: '包装中' },
{ dataCode: 1, dataName: '复投中' },
{ dataCode: 2, dataName: '已下架' }
]
}

53
src/utils/index.js Normal file
View File

@ -0,0 +1,53 @@
import moment from 'moment'
import dictionaryList from './dict'
// tableHeight
export function tableHeight(n) {
return window.innerHeight - n
}
// table中用来过滤时间
export function timeFormatter(timeObj) {
if (timeObj) {
return moment(timeObj).format('YYYY-MM-DD HH:mm:ss')
} else {
return '-'
}
}
// table中用来过滤字典
const publicList = JSON.parse(localStorage.getItem('publicList'))
export function publicFormatter(dictTable) {
return function (val) {
const arr = {}
const dicList = publicList?.[dictTable]
dicList.map((item) => {
arr[item.dataCode] = item.dataName
})
return arr?.[val]
}
}
export function dictionaryFormatter(dictTable) {
return function (val) {
const arr = {}
const dictList = dictionaryList?.[dictTable]
dictList.map((item) => {
arr[item.dataCode] = item.dataName
})
return arr?.[val]
}
}
// table中用来过滤金额
export function amountFormatter(param) {
if (param) {
return parseFloat(param).toFixed(2)
} else {
return '0.00'
}
}
// svgicon
export function isExternal(path) {
return /^(https?:|mailto:|tel:)/.test(path)
}

View File

@ -0,0 +1,224 @@
<template>
<div class="page-box">
<search-bar
:formConfigs="formConfig"
ref="searchBarForm"
@headBtnClick="buttonClick"
/>
<base-table
:page="listQuery.current"
:limit="listQuery.size"
:table-props="tableProps"
:table-data="tableData"
:max-height="tableH"
>
<method-btn
v-if="tableBtn.length"
slot="handleBtn"
:width="80"
label="操作"
:method-list="tableBtn"
@clickBtn="handleClick"
/>
</base-table>
<pagination
:page.sync="listQuery.current"
:limit.sync="listQuery.size"
:total="total"
@pagination="getList()"
/>
<!-- 新增 -->
<base-dialog
:title="addOrEditTitle"
:dialogVisible="centervisible"
@cancel="handleCancel"
@confirm="handleConfirm"
:before-close="handleCancel"
>
<account-add ref="accountList" @successSubmit="successSubmit" />
</base-dialog>
</div>
</template>
<script>
import { tableHeight } from '@/utils/index'
import AccountAdd from './components/accountAdd.vue'
import statusTag from './components/statusTag.vue'
import { getAccountPage } from '@/api/basicConfig'
const tableProps = [
{
prop: 'account',
label: '用户名'
},
{
prop: 'name',
label: '姓名'
},
{
prop: 'role',
label: '角色'
},
{
prop: 'proLineName',
label: '所属产线'
},
{
prop: 'enabled',
label: '状态',
width: 120,
subcomponent: statusTag
}
]
const tableBtn = [
{
type: 'edit',
btnName: '编辑'
}
]
export default {
name: 'AccountConfig',
components: { AccountAdd },
data() {
return {
formConfig: [
{
type: 'select',
label: '产线',
selectOptions: JSON.parse(localStorage.getItem('publicList'))
.proLineVoList,
param: 'proLineId',
defaultSelect: '',
width: 120
},
{
type: 'select',
label: '角色',
selectOptions: JSON.parse(localStorage.getItem('publicList'))
.roleVoList,
labelField: 'dataName',
valueField: 'dataName',
param: 'role',
defaultSelect: '',
width: 150
},
{
type: 'input',
label: '用户名',
placeholder: '用户名',
param: 'account'
},
{
type: 'input',
label: '姓名',
placeholder: '姓名',
param: 'name',
width: 150
},
{
type: 'select',
label: '状态',
selectOptions: JSON.parse(localStorage.getItem('publicList'))
.enabledVoList,
labelField: 'dataName',
valueField: 'dataCode',
param: 'enabled',
defaultSelect: '',
width: 120
},
{
type: 'button',
btnName: '查询',
name: 'search',
color: 'primary'
},
{
type: 'button',
btnName: '重置',
name: 'reset'
},
{
type: 'button',
btnName: '新增',
name: 'add',
color: 'primary',
plain: true
}
],
tableProps,
tableData: [],
tableBtn,
tableH: tableHeight(265),
total: 0,
listQuery: {
current: 1,
size: 20
},
centervisible: false,
addOrEditTitle: '' //title
}
},
mounted() {
window.addEventListener('resize', () => {
this.tableH = tableHeight(265)
})
this.getList()
},
methods: {
getList() {
getAccountPage({ ...this.listQuery }).then((res) => {
if (res.code === 0) {
this.total = res.data.total
this.tableData = res.data.records
}
})
},
handleClick(val) {
this.addOrEditTitle = '编辑'
this.$nextTick(() => {
this.$refs.accountList.init(val.data.id)
})
this.centervisible = true
},
buttonClick(val) {
switch (val.btnName) {
case 'search':
this.listQuery.proLineId = val.proLineId
this.listQuery.role = val.role
this.listQuery.account = val.account
this.listQuery.name = val.name
this.listQuery.enabled = val.enabled
this.listQuery.current = 1
this.getList()
break
case 'reset':
this.$refs.searchBarForm.resetForm()
this.listQuery.proLineId = ''
this.listQuery.role = ''
this.listQuery.account = ''
this.listQuery.name = ''
this.listQuery.enabled = null
this.listQuery.current = 1
this.getList()
break
default:
this.addOrEditTitle = '新增'
this.centervisible = true
this.$nextTick(() => {
this.$refs.accountList.init()
})
}
},
handleCancel() {
this.$refs.accountList.formClear()
this.centervisible = false
this.addOrEditTitle = ''
},
handleConfirm() {
this.$refs.accountList.submitForm()
},
successSubmit() {
this.handleCancel()
this.getList()
}
}
}
</script>

View File

@ -0,0 +1,147 @@
<template>
<el-form ref="form" :rules="rules" label-width="80px" :model="form">
<el-form-item label="用户名" prop="account">
<el-input
v-model="form.account"
onkeyup="value=value.replace(/[\W]/g,'')"
placeholder="只能输入字母或数字,不可输入汉字"
:disabled="isEdit"
></el-input>
</el-form-item>
<el-form-item label="密码" prop="password">
<el-input v-model="form.password"></el-input>
</el-form-item>
<el-form-item label="姓名" prop="name">
<el-input v-model="form.name"></el-input>
</el-form-item>
<el-form-item label="所属产线" prop="proLineId">
<el-select
v-model="form.proLineId"
placeholder="所属产线"
style="width: 100%"
>
<el-option
v-for="(item, i) in lineList"
:key="i"
:label="item.name"
:value="item.id"
></el-option>
</el-select>
</el-form-item>
<el-form-item label="角色" prop="role">
<el-select v-model="form.role" placeholder="角色" style="width: 100%">
<el-option
v-for="(item, i) in roleList"
:key="i"
:label="item.dataName"
:value="item.dataName"
></el-option>
</el-select>
</el-form-item>
<el-form-item label="状态" prop="enabled" v-show="isEdit">
<el-select v-model="form.enabled" placeholder="状态" style="width: 100%">
<el-option
v-for="(item, i) in stateList"
:key="i"
:label="item.dataName"
:value="item.dataCode"
></el-option>
</el-select>
</el-form-item>
</el-form>
</template>
<script>
import { addAccount, getAccount, accountUpdate } from '@/api/basicConfig'
export default {
name: 'AccountAdd',
data() {
return {
form: {
id: '',
account: '',
password: '123456',
name: '',
proLineId: '',
role: '',
enabled: 1
},
isEdit: false, //
lineList: JSON.parse(localStorage.getItem('publicList')).proLineVoList,
roleList: JSON.parse(localStorage.getItem('publicList')).roleVoList,
stateList: JSON.parse(localStorage.getItem('publicList')).enabledVoList,
rules: {
account: [
{ required: true, message: '请输入用户名', trigger: 'blur' },
{ min: 3, max: 18, message: '长度在 3 到 18 个字符', trigger: 'blur' }
],
password: [{ required: true, message: '请输入密码', trigger: 'blur' }],
name: [
{ required: true, message: '请输入姓名', trigger: 'blur' },
{ min: 3, max: 18, message: '长度在 3 到 18 个字符', trigger: 'blur' }
],
proLineId: [
{ required: true, message: '请选择所属产线', trigger: 'change' }
],
role: [{ required: true, message: '请选择角色', trigger: 'change' }]
}
}
},
methods: {
init(id) {
if (id) {
this.isEdit = true
this.form.id = id
getAccount({ id }).then((res) => {
if (res.code === 0) {
this.form.account = res.data.account
this.form.password = res.data.password
this.form.name = res.data.name
this.form.proLineId = res.data.proLineId
this.form.role = res.data.role
this.form.enabled = res.data.enabled
}
})
} else {
this.isEdit = false
this.form.id = ''
}
},
submitForm() {
this.$refs['form'].validate((valid) => {
if (valid) {
if (this.isEdit) {
//
accountUpdate({ ...this.form }).then((res) => {
if (res.code === 0) {
this.$message({
message: '操作成功',
type: 'success',
duration: 1500
})
this.$emit('successSubmit')
}
})
} else {
addAccount({ ...this.form }).then((res) => {
if (res.code === 0) {
this.$message({
message: '操作成功',
type: 'success',
duration: 1500
})
this.$emit('successSubmit')
}
})
}
} else {
return false
}
})
},
formClear() {
this.$refs.form.resetFields()
this.isEdit = false
}
}
}
</script>

View File

@ -0,0 +1,126 @@
<template>
<el-form ref="form" :rules="rules" label-width="100px" :model="form">
<el-form-item label="耗材名称" prop="name">
<el-input v-model="form.name" placeholder="耗材名称"></el-input>
</el-form-item>
<el-form-item label="耗材规格" prop="spec">
<el-input v-model="form.spec" placeholder="耗材规格"></el-input>
</el-form-item>
<el-form-item label="耗材单位" prop="unit">
<el-input v-model="form.unit" placeholder="耗材单位"></el-input>
</el-form-item>
<el-form-item label="基础消耗量" prop="basicConsume">
<el-input-number
v-model="form.basicConsume"
:min="0"
:max="999999999"
:precision="2"
:step="1"
style="width: 100%"
></el-input-number>
</el-form-item>
<el-form-item label="状态" prop="enabled" v-show="isEdit">
<el-select v-model="form.enabled" placeholder="状态" style="width: 100%">
<el-option
v-for="(item, i) in stateList"
:key="i"
:label="item.dataName"
:value="item.dataCode"
></el-option>
</el-select>
</el-form-item>
</el-form>
</template>
<script>
import {
addConsumable,
getConsumable,
consumableUpdate
} from '@/api/basicConfig'
export default {
name: 'ConsumablesAdd',
data() {
return {
form: {
id: '',
name: '',
spec: '',
unit: '',
basicConsume: '',
enabled: 1
},
rules: {
name: [{ required: true, message: '请输入耗材名称', trigger: 'blur' }],
spec: [{ required: true, message: '请输入耗材规格', trigger: 'blur' }],
unit: [{ required: true, message: '请输入耗材单位', trigger: 'blur' }],
basicConsume: [
{ required: true, message: '请输入基础消耗量', trigger: 'blur' }
]
},
stateList: JSON.parse(localStorage.getItem('publicList')).enabledVoList,
isEdit: false
}
},
methods: {
init(val) {
if (val) {
this.form.id = val.id
getConsumable({ id: val.id }).then((res) => {
if (res.code === 0) {
console.log(res)
this.form.name = res.data.name
this.form.spec = res.data.spec
this.form.unit = res.data.unit
this.form.basicConsume = res.data.basicConsume
this.form.enabled = res.data.enabled
}
})
if (val.type === 'edit') {
this.isEdit = true
} else {
this.isEdit = false
this.form.id = ''
}
} else {
this.isEdit = false
this.form.id = ''
}
},
submitForm() {
this.$refs['form'].validate((valid) => {
if (valid) {
if (this.isEdit) {
consumableUpdate({ ...this.form }).then((res) => {
if (res.code === 0) {
this.$message({
message: '操作成功',
type: 'success',
duration: 1500
})
this.$emit('successSubmit')
}
})
} else {
addConsumable({ ...this.form }).then((res) => {
if (res.code === 0) {
this.$message({
message: '操作成功',
type: 'success',
duration: 1500
})
this.$emit('successSubmit')
}
})
}
} else {
return false
}
})
},
formClear() {
this.$refs.form.resetFields()
this.isEdit = false
}
}
}
</script>

View File

@ -0,0 +1,379 @@
<template>
<el-form ref="form" :rules="rules" label-width="100px" :model="form">
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="产线" prop="proLineId">
<el-select
v-model="form.proLineId"
placeholder="产线"
style="width: 100%"
@change="getUnitModule"
>
<el-option
v-for="item in productionLineList"
:key="item.id"
:label="item.name"
:value="item.id"
></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="单元" prop="unitId">
<el-select
v-model="form.unitId"
placeholder="单元"
style="width: 100%"
@change="getEqCode"
>
<el-option
v-for="item in unitModuleList"
:key="item.id"
:label="item.name"
:value="item.id"
></el-option>
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="设备名称" prop="name">
<el-input v-model="form.name" placeholder="设备名称"></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="设备规格" prop="spec">
<el-input v-model="form.spec" placeholder="设备规格"></el-input>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="设备编码" prop="code">
<el-input
v-model="form.code"
placeholder="设备编码"
disabled
></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="基础寿命" prop="baseLife">
<el-input-number
v-model="form.baseLife"
placeholder="基础寿命"
:min="0"
:max="999999999"
:precision="2"
style="width: 100%"
></el-input-number>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="购买价格/万元" prop="price">
<el-input-number
v-model="form.price"
placeholder="购买价格/万元"
:min="0"
:max="999999999"
:precision="2"
style="width: 100%"
></el-input-number>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="供应商" prop="supplier">
<el-input v-model="form.supplier" placeholder="供应商"></el-input>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="采购日期" prop="purchaseDate">
<el-date-picker
v-model="form.purchaseDate"
type="date"
format="yyyy-MM-dd"
value-format="yyyy-MM-dd"
placeholder="采购日期"
style="width: 100%"
>
</el-date-picker>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="生产日期" prop="productDate">
<el-date-picker
v-model="form.productDate"
type="date"
placeholder="生产日期"
format="yyyy-MM-dd"
value-format="yyyy-MM-dd"
style="width: 100%"
>
</el-date-picker>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="备注" prop="remark">
<el-input v-model="form.remark" placeholder="备注"></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="状态" prop="enabled" v-show="isEdit">
<el-select
v-model="form.enabled"
placeholder="状态"
style="width: 100%"
>
<el-option
v-for="(item, i) in stateList"
:key="i"
:label="item.dataName"
:value="item.dataCode"
></el-option>
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="设备图片" prop="imgId">
<div v-if="deviceImg" style="position: relative">
<el-image
:src="deviceImg"
class="avatar"
:preview-src-list="srcList"
>
</el-image>
<i
class="el-icon-circle-close avatar-close-icon"
@click="clearImg()"
></i>
</div>
<el-upload
v-else
:data="dataObj"
name="files"
class="device-uploader"
:action="uploadApi"
:show-file-list="false"
:on-success="handleAvatarSuccess"
:before-upload="beforeAvatarUpload"
:headers="{ token }"
>
<i class="el-icon-plus avatar-uploader-icon"></i>
</el-upload>
</el-form-item>
</el-col>
</el-row>
</el-form>
</template>
<script>
import {
addEquipment,
getEquipment,
equipmentUpdate,
getEqCode,
getUnitList
} from '@/api/basicConfig'
import { commonUploadPath } from '@/api/app'
import { getToken } from '@/utils/auth'
export default {
name: 'ConsumablesAdd',
data() {
return {
form: {
id: '',
proLineId: '',
unitId: '',
name: '',
spec: '',
code: '',
baseLife: '',
price: '',
imgId: '',
supplier: '',
purchaseDate: '',
productDate: '',
remark: '',
enabled: 1
},
deviceImg: '',
srcList: [],
rules: {
proLineId: [
{ required: true, message: '请选择产线', trigger: 'change' }
],
unitId: [{ required: true, message: '请选择单元', trigger: 'change' }],
name: [{ required: true, message: '请输入设备名称', trigger: 'blur' }],
spec: [{ required: true, message: '请输入设备规格', trigger: 'blur' }],
code: [{ required: true, message: '请输入设备编码', trigger: 'blur' }]
},
isEdit: false,
productionLineList: JSON.parse(localStorage.getItem('publicList'))
.proLineVoList,
stateList: JSON.parse(localStorage.getItem('publicList')).enabledVoList,
unitModuleList: [],
uploadApi: commonUploadPath,
token: getToken(),
dataObj: { typeCode: 'equipmentImage' }
}
},
methods: {
init(id) {
if (id) {
this.form.id = id
this.isEdit = true
getEquipment({ id }).then((res) => {
if (res.code === 0) {
this.form = res.data
this.form.proLineId = res.data.proLineId
this.form.unitId = res.data.unitId
this.form.name = res.data.name
this.form.spec = res.data.spec
this.form.code = res.data.code
this.form.baseLife = res.data.baseLife
this.form.price = res.data.price
this.form.imgId = res.data.imgId
if (res.data.imgId) {
this.deviceImg = process.env.VUE_APP_VIEW_PIC + res.data.imgId
this.srcList = [this.deviceImg]
}
this.form.supplier = res.data.supplier
this.form.purchaseDate = res.data.purchaseDate
this.form.productDate = res.data.productDate
this.form.remark = res.data.remark
this.form.enabled = res.data.enabled
}
})
} else {
this.form.id = ''
this.isEdit = false
}
},
getUnitModule(val) {
getUnitList(val).then((res) => {
this.unitModuleList = res.data || []
this.form.unitId = ''
this.form.code = ''
})
},
getEqCode() {
if (this.form.proLineId && this.form.unitId) {
let sequence = ''
this.unitModuleList &&
this.unitModuleList.map((item) => {
if (item.id === this.form.unitId) {
sequence = item.sequence
}
})
getEqCode({
proLineId: this.form.proLineId,
sequence: sequence
}).then((res) => {
this.form.code = res.data
})
}
},
submitForm() {
this.$refs['form'].validate((valid) => {
if (valid) {
if (this.isEdit) {
equipmentUpdate({ ...this.form }).then((res) => {
if (res.code === 0) {
this.$message({
message: '操作成功',
type: 'success',
duration: 1500
})
this.$emit('successSubmit')
}
})
} else {
addEquipment({ ...this.form }).then((res) => {
if (res.code === 0) {
this.$message({
message: '操作成功',
type: 'success',
duration: 1500
})
this.$emit('successSubmit')
}
})
}
} else {
return false
}
})
},
formClear() {
this.$refs.form.resetFields()
this.deviceImg = ''
this.srcList = []
},
handleAvatarSuccess(res) {
this.form.imgId = res.data[0].id
this.deviceImg = process.env.VUE_APP_VIEW_PIC + res.data[0].id
this.srcList = [this.deviceImg]
},
clearImg() {
this.deviceImg = ''
this.srcList = []
this.form.imgId = ''
},
beforeAvatarUpload(file) {
const isJPG = file.type === ('image/jpeg' || 'image/png')
const isLt2M = file.size / 1024 / 1024 < 2
if (!isJPG) {
this.$message.error('上传头像图片只能是 JPG / PNG格式!')
}
if (!isLt2M) {
this.$message.error('上传头像图片大小不能超过 2MB!')
}
return isJPG && isLt2M
}
}
}
</script>
<style lang="scss">
.avatar {
width: 100%;
cursor: pointer;
}
.avatar-close-icon {
font-size: 24px;
position: absolute;
right: -10px;
top: -10px;
cursor: pointer;
}
.device-uploader {
width: 178px;
height: 178px;
display: block;
.el-upload {
border: 1px dashed #d9d9d9;
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
}
.el-upload:hover {
border-color: #409eff;
}
.avatar-uploader-icon {
font-size: 28px;
color: #8c939d;
width: 178px;
height: 178px;
line-height: 178px;
text-align: center;
}
}
</style>

View File

@ -0,0 +1,114 @@
<template>
<el-form ref="form" :rules="rules" label-width="80px" :model="form">
<el-form-item label="产线" prop="proLineId">
<el-select
v-model="form.proLineId"
placeholder="产线"
style="width: 100%"
>
<el-option
v-for="(item, i) in productionLineList"
:key="i"
:label="item.name"
:value="item.id"
></el-option>
</el-select>
</el-form-item>
<el-form-item label="线边库" prop="name">
<el-input v-model="form.name" placeholder="线边库"></el-input>
</el-form-item>
<el-form-item label="状态" prop="enabled" v-show="isEdit">
<el-select v-model="form.enabled" placeholder="状态" style="width: 100%">
<el-option
v-for="(item, i) in stateList"
:key="i"
:label="item.dataName"
:value="item.dataCode"
></el-option>
</el-select>
</el-form-item>
</el-form>
</template>
<script>
import {
addSideLibrary,
getSideLibrary,
sideLibraryUpdate
} from '@/api/basicConfig'
export default {
name: 'LineSideLibraryConfigAdd',
data() {
return {
form: {
id: '',
proLineId: '',
name: '',
enabled: 1
},
productionLineList: JSON.parse(localStorage.getItem('publicList'))
.proLineVoList,
stateList: JSON.parse(localStorage.getItem('publicList')).enabledVoList,
rules: {
proLineId: [
{ required: true, message: '请选择产线', trigger: 'change' }
],
name: [{ required: true, message: '请输入线边库', trigger: 'blur' }]
},
isEdit: false
}
},
methods: {
init(id) {
if (id) {
this.isEdit = true
this.form.id = id
getSideLibrary({ id }).then((res) => {
if (res.code === 0) {
this.form.proLineId = res.data.proLineId
this.form.name = res.data.name
this.form.enabled = res.data.enabled
}
})
} else {
this.isEdit = false
this.form.id = ''
}
},
submitForm() {
this.$refs['form'].validate((valid) => {
if (valid) {
if (this.isEdit) {
sideLibraryUpdate({ ...this.form }).then((res) => {
if (res.code === 0) {
this.$message({
message: '操作成功',
type: 'success',
duration: 1500
})
this.$emit('successSubmit')
}
})
} else {
addSideLibrary({ ...this.form }).then((res) => {
if (res.code === 0) {
this.$message({
message: '操作成功',
type: 'success',
duration: 1500
})
this.$emit('successSubmit')
}
})
}
} else {
return false
}
})
},
formClear() {
this.$refs.form.resetFields()
this.isEdit = false
}
}
}
</script>

View File

@ -0,0 +1,127 @@
<template>
<el-form ref="form" :rules="rules" label-width="80px" :model="form">
<el-form-item label="备件类别" prop="type">
<el-select v-model="form.type" placeholder="备件类别" style="width: 100%">
<el-option
v-for="(item, i) in sparePartsTypeList"
:key="i"
:label="item.dataName"
:value="item.dataName"
></el-option>
</el-select>
</el-form-item>
<el-form-item label="备件名称" prop="name">
<el-input v-model="form.name" placeholder="备件名称"></el-input>
</el-form-item>
<el-form-item label="备件型号" prop="model">
<el-input v-model="form.model" placeholder="无型号请填写为 /"></el-input>
</el-form-item>
<el-form-item label="状态" prop="enabled" v-show="isEdit">
<el-select v-model="form.enabled" placeholder="状态" style="width: 100%">
<el-option
v-for="(item, i) in stateList"
:key="i"
:label="item.dataName"
:value="item.dataCode"
></el-option>
</el-select>
</el-form-item>
</el-form>
</template>
<script>
import {
addSpareParts,
getSpareParts,
sparePartsUpdate
} from '@/api/basicConfig'
export default {
name: 'SparePartsAdd',
data() {
return {
form: {
id: '',
type: '',
name: '',
model: '',
enabled: 1
},
sparePartsTypeList: JSON.parse(localStorage.getItem('publicList'))
.sparePartsTypeList,
stateList: JSON.parse(localStorage.getItem('publicList')).enabledVoList,
rules: {
type: [
{ required: true, message: '请选择备件类别', trigger: 'change' }
],
name: [{ required: true, message: '请输入备件名称', trigger: 'blur' }],
model: [
{
required: true,
message: '请输入备件型号',
trigger: 'blur'
}
]
},
isEdit: false
}
},
methods: {
init(val) {
if (val) {
this.form.id = val.id
getSpareParts({ id: val.id }).then((res) => {
if (res.code === 0) {
this.form.type = res.data.type
this.form.name = res.data.name
this.form.model = res.data.model
this.form.enabled = res.data.enabled
}
})
if (val.type === 'edit') {
this.isEdit = true
} else {
this.isEdit = false
this.form.id = ''
}
} else {
this.isEdit = false
this.form.id = ''
}
},
submitForm() {
this.$refs['form'].validate((valid) => {
if (valid) {
if (this.isEdit) {
sparePartsUpdate({ ...this.form }).then((res) => {
if (res.code === 0) {
this.$message({
message: '操作成功',
type: 'success',
duration: 1500
})
this.$emit('successSubmit')
}
})
} else {
addSpareParts({ ...this.form }).then((res) => {
if (res.code === 0) {
this.$message({
message: '操作成功',
type: 'success',
duration: 1500
})
this.$emit('successSubmit')
}
})
}
} else {
return false
}
})
},
formClear() {
this.$refs.form.resetFields()
this.isEdit = false
}
}
}
</script>

View File

@ -0,0 +1,48 @@
<template>
<div>
<el-tag size="medium" :type="tagType">{{ showName }}</el-tag>
</div>
</template>
<script>
export default {
name: 'statusTag',
props: {
injectData: {
type: Object,
default: () => ({})
}
},
data() {
return {
tagType: '',
showName: '',
list: JSON.parse(localStorage.getItem('publicList')).enabledVoList
}
},
mounted() {
this.init()
},
methods: {
init() {
if (this.injectData.enabled === 1) {
this.tagType = ''
} else {
this.tagType = 'warning'
}
this.list &&
this.list.map((item) => {
if (item.dataCode === this.injectData.enabled) {
this.showName = item.dataName
}
})
}
},
watch: {
injectData(newVal, oldVal) {
if (oldVal.enabled !== newVal.enabled) {
this.init()
}
}
}
}
</script>

View File

@ -0,0 +1,215 @@
<template>
<div class="page-box">
<search-bar
:formConfigs="formConfig"
ref="searchBarForm"
@headBtnClick="buttonClick"
/>
<base-table
:page="listQuery.current"
:limit="listQuery.size"
:table-props="tableProps"
:table-data="tableData"
:max-height="tableH"
>
<method-btn
v-if="tableBtn.length"
slot="handleBtn"
:width="120"
label="操作"
:method-list="tableBtn"
@clickBtn="handleClick"
/>
</base-table>
<pagination
:page.sync="listQuery.current"
:limit.sync="listQuery.size"
:total="total"
@pagination="getList()"
/>
<!-- 新增 -->
<base-dialog
:title="addOrEditTitle"
:dialogVisible="centervisible"
@cancel="handleCancel"
@confirm="handleConfirm"
:before-close="handleCancel"
>
<consumables-add ref="consumablesAdd" @successSubmit="successSubmit" />
</base-dialog>
</div>
</template>
<script>
import ConsumablesAdd from './components/consumablesAdd'
import { tableHeight } from '@/utils/index'
import statusTag from './components/statusTag.vue'
import { getConsumablePage } from '@/api/basicConfig'
const tableProps = [
{
prop: 'name',
label: '耗材名称'
},
{
prop: 'spec',
label: '耗材规格'
},
{
prop: 'unit',
label: '耗材单位'
},
{
prop: 'basicConsume',
label: '基础消耗量'
},
{
prop: 'enabled',
label: '状态',
width: 120,
subcomponent: statusTag
}
]
const tableBtn = [
{
type: 'edit',
btnName: '编辑'
},
{
type: 'copy',
btnName: '复制'
}
]
export default {
name: 'ConsumablesConfig',
components: { ConsumablesAdd },
data() {
return {
formConfig: [
{
type: 'input',
label: '耗材名称',
placeholder: '耗材名称',
param: 'name'
},
{
type: 'input',
label: '耗材规格',
placeholder: '耗材规格',
param: 'spec'
},
{
type: 'select',
label: '状态',
selectOptions: JSON.parse(localStorage.getItem('publicList'))
.enabledVoList,
labelField: 'dataName',
valueField: 'dataCode',
param: 'enabled',
defaultSelect: '',
width: 120
},
{
type: 'button',
btnName: '查询',
name: 'search',
color: 'primary'
},
{
type: 'button',
btnName: '重置',
name: 'reset'
},
{
type: 'button',
btnName: '新增',
name: 'add',
color: 'primary',
plain: true
}
],
tableProps,
tableData: [],
tableBtn,
tableH: tableHeight(265),
total: 0,
listQuery: {
current: 1,
size: 20
},
centervisible: false,
addOrEditTitle: ''
}
},
mounted() {
window.addEventListener('resize', () => {
this.tableH = tableHeight(265)
})
this.getList()
},
methods: {
getList() {
getConsumablePage({ ...this.listQuery }).then((res) => {
console.log(res)
if (res.code === 0) {
this.total = res.data.total
this.tableData = res.data.records
}
})
},
buttonClick(val) {
console.log(val)
switch (val.btnName) {
case 'search':
this.listQuery.enabled = val.enabled
this.listQuery.spec = val.spec
this.listQuery.name = val.name
this.listQuery.current = 1
this.getList()
break
case 'reset':
this.$refs.searchBarForm.resetForm()
this.listQuery.enabled = ''
this.listQuery.spec = ''
this.listQuery.name = ''
this.listQuery.current = 1
this.getList()
break
default:
this.addOrEditTitle = '新增'
this.centervisible = true
this.$nextTick(() => {
this.$refs.consumablesAdd.init()
})
}
},
handleClick(val) {
if (val.type === 'edit') {
this.addOrEditTitle = '编辑'
const param = { id: val.data.id, type: 'edit' }
this.$nextTick(() => {
this.$refs.consumablesAdd.init(param)
})
this.centervisible = true
} else if (val.type === 'copy') {
this.addOrEditTitle = '新增'
const param = { id: val.data.id, type: 'copy' }
this.$nextTick(() => {
this.$refs.consumablesAdd.init(param)
})
this.centervisible = true
}
},
handleCancel() {
this.$refs.consumablesAdd.formClear()
this.centervisible = false
this.addOrEditTitle = ''
},
handleConfirm() {
this.$refs.consumablesAdd.submitForm()
},
successSubmit() {
this.handleCancel()
this.getList()
}
}
}
</script>

View File

@ -0,0 +1,328 @@
<template>
<div>
<el-row :gutter="10" class="main-box">
<el-col :span="4">
<div class="left-box">
<el-tree
:data="treeData"
node-key="id"
:props="defaultProps"
default-expand-all
@node-drag-end="handleDragEnd"
draggable
:allow-drop="allowDrop"
:allow-drag="allowDrag"
:highlight-current="true"
@node-click="clickDevice"
>
</el-tree>
</div>
</el-col>
<el-col :span="20">
<div class="right-box">
<search-bar
:formConfigs="formConfig"
ref="searchBarForm"
@headBtnClick="buttonClick"
/>
<base-table
:page="listQuery.current"
:limit="listQuery.size"
:table-props="tableProps"
:table-data="tableData"
:max-height="tableH"
>
<method-btn
v-if="tableBtn.length"
slot="handleBtn"
:width="80"
label="操作"
fixed="right"
:method-list="tableBtn"
@clickBtn="handleClick"
/>
</base-table>
<pagination
:page.sync="listQuery.current"
:limit.sync="listQuery.size"
:total="total"
@pagination="getList()"
/>
</div>
<!-- 新增 -->
<base-dialog
:title="addOrEditTitle"
:dialogVisible="centervisible"
@cancel="handleCancel"
@confirm="handleConfirm"
:before-close="handleCancel"
>
<device-add ref="deviceAdd" @successSubmit="successSubmit" />
</base-dialog>
</el-col>
</el-row>
</div>
</template>
<script>
import DeviceAdd from './components/deviceAdd'
import { tableHeight } from '@/utils/index'
import statusTag from './components/statusTag.vue'
import { getTreeData, updateTreeData } from '@/api/app'
import { getEquipmentPage } from '@/api/basicConfig'
import { timeFormatter, publicFormatter } from '@/utils'
const tableProps = [
{
prop: 'proLineId',
label: '产线',
filter: publicFormatter('proLineVoList'),
width: 100
},
{
prop: 'unitName',
label: '单元',
width: 150
},
{
prop: 'name',
label: '设备名称',
width: 150
},
{
prop: 'spec',
label: '设备规格',
width: 150
},
{
prop: 'code',
label: '设备编码',
width: 150
},
{
prop: 'status',
label: '设备状态',
width: 120,
subcomponent: statusTag
},
{
prop: 'baseLife',
label: '基础寿命',
width: 100
},
{
prop: 'price',
label: '购买价格/万元',
width: 140
},
{
prop: 'supplier',
label: '供应商',
width: 150
},
{
prop: 'purchaseDate',
label: '采购日期',
minWidth: 120
},
{
prop: 'productDate',
label: '生产日期',
minWidth: 120
},
{
prop: 'creatorName',
label: '登记人',
width: 120
},
{
prop: 'createTime',
label: '登记时间',
filter: timeFormatter,
minWidth: 160
},
{
prop: 'remark',
label: '备注',
width: 150
}
]
const tableBtn = [
{
type: 'edit',
btnName: '编辑'
}
]
export default {
name: 'DeviceConfig',
components: { DeviceAdd },
data() {
return {
treeData: [],
defaultProps: {
children: 'children',
label: 'name'
},
formConfig: [
{
type: 'input',
label: '设备规格',
placeholder: '设备规格',
param: 'spec'
},
{
type: 'input',
label: '设备编码',
placeholder: '设备编码',
param: 'code'
},
{
type: 'button',
btnName: '查询',
name: 'search',
color: 'primary'
},
{
type: 'button',
btnName: '新增',
name: 'add',
color: 'primary',
plain: true
}
],
tableProps,
tableData: [],
tableBtn,
tableH: tableHeight(275),
total: 0,
listQuery: {
current: 1,
size: 20
},
centervisible: false,
addOrEditTitle: ''
}
},
mounted() {
window.addEventListener('resize', () => {
this.tableH = tableHeight(275)
})
this.getTree()
this.getList()
},
methods: {
getTree() {
getTreeData().then((res) => {
this.treeData = res.data
})
},
getList() {
getEquipmentPage({ ...this.listQuery }).then((res) => {
if (res.code === 0) {
this.total = res.data.total
this.tableData = res.data.records
}
})
},
// tree-start
handleDragEnd(draggingNode) {
let unitId = draggingNode.data.parent
let list = []
for (let i = 0; i < this.treeData.length; i++) {
for (let j = 0; j < this.treeData[i].children.length; j++) {
if (this.treeData[i].children[j].id === unitId) {
list = this.treeData[i].children[j].children
}
}
}
updateTreeData(list).then(() => {
this.$message({
message: '操作成功',
type: 'success',
duration: 1500,
onClose: () => {}
})
this.getTree()
})
},
allowDrop(draggingNode, dropNode, type) {
if (
draggingNode.data.parent === dropNode.data.parent &&
dropNode.level === 3 &&
type !== 'inner'
) {
return true
} else {
return false
}
},
allowDrag(draggingNode) {
return draggingNode.data.isAllowDrag
},
clickDevice(val) {
if (!val.children) {
this.$refs.searchBarForm.resetForm()
this.listQuery.spec = ''
this.listQuery.code = ''
this.listQuery.id = val.id
this.listQuery.current = 1
this.getList()
}
},
// tree-end
buttonClick(val) {
console.log(val)
switch (val.btnName) {
case 'search':
this.listQuery.spec = val.spec
this.listQuery.code = val.code
this.listQuery.id = ''
this.listQuery.current = 1
this.getList()
break
default:
this.addOrEditTitle = '新增'
this.centervisible = true
this.$nextTick(() => {
this.$refs.deviceAdd.init()
})
}
},
handleClick(val) {
if (val.type === 'edit') {
this.addOrEditTitle = '编辑'
this.$nextTick(() => {
this.$refs.deviceAdd.init(val.data.id)
})
this.centervisible = true
}
},
handleCancel() {
this.$refs.deviceAdd.formClear()
this.centervisible = false
this.addOrEditTitle = ''
},
handleConfirm() {
this.$refs.deviceAdd.submitForm()
},
successSubmit() {
this.handleCancel()
this.getList()
this.getTree()
}
}
}
</script>
<style lang="scss" scoped>
.main-box {
width: 100%;
padding: 0px 6px 0 16px;
.left-box,
.right-box {
padding: 16px 10px 0;
border-radius: 8px;
background-color: #fff;
overflow-y: auto;
height: calc(100vh - 147px);
margin-top: 8px;
}
}
</style>

View File

@ -0,0 +1,184 @@
<template>
<div class="page-box">
<search-bar :formConfigs="formConfig" @headBtnClick="buttonClick" />
<base-table
:page="listQuery.current"
:limit="listQuery.size"
:table-props="tableProps"
:table-data="tableData"
:max-height="tableH"
>
<method-btn
v-if="tableBtn.length"
slot="handleBtn"
:width="80"
label="操作"
:method-list="tableBtn"
@clickBtn="handleClick"
/>
</base-table>
<pagination
:page.sync="listQuery.current"
:limit.sync="listQuery.size"
:total="total"
@pagination="getList()"
/>
<!-- 新增 -->
<base-dialog
:title="addOrEditTitle"
:dialogVisible="centervisible"
@cancel="handleCancel"
@confirm="handleConfirm"
:before-close="handleCancel"
>
<line-side-library-add
ref="lineSideLibraryAdd"
@successSubmit="successSubmit"
/>
</base-dialog>
</div>
</template>
<script>
import LineSideLibraryAdd from './components/lineSideLibraryAdd'
import { tableHeight } from '@/utils/index'
import statusTag from './components/statusTag.vue'
import { getSideLibraryPage } from '@/api/basicConfig'
import { publicFormatter } from '@/utils'
const tableProps = [
{
prop: 'proLineId',
filter: publicFormatter('proLineVoList'),
label: '产线'
},
{
prop: 'name',
label: '线边库'
},
{
prop: 'enabled',
label: '状态',
width: 120,
subcomponent: statusTag
}
]
const tableBtn = [
{
type: 'edit',
btnName: '编辑'
}
]
export default {
name: 'LineSideLibraryConfig',
components: { LineSideLibraryAdd },
data() {
return {
formConfig: [
{
type: 'select',
label: '产线',
selectOptions: JSON.parse(localStorage.getItem('publicList'))
.proLineVoList,
param: 'proLineId',
defaultSelect: '',
width: 120
},
{
type: 'input',
label: '线边库',
placeholder: '线边库',
param: 'name'
},
{
type: 'select',
label: '状态',
selectOptions: JSON.parse(localStorage.getItem('publicList'))
.enabledVoList,
labelField: 'dataName',
valueField: 'dataCode',
param: 'enabled',
defaultSelect: '',
width: 120
},
{
type: 'button',
btnName: '查询',
name: 'search',
color: 'primary'
},
{
type: 'button',
btnName: '新增',
name: 'add',
color: 'primary',
plain: true
}
],
tableProps,
tableData: [],
tableBtn,
tableH: tableHeight(265),
total: 0,
listQuery: {
current: 1,
size: 20
},
centervisible: false,
addOrEditTitle: ''
}
},
mounted() {
window.addEventListener('resize', () => {
this.tableH = tableHeight(265)
})
this.getList()
},
methods: {
getList() {
getSideLibraryPage({ ...this.listQuery }).then((res) => {
console.log(res)
if (res.code === 0) {
this.total = res.data.total
this.tableData = res.data.records
}
})
},
buttonClick(val) {
console.log(val)
switch (val.btnName) {
case 'search':
this.listQuery.proLineId = val.proLineId
this.listQuery.name = val.name
this.listQuery.enabled = val.enabled
this.listQuery.current = 1
this.getList()
break
default:
this.addOrEditTitle = '新增'
this.centervisible = true
this.$nextTick(() => {
this.$refs.lineSideLibraryAdd.init()
})
}
},
handleClick(val) {
this.addOrEditTitle = '编辑'
this.$nextTick(() => {
this.$refs.lineSideLibraryAdd.init(val.data.id)
})
this.centervisible = true
},
handleCancel() {
this.$refs.lineSideLibraryAdd.formClear()
this.centervisible = false
this.addOrEditTitle = ''
},
handleConfirm() {
this.$refs.lineSideLibraryAdd.submitForm()
},
successSubmit() {
this.handleCancel()
this.getList()
}
}
}
</script>

View File

@ -0,0 +1,221 @@
<template>
<div class="page-box">
<search-bar
:formConfigs="formConfig"
ref="searchBarForm"
@headBtnClick="buttonClick"
/>
<base-table
:page="listQuery.current"
:limit="listQuery.size"
:table-props="tableProps"
:table-data="tableData"
:max-height="tableH"
>
<method-btn
v-if="tableBtn.length"
slot="handleBtn"
:width="120"
label="操作"
:method-list="tableBtn"
@clickBtn="handleClick"
/>
</base-table>
<pagination
:page.sync="listQuery.current"
:limit.sync="listQuery.size"
:total="total"
@pagination="getList()"
/>
<!-- 新增 -->
<base-dialog
:title="addOrEditTitle"
:dialogVisible="centervisible"
@cancel="handleCancel"
@confirm="handleConfirm"
:before-close="handleCancel"
>
<spare-parts-add ref="sparePartsAdd" @successSubmit="successSubmit" />
</base-dialog>
</div>
</template>
<script>
import SparePartsAdd from './components/sparePartsAdd.vue'
import { tableHeight } from '@/utils/index'
import statusTag from './components/statusTag.vue'
import { getSparePartsPage } from '@/api/basicConfig'
const tableProps = [
{
prop: 'type',
label: '备件类别'
},
{
prop: 'name',
label: '备件名称'
},
{
prop: 'model',
label: '备件型号'
},
{
prop: 'enabled',
label: '状态',
width: 120,
subcomponent: statusTag
}
]
const tableBtn = [
{
type: 'edit',
btnName: '编辑'
},
{
type: 'copy',
btnName: '复制'
}
]
export default {
name: 'SparePartsConfig',
components: { SparePartsAdd },
data() {
return {
formConfig: [
{
type: 'select',
label: '备件类别',
selectOptions: JSON.parse(localStorage.getItem('publicList'))
.sparePartsTypeList,
param: 'type',
labelField: 'dataName',
valueField: 'dataName',
defaultSelect: ''
},
{
type: 'input',
label: '备件名称',
placeholder: '备件名称',
param: 'name'
},
{
type: 'input',
label: '备件型号',
placeholder: '备件型号',
param: 'model'
},
{
type: 'select',
label: '状态',
selectOptions: JSON.parse(localStorage.getItem('publicList'))
.enabledVoList,
labelField: 'dataName',
valueField: 'dataCode',
param: 'enabled',
defaultSelect: '',
width: 120
},
{
type: 'button',
btnName: '查询',
name: 'search',
color: 'primary'
},
{
type: 'button',
btnName: '重置',
name: 'reset'
},
{
type: 'button',
btnName: '新增',
name: 'add',
color: 'primary',
plain: true
}
],
tableProps,
tableData: [],
tableBtn,
tableH: tableHeight(265),
total: 0,
listQuery: {
current: 1,
size: 20
},
centervisible: false,
addOrEditTitle: ''
}
},
mounted() {
window.addEventListener('resize', () => {
this.tableH = tableHeight(265)
})
this.getList()
},
methods: {
getList() {
getSparePartsPage({ ...this.listQuery }).then((res) => {
if (res.code === 0) {
this.total = res.data.total
this.tableData = res.data.records
}
})
},
buttonClick(val) {
switch (val.btnName) {
case 'search':
this.listQuery.type = val.type
this.listQuery.name = val.name
this.listQuery.model = val.model
this.listQuery.enabled = val.enabled
this.listQuery.current = 1
this.getList()
break
case 'reset':
this.$refs.searchBarForm.resetForm()
this.listQuery.type = ''
this.listQuery.name = ''
this.listQuery.model = ''
this.listQuery.enabled = ''
this.listQuery.current = 1
this.getList()
break
default:
this.addOrEditTitle = '新增'
this.centervisible = true
this.$nextTick(() => {
this.$refs.sparePartsAdd.init()
})
}
},
handleClick(val) {
if (val.type === 'edit') {
this.addOrEditTitle = '编辑'
const param = { id: val.data.id, type: 'edit' }
this.$nextTick(() => {
this.$refs.sparePartsAdd.init(param)
})
this.centervisible = true
} else if (val.type === 'copy') {
this.addOrEditTitle = '新增'
const param = { id: val.data.id, type: 'copy' }
this.$nextTick(() => {
this.$refs.sparePartsAdd.init(param)
})
this.centervisible = true
}
},
handleCancel() {
this.$refs.sparePartsAdd.formClear()
this.centervisible = false
this.addOrEditTitle = ''
},
handleConfirm() {
this.$refs.sparePartsAdd.submitForm()
},
successSubmit() {
this.handleCancel()
this.getList()
}
}
}
</script>

View File

@ -0,0 +1,53 @@
<template>
<el-popover placement="right" width="500" trigger="click">
<el-table :data="tableData">
<el-table-column prop="batchCode" label="批次编码" width="160">
</el-table-column>
<el-table-column prop="supplier" label="供应商" width="220">
</el-table-column>
<el-table-column prop="num" label="当前库存"> </el-table-column>
</el-table>
<el-button
slot="reference"
type="text"
class="tableInnerButton"
@click="showInnerTable(injectData)"
>详情</el-button
>
</el-popover>
</template>
<script>
import { getMaterialDetail } from '@/api/consumablesManagement'
export default {
name: 'InnerTable',
props: {
injectData: {
type: Object,
default: () => ({})
},
itemProp: {
type: String
}
},
data() {
return {
list: this.injectData,
tableData: []
}
},
methods: {
showInnerTable(data) {
getMaterialDetail({ id: data.id }).then((res) => {
if (res.code === 0) {
this.tableData = res.data
}
})
}
}
}
</script>
<style lang="scss">
.tableInnerButton {
padding: 0;
}
</style>

View File

@ -0,0 +1,104 @@
<template>
<el-form ref="form" :rules="rules" label-width="150px" :model="form">
<el-form-item label="产线" prop="proLineId">
<el-select
v-model="form.proLineId"
placeholder="产线"
style="width: 100%"
@change="selectLine"
>
<el-option
v-for="item in proLineIdList"
:key="item.id"
:label="item.name"
:value="item.id"
></el-option>
</el-select>
</el-form-item>
<el-form-item label="吸附垫寿命上限值" prop="adsorptionPadCeiling">
<el-input-number
v-model="form.adsorptionPadCeiling"
:min="0"
:max="9999999999"
style="width: 100%"
></el-input-number>
</el-form-item>
<el-form-item label="辅助边框寿命上限值" prop="auxiliaryFrameCeiling">
<el-input-number
v-model="form.auxiliaryFrameCeiling"
:min="0"
:max="9999999999"
style="width: 100%"
></el-input-number>
</el-form-item>
</el-form>
</template>
<script>
import { setAlarmValueGet, setAlarmValue } from '@/api/consumablesManagement'
export default {
name: 'alarmAdd',
data() {
return {
form: {
proLineId: '',
adsorptionPadCeiling: 0,
auxiliaryFrameCeiling: 0
},
rules: {
proLineId: [
{ required: true, message: '请选择产线', trigger: 'change' }
],
adsorptionPadCeiling: [
{ required: true, message: '请输入吸附垫寿命上限值', trigger: 'blur' }
],
auxiliaryFrameCeiling: [
{
required: true,
message: '请输入辅助边框寿命上限值',
trigger: 'blur'
}
]
},
proLineIdList: JSON.parse(localStorage.getItem('publicList'))
.proLineVoList
}
},
methods: {
init() {
console.log('init')
},
selectLine(val) {
this.form.proLineId = val
setAlarmValueGet({ id: val }).then((res) => {
this.form.adsorptionPadCeiling = res.data.adsorptionPadCeiling
? res.data.adsorptionPadCeiling
: null
this.form.auxiliaryFrameCeiling = res.data.auxiliaryFrameCeiling
? res.data.auxiliaryFrameCeiling
: null
})
},
submitForm() {
this.$refs['form'].validate((valid) => {
if (valid) {
setAlarmValue({ ...this.form }).then((res) => {
if (res.code === 0) {
this.$message({
message: '操作成功',
type: 'success',
duration: 1500
})
this.$emit('successSubmit')
}
})
} else {
return false
}
})
},
formClear() {
this.$refs.form.resetFields()
}
}
}
</script>

View File

@ -0,0 +1,368 @@
<template>
<el-form ref="form" :rules="rules" label-width="110px" :model="form">
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="产线" prop="proLineId">
<el-select
v-model="form.proLineId"
placeholder="产线"
style="width: 100%"
:disabled="showEdit || showOutput"
@change="getSideLibaryList"
>
<el-option
v-for="item in proLineIdList"
:key="item.id"
:label="item.name"
:value="item.id"
></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="所属线边库" prop="sideLibaryId">
<el-select
v-model="form.sideLibaryId"
placeholder="所属线边库"
style="width: 100%"
:disabled="showEdit || showOutput"
>
<el-option
v-for="(item, i) in sideLibaryIdList"
:key="i"
:label="item.name"
:value="item.sideLibaryId"
></el-option>
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="耗材名称" prop="name">
<el-select
v-model="form.name"
filterable
placeholder="耗材名称"
style="width: 100%"
:disabled="showEdit || showOutput"
@change="selectMeta"
>
<el-option
v-for="(item, i) in consumablesNameList"
:key="i"
:label="item.name"
:value="item.name"
></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="规格" prop="spec">
<el-select
v-model="form.spec"
placeholder="规格"
style="width: 100%"
:disabled="showEdit || showOutput"
>
<el-option
v-for="(item, i) in consumablesSpecList"
:key="i"
:label="item.spec"
:value="item.spec"
></el-option>
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="批次编码" prop="batchCode" v-if="!showEdit">
<el-input
v-if="!showOutput"
v-model="form.batchCode"
:disabled="showEdit"
placeholder="批次编码"
></el-input>
<el-select
v-if="showOutput"
v-model="form.batchCode"
placeholder="批次编码"
style="width: 100%"
@change="toggleSupplier"
>
<el-option
v-for="item in batchCodeList"
:key="item.batchCode"
:label="item.batchCode"
:value="item.batchCode"
></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item
:label="showOutput ? '出库数量' : '入库数量'"
prop="num"
v-if="!showEdit"
>
<el-input-number
v-model="form.num"
:min="0"
:max="999999999"
style="width: 100%"
:disabled="showEdit"
></el-input-number>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="供应商" prop="supplier" v-if="!showEdit">
<el-input
v-model="form.supplier"
:disabled="showEdit || showOutput"
placeholder="供应商"
></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="单位" prop="unit">
<el-input v-model="form.unit" placeholder="单位" disabled></el-input>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20" v-if="showEdit">
<el-col :span="12">
<el-form-item label="最低安全库存" prop="minNum">
<el-input-number
v-model="form.minNum"
:min="0"
:max="999999999"
style="width: 100%"
></el-input-number>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="最高安全库存" prop="maxNum">
<el-input-number
v-model="form.maxNum"
:min="0"
:max="9999999999"
style="width: 100%"
></el-input-number>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20" v-if="showOutput">
<el-col :span="12">
<el-form-item label="领取人" prop="recipient">
<el-input v-model="form.recipient" placeholder="领取人"></el-input>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="24">
<el-form-item label="备注" prop="remark">
<el-input v-model="form.remark" placeholder="备注"></el-input>
</el-form-item>
</el-col>
</el-row>
</el-form>
</template>
<script>
import {
getMaterialDetail,
getMaterialManageByid,
materialManageUpdate,
materialManageInsert,
materialManageDelete
} from '@/api/consumablesManagement'
import { sideLibraryList, materialList, getSpecList } from '@/api/basicConfig'
export default {
name: 'inAndOutManagementAdd',
data() {
return {
form: {
id: '',
proLineId: '',
sideLibaryId: '',
name: '',
spec: '',
batchCode: '',
num: '',
supplier: '',
unit: '',
remark: '',
minNum: '',
maxNum: '',
recipient: ''
},
rules: {
proLineId: [
{ required: true, message: '请选择产线', trigger: 'change' }
],
sideLibaryId: [
{ required: true, message: '请选择所属线边库', trigger: 'change' }
],
name: [
{ required: true, message: '请选择耗材名称', trigger: 'change' }
],
spec: [{ required: true, message: '请选择规格', trigger: 'change' }],
batchCode: [
{ required: true, message: '请输入批次编码', trigger: 'blur' }
],
num: [{ required: true, message: '请输入入库数量', trigger: 'blur' }],
minNum: [
{ required: true, message: '请输入最低安全库存', trigger: 'blur' }
],
maxNum: [
{ required: true, message: '请输入最高安全库存', trigger: 'blur' }
]
},
proLineIdList: JSON.parse(localStorage.getItem('publicList'))
.proLineVoList,
sideLibaryIdList: [],
consumablesNameList: [],
consumablesSpecList: [],
showEdit: false, //
showOutput: false, //
batchCodeList: []
}
},
methods: {
init(param) {
this.getMaterialList()
if (param) {
this.form.id = param.id
getMaterialManageByid({ id: this.form.id }).then((res) => {
this.form.proLineId = '1'
// this.form.proLineId = res.data.lineID
this.getSideLibaryList(this.form.proLineId)
this.form.sideLibaryId = res.data.sideLibaryId
this.form.name = res.data.materialName
this.selectMeta(this.form.name)
this.form.spec = res.data.spec
this.form.minNum = res.data.minNum ? res.data.minNum : 0
this.form.maxNum = res.data.maxNum ? res.data.maxNum : 0
this.form.remark = res.data.remark
})
if (param.type === 'edit') {
this.showEdit = true
this.showOutput = false
console.log('编辑')
} else {
this.showOutput = true
this.showEdit = false
this.getBatch(this.form.id)
console.log('出库')
}
} else {
this.form.id = ''
}
},
getSideLibaryList(id) {
this.form.sideLibaryId = ''
sideLibraryList({ id }).then((res) => {
if (res.code === 0) {
this.sideLibaryIdList = res.data
}
})
},
getMaterialList() {
materialList().then((res) => {
if (res.code === 0) {
this.consumablesNameList = res.data
}
})
},
selectMeta(val) {
this.consumablesNameList &&
this.consumablesNameList.map((item) => {
if (item.name === val) {
this.form.unit = item.unit
}
})
getSpecList({ name: val }).then((res) => {
console.log(res)
if (res.code === 0) {
this.consumablesSpecList = res.data
if (!this.showEdit && !this.showOutput) {
this.form.spec = ''
}
}
})
},
getBatch(id) {
getMaterialDetail({ id }).then((res) => {
if (res.code === 0) {
this.batchCodeList = res.data
}
})
},
submitForm() {
this.$refs['form'].validate((valid) => {
if (valid) {
if (this.form.id) {
if (this.showEdit) {
materialManageUpdate({
id: this.form.id,
minNum: this.form.minNum,
maxNum: this.form.maxNum,
remark: this.form.remark
}).then((res) => {
if (res.code === 0) {
this.$message({
message: '操作成功',
type: 'success',
duration: 1500
})
this.$emit('successSubmit')
}
})
} else {
materialManageDelete({ ...this.form }).then((res) => {
if (res.code === 0) {
this.$message({
message: '操作成功',
type: 'success',
duration: 1500
})
this.$emit('successSubmit')
}
})
}
} else {
materialManageInsert({ ...this.form }).then((res) => {
if (res.code === 0) {
this.$message({
message: '操作成功',
type: 'success',
duration: 1500
})
this.$emit('successSubmit')
}
})
}
} else {
return false
}
})
},
formClear() {
this.showEdit = false
this.showOutput = false
this.batchCodeList = []
this.$refs.form.resetFields()
},
toggleSupplier(val) {
this.batchCodeList &&
this.batchCodeList.map((item) => {
if (val === item.batchCode) {
this.form.supplier = item.supplier
}
})
}
}
}
</script>

View File

@ -0,0 +1,311 @@
<template>
<div class="page-box">
<search-bar
:formConfigs="formConfig"
ref="searchBarForm"
@headBtnClick="buttonClick"
@select-changed="selectChanged"
/>
<base-table
:page="listQuery.current"
:limit="listQuery.size"
:table-props="tableProps"
:table-data="tableData"
:max-height="tableH"
>
<method-btn
v-if="tableBtn.length"
slot="handleBtn"
:width="120"
label="操作"
fixed="right"
:method-list="tableBtn"
@clickBtn="handleClick"
/>
</base-table>
<pagination
:page.sync="listQuery.current"
:limit.sync="listQuery.size"
:total="total"
@pagination="getList"
/>
<!-- 入库 -->
<base-dialog
:title="addAndEdit"
:dialogVisible="centervisible"
@cancel="handleCancel"
@confirm="handleConfirm"
:before-close="handleCancel"
>
<in-and-out-management-add
ref="inAndOutManagementAdd"
@successSubmit="successSubmit"
/>
</base-dialog>
</div>
</template>
<script>
import InAndOutManagementAdd from './components/inAndOutManagementAdd'
import InnerTable from './components/InnerTable'
import { tableHeight } from '@/utils/index'
import {
getMaterialManagePage,
outInRecordRecordExport
} from '@/api/consumablesManagement'
import { sideLibraryList, materialList } from '@/api/basicConfig'
const tableProps = [
{
prop: 'line',
label: '产线',
minWidth: 120
},
{
prop: 'sideLibraryName',
label: '所属线边库',
minWidth: 150
},
{
prop: 'materialName',
label: '耗材名称',
minWidth: 150
},
{
prop: 'spec',
label: '规格',
minWidth: 150
},
{
prop: 'unit',
label: '单位',
minWidth: 150
},
{
prop: 'stockNum',
label: '库存数量/个',
sortable: true,
minWidth: 150
},
{
prop: 'details',
label: '库存详情',
minWidth: 100,
subcomponent: InnerTable
},
{
prop: 'minNum',
label: '最低安全库存',
width: 150
},
{
prop: 'maxNum',
label: '最高安全库存',
width: 150
},
{
prop: 'remark',
label: '备注',
minWidth: 150
}
]
const tableBtn = [
{
type: 'edit',
btnName: '编辑'
},
{
type: 'output',
btnName: '出库'
}
]
export default {
name: 'InAndOutManagement',
components: { InAndOutManagementAdd },
data() {
return {
formConfig: [
{
type: 'select',
label: '产线',
selectOptions: JSON.parse(localStorage.getItem('publicList'))
.proLineVoList,
param: 'lineName',
labelField: 'dataName',
valueField: 'dataName',
defaultSelect: '',
onchange: true,
width: 150
},
{
type: 'select',
label: '线边库名称',
selectOptions: [],
param: 'libraryName',
labelField: 'name',
valueField: 'name',
defaultSelect: '',
width: 200
},
{
type: 'select',
label: '耗材名称',
selectOptions: [],
param: 'materialName',
labelField: 'name',
valueField: 'name',
defaultSelect: '',
width: 150
},
{
type: 'button',
btnName: '查询',
name: 'search',
color: 'primary'
},
{
type: 'button',
btnName: '入库',
name: 'add',
color: 'primary',
plain: true
},
{
type: 'button',
btnName: '导出',
name: 'export',
color: 'primary',
plain: true
}
],
tableProps,
tableData: [],
tableBtn,
tableH: tableHeight(275),
total: 0,
listQuery: {
current: 1,
size: 20
},
addAndEdit: '',
centervisible: false
}
},
mounted() {
window.addEventListener('resize', () => {
this.tableH = tableHeight(275)
})
this.getMaterialList()
this.getList()
},
methods: {
getList() {
getMaterialManagePage({ ...this.listQuery }).then((res) => {
console.log(res)
if (res.code === 0) {
this.total = res.data.total
this.tableData = res.data.records
}
})
},
selectChanged(val) {
console.log(val)
if (val.value) {
let id = null
let line = JSON.parse(localStorage.getItem('publicList')).proLineVoList
line &&
line.map((item) => {
if (item.dataName === val.value) {
id = item.dataCode
}
})
sideLibraryList({ id }).then((res) => {
if (res.code === 0) {
this.formConfig[1].selectOptions = res.data
this.formConfig[1].defaultSelect = null
}
})
}
},
getMaterialList() {
materialList().then((res) => {
console.log(res)
if (res.code === 0) {
this.formConfig[2].selectOptions = res.data
}
})
},
handleClick(val) {
console.log(val)
if (val.type === 'edit') {
this.centervisible = true
this.addAndEdit = '编辑'
const param = { id: val.data.id, type: 'edit' }
this.$nextTick(() => {
this.$refs.inAndOutManagementAdd.init(param)
})
} else if (val.type === 'output') {
this.centervisible = true
this.addAndEdit = '出库'
const param = { id: val.data.id, type: 'output' }
this.$nextTick(() => {
this.$refs.inAndOutManagementAdd.init(param)
})
}
},
buttonClick(val) {
console.log(val)
switch (val.btnName) {
case 'search':
this.listQuery.lineName = val.lineName
this.listQuery.libraryName = val.libraryName
this.listQuery.materialName = val.materialName
this.listQuery.current = 1
this.getList()
break
case 'add':
this.centervisible = true
this.addAndEdit = '入库'
this.$nextTick(() => {
this.$refs.inAndOutManagementAdd.init()
})
break
default:
outInRecordRecordExport({ ...this.listQuery }).then((response) => {
console.log(response)
let fileName = ''
const contentDisposition = response.headers['content-disposition']
if (contentDisposition) {
fileName = decodeURIComponent(
contentDisposition.slice(
contentDisposition.indexOf('filename=') + 9
)
)
}
const blob = new Blob([response.data])
const reader = new FileReader()
reader.readAsDataURL(blob)
reader.onload = (e) => {
const a = document.createElement('a')
a.download = fileName
a.href = e.target.result
document.body.appendChild(a)
a.click()
document.body.removeChild(a)
}
})
}
},
handleCancel() {
this.$refs.inAndOutManagementAdd.formClear()
this.addAndEdit = ''
this.centervisible = false
},
handleConfirm() {
this.$refs.inAndOutManagementAdd.submitForm()
},
successSubmit() {
this.handleCancel()
this.getList()
}
}
}
</script>

Some files were not shown because too many files have changed in this diff Show More