Merge remote-tracking branch 'origin/0.8.X' into 0.8.X
This commit is contained in:
@@ -303,4 +303,17 @@
|
||||
|
||||
#app .sidebar-container .nest-menu .el-submenu .el-submenu__title {
|
||||
margin: 0 4px;
|
||||
}
|
||||
|
||||
// 二级菜单叶子页面小圆点
|
||||
.el-menu-item.is-page-item::before {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-radius: 50%;
|
||||
background: var(--current-color, #409EFF);
|
||||
margin-right: 8px;
|
||||
flex-shrink: 0;
|
||||
vertical-align: middle;
|
||||
}
|
||||
@@ -2,7 +2,8 @@
|
||||
<div class="navbar">
|
||||
<hamburger id="hamburger-container" :is-active="sidebar.opened" class="hamburger-container" @toggleClick="toggleSideBar" />
|
||||
|
||||
<breadcrumb id="breadcrumb-container" class="breadcrumb-container" v-if="!topNav"/>
|
||||
<breadcrumb id="breadcrumb-container" class="breadcrumb-container" v-show="topNav || !showSubNav"/>
|
||||
<sub-nav id="subnav-container" class="subnav-container" v-if="!topNav" @has-items="showSubNav = $event"/>
|
||||
<top-nav id="topmenu-container" class="topmenu-container" v-if="topNav"/>
|
||||
|
||||
<div class="right-menu">
|
||||
@@ -52,6 +53,7 @@
|
||||
import { mapGetters } from 'vuex'
|
||||
import Breadcrumb from '@/components/Breadcrumb'
|
||||
import TopNav from '@/components/TopNav'
|
||||
import SubNav from './SubNav'
|
||||
import Hamburger from '@/components/Hamburger'
|
||||
import Screenfull from '@/components/Screenfull'
|
||||
// import SizeSelect from '@/components/SizeSelect'
|
||||
@@ -64,6 +66,7 @@ export default {
|
||||
components: {
|
||||
Breadcrumb,
|
||||
TopNav,
|
||||
SubNav,
|
||||
Hamburger,
|
||||
Screenfull,
|
||||
// SizeSelect,
|
||||
@@ -74,7 +77,8 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
hasWarning: false,
|
||||
warningTimer: null
|
||||
warningTimer: null,
|
||||
showSubNav: true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -153,6 +157,8 @@ export default {
|
||||
height: 50px;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
// 金属质感渐变背景
|
||||
// background: #454c51;
|
||||
// border-bottom: 1px solid #8d939b;
|
||||
@@ -162,7 +168,7 @@ export default {
|
||||
.hamburger-container {
|
||||
line-height: 46px;
|
||||
height: 100%;
|
||||
float: left;
|
||||
flex-shrink: 0;
|
||||
cursor: pointer;
|
||||
transition: all .3s ease;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
@@ -177,10 +183,16 @@ export default {
|
||||
}
|
||||
|
||||
.breadcrumb-container {
|
||||
float: left;
|
||||
flex-shrink: 0;
|
||||
padding-left: 15px;
|
||||
}
|
||||
|
||||
.subnav-container {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
padding-left: 8px;
|
||||
}
|
||||
|
||||
.topmenu-container {
|
||||
position: absolute;
|
||||
left: 50px;
|
||||
@@ -192,7 +204,8 @@ export default {
|
||||
}
|
||||
|
||||
.right-menu {
|
||||
float: right;
|
||||
flex-shrink: 0;
|
||||
margin-left: auto;
|
||||
height: 100%;
|
||||
line-height: 50px;
|
||||
|
||||
|
||||
@@ -1,6 +1,23 @@
|
||||
<template>
|
||||
<div v-if="!item.hidden">
|
||||
<template v-if="hasOneShowingChild(item.children,item) && (!onlyOneChild.children||onlyOneChild.noShowingChildren)&&!item.alwaysShow">
|
||||
<template v-if="depth >= 1">
|
||||
<template v-if="hasVisibleChild">
|
||||
<app-link :to="subMenuRoute">
|
||||
<el-menu-item :index="basePath" :class="{'submenu-title-noDropdown':!isNest}">
|
||||
<item :icon="item.meta && item.meta.icon" :title="item.meta && item.meta.title" />
|
||||
</el-menu-item>
|
||||
</app-link>
|
||||
</template>
|
||||
<template v-else>
|
||||
<app-link :to="selfRoute">
|
||||
<el-menu-item :index="basePath" :class="{'submenu-title-noDropdown':!isNest, 'is-page-item': true}">
|
||||
<item :icon="item.meta && item.meta.icon" :title="item.meta && item.meta.title" />
|
||||
</el-menu-item>
|
||||
</app-link>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<template v-else-if="hasOneShowingChild(item.children,item) && (!onlyOneChild.children||onlyOneChild.noShowingChildren)&&!item.alwaysShow">
|
||||
<app-link v-if="onlyOneChild.meta" :to="resolvePath(onlyOneChild.path, onlyOneChild.query)">
|
||||
<el-menu-item :index="resolvePath(onlyOneChild.path)" :class="{'submenu-title-noDropdown':!isNest}">
|
||||
<item :icon="onlyOneChild.meta.icon||(item.meta&&item.meta.icon)" :title="onlyOneChild.meta.title" />
|
||||
@@ -18,6 +35,7 @@
|
||||
:is-nest="true"
|
||||
:item="child"
|
||||
:base-path="resolvePath(child.path)"
|
||||
:depth="depth + 1"
|
||||
class="nest-menu"
|
||||
/>
|
||||
</el-submenu>
|
||||
@@ -48,12 +66,38 @@ export default {
|
||||
basePath: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
depth: {
|
||||
type: Number,
|
||||
default: 0
|
||||
}
|
||||
},
|
||||
data() {
|
||||
this.onlyOneChild = null
|
||||
return {}
|
||||
},
|
||||
computed: {
|
||||
hasVisibleChild() {
|
||||
if (!this.item.children || !this.item.children.length) return false
|
||||
return this.item.children.some(c => !c.hidden)
|
||||
},
|
||||
selfRoute() {
|
||||
if (this.item.query) {
|
||||
return { path: this.basePath, query: JSON.parse(this.item.query) }
|
||||
}
|
||||
return this.basePath
|
||||
},
|
||||
subMenuRoute() {
|
||||
const title = (this.item.meta && this.item.meta.title) || this.item.name || ''
|
||||
return {
|
||||
path: '/redirect/subMenu',
|
||||
query: {
|
||||
parent: this.basePath,
|
||||
title: title
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
hasOneShowingChild(children = [], parent) {
|
||||
if (!children) {
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import path from 'path'
|
||||
import { mapGetters, mapState } from "vuex";
|
||||
import Logo from "./Logo";
|
||||
import SidebarItem from "./SidebarItem";
|
||||
@@ -36,12 +37,15 @@ export default {
|
||||
...mapGetters(["sidebarRouters", "sidebar"]),
|
||||
activeMenu() {
|
||||
const route = this.$route;
|
||||
const { meta, path } = route;
|
||||
// if set path, the sidebar will highlight the path you set
|
||||
const { meta, path: routePath } = route;
|
||||
if (meta.activeMenu) {
|
||||
return meta.activeMenu;
|
||||
}
|
||||
return path;
|
||||
const level2 = this.findLevel2Parent(routePath);
|
||||
if (level2) {
|
||||
return level2;
|
||||
}
|
||||
return routePath;
|
||||
},
|
||||
showLogo() {
|
||||
return this.$store.state.settings.sidebarLogo;
|
||||
@@ -53,5 +57,28 @@ export default {
|
||||
return !this.sidebar.opened;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
findLevel2Parent(currentPath) {
|
||||
return this.walkTree(this.sidebarRouters, currentPath, 0, '');
|
||||
},
|
||||
walkTree(routes, currentPath, depth, basePath) {
|
||||
for (const route of routes) {
|
||||
if (route.hidden) continue;
|
||||
const fullPath = basePath ? path.resolve(basePath, route.path) : route.path;
|
||||
if (currentPath !== fullPath && !currentPath.startsWith(fullPath + '/')) continue;
|
||||
if (depth >= 1) {
|
||||
return fullPath;
|
||||
}
|
||||
if (route.children && route.children.length) {
|
||||
const result = this.walkTree(route.children, currentPath, depth + 1, fullPath);
|
||||
if (result) return result;
|
||||
}
|
||||
if (currentPath === fullPath) {
|
||||
return depth >= 1 ? fullPath : null;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
306
klp-ui/src/layout/components/SubNav.vue
Normal file
306
klp-ui/src/layout/components/SubNav.vue
Normal file
@@ -0,0 +1,306 @@
|
||||
<template>
|
||||
<div v-if="subNavItems.length > 0" class="sub-nav">
|
||||
<span class="sub-nav__arrow" :class="{ 'is-disabled': scrollLeft <= 0 }" @click="scrollTo('left')">
|
||||
<i class="el-icon-arrow-left" />
|
||||
</span>
|
||||
<div ref="subNavWrap" class="sub-nav__wrap" @mousewheel="handleWheel">
|
||||
<div ref="subNavInner" class="sub-nav__inner">
|
||||
<template v-for="item in subNavItems">
|
||||
<el-dropdown
|
||||
v-if="item._hasChildren"
|
||||
:key="item._ownPath"
|
||||
trigger="click"
|
||||
class="sub-nav__dropdown"
|
||||
@command="handleCommand"
|
||||
>
|
||||
<span class="sub-nav__item sub-nav__item--dropdown" :class="{ 'is-active': isActive(item) }">
|
||||
{{ item._title }}
|
||||
<i class="el-icon-arrow-down el-icon--right" />
|
||||
</span>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item
|
||||
v-for="child in item._children"
|
||||
:key="child._ownPath"
|
||||
:command="child._fullPath"
|
||||
:class="{ 'is-active': isChildActive(child) }"
|
||||
>
|
||||
{{ child._title }}
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
<router-link
|
||||
v-else
|
||||
:key="item._ownPath"
|
||||
:to="item._fullPath"
|
||||
class="sub-nav__item"
|
||||
:class="{ 'is-active': isActive(item) }"
|
||||
>
|
||||
{{ item._title }}
|
||||
</router-link>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
<span class="sub-nav__arrow" :class="{ 'is-disabled': !canScrollRight }" @click="scrollTo('right')">
|
||||
<i class="el-icon-arrow-right" />
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import path from 'path'
|
||||
|
||||
export default {
|
||||
name: 'SubNav',
|
||||
data() {
|
||||
return {
|
||||
scrollLeft: 0,
|
||||
canScrollRight: false,
|
||||
resizeObserver: null
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
subNavItems() {
|
||||
const sidebarRouters = this.$store.state.permission.sidebarRouters
|
||||
const items = this.getThirdLevelMenus(sidebarRouters)
|
||||
return items
|
||||
},
|
||||
hasItems() {
|
||||
return this.subNavItems.length > 0
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
$route() {
|
||||
this.$nextTick(() => {
|
||||
this.checkArrows()
|
||||
this.scrollToActive()
|
||||
})
|
||||
},
|
||||
hasItems(val) {
|
||||
this.$emit('has-items', val)
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.$nextTick(() => {
|
||||
this.checkArrows()
|
||||
this.scrollToActive()
|
||||
})
|
||||
if (window.ResizeObserver) {
|
||||
this.resizeObserver = new ResizeObserver(() => {
|
||||
this.checkArrows()
|
||||
})
|
||||
if (this.$refs.subNavWrap) {
|
||||
this.resizeObserver.observe(this.$refs.subNavWrap)
|
||||
}
|
||||
}
|
||||
},
|
||||
beforeDestroy() {
|
||||
if (this.resizeObserver) {
|
||||
this.resizeObserver.disconnect()
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleCommand(command) {
|
||||
this.$router.push(command)
|
||||
},
|
||||
isActive(item) {
|
||||
return this.$route.path === item._ownPath || this.$route.path.startsWith(item._ownPath + '/')
|
||||
},
|
||||
isChildActive(child) {
|
||||
return this.$route.path === child._ownPath || this.$route.path.startsWith(child._ownPath + '/')
|
||||
},
|
||||
getThirdLevelMenus(routes) {
|
||||
let currentPath = this.$route.path
|
||||
if (currentPath === '/redirect/subMenu' && this.$route.query.parent) {
|
||||
currentPath = this.$route.query.parent
|
||||
}
|
||||
return this.findActiveLevel2(routes, currentPath, 0, '') || []
|
||||
},
|
||||
findActiveLevel2(routes, currentPath, depth, basePath) {
|
||||
for (const route of routes) {
|
||||
if (route.hidden) continue
|
||||
if (!route.children || route.children.length === 0) continue
|
||||
|
||||
const fullPath = basePath ? path.resolve(basePath, route.path) : route.path
|
||||
if (currentPath !== fullPath && !currentPath.startsWith(fullPath + '/')) continue
|
||||
|
||||
if (currentPath === fullPath && depth >= 1) {
|
||||
if (depth === 1) {
|
||||
return this.buildItems(route.children, fullPath)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
const nested = this.findActiveLevel2(route.children, currentPath, depth + 1, fullPath)
|
||||
if (nested) return nested
|
||||
|
||||
if (depth === 1) {
|
||||
return this.buildItems(route.children, fullPath)
|
||||
}
|
||||
}
|
||||
return null
|
||||
},
|
||||
buildItems(children, parentPath) {
|
||||
return children.filter(c => !c.hidden).map(c => this.buildItem(c, parentPath))
|
||||
},
|
||||
buildItem(c, parentPath) {
|
||||
const ownPath = c.path.startsWith('/') ? c.path : path.resolve(parentPath, c.path)
|
||||
const title = (c.meta && c.meta.title) || c.name || c.path
|
||||
const hasChildren = c.children && c.children.length > 0 && c.children.some(gc => !gc.hidden)
|
||||
const item = {
|
||||
...c,
|
||||
_ownPath: ownPath,
|
||||
_fullPath: ownPath,
|
||||
_title: title,
|
||||
_hasChildren: hasChildren
|
||||
}
|
||||
if (hasChildren) {
|
||||
item._children = this.buildItems(c.children, ownPath)
|
||||
}
|
||||
return item
|
||||
},
|
||||
handleWheel(e) {
|
||||
if (!this.$refs.subNavWrap) return
|
||||
const delta = e.deltaY || e.wheelDelta
|
||||
this.$refs.subNavWrap.scrollLeft += delta
|
||||
this.scrollLeft = this.$refs.subNavWrap.scrollLeft
|
||||
this.checkArrows()
|
||||
},
|
||||
scrollTo(direction) {
|
||||
if (!this.$refs.subNavWrap) return
|
||||
const wrap = this.$refs.subNavWrap
|
||||
const scrollAmount = 200
|
||||
if (direction === 'left') {
|
||||
wrap.scrollLeft = Math.max(0, wrap.scrollLeft - scrollAmount)
|
||||
} else {
|
||||
wrap.scrollLeft = Math.min(wrap.scrollWidth - wrap.clientWidth, wrap.scrollLeft + scrollAmount)
|
||||
}
|
||||
this.scrollLeft = wrap.scrollLeft
|
||||
this.checkArrows()
|
||||
},
|
||||
checkArrows() {
|
||||
if (!this.$refs.subNavWrap) return
|
||||
const wrap = this.$refs.subNavWrap
|
||||
this.scrollLeft = wrap.scrollLeft
|
||||
this.canScrollRight = wrap.scrollWidth > wrap.clientWidth + wrap.scrollLeft + 1
|
||||
},
|
||||
scrollToActive() {
|
||||
if (!this.$refs.subNavWrap) return
|
||||
const activeEl = this.$refs.subNavWrap.querySelector('.sub-nav__item.is-active')
|
||||
if (activeEl) {
|
||||
const wrap = this.$refs.subNavWrap
|
||||
const activeLeft = activeEl.offsetLeft
|
||||
const activeRight = activeLeft + activeEl.offsetWidth
|
||||
if (activeLeft < wrap.scrollLeft) {
|
||||
wrap.scrollLeft = activeLeft
|
||||
} else if (activeRight > wrap.scrollLeft + wrap.clientWidth) {
|
||||
wrap.scrollLeft = activeRight - wrap.clientWidth
|
||||
}
|
||||
this.scrollLeft = wrap.scrollLeft
|
||||
this.checkArrows()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.sub-nav {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 50px;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
padding: 0 4px;
|
||||
|
||||
&__arrow {
|
||||
flex-shrink: 0;
|
||||
width: 24px;
|
||||
height: 28px;
|
||||
line-height: 28px;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
border-radius: 4px;
|
||||
color: #606266;
|
||||
font-size: 12px;
|
||||
transition: all 0.2s;
|
||||
|
||||
&:hover:not(.is-disabled) {
|
||||
background: rgba(0, 0, 0, 0.06);
|
||||
color: #111;
|
||||
}
|
||||
|
||||
&.is-disabled {
|
||||
color: #c0c4cc;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
|
||||
&__wrap {
|
||||
flex: 1;
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
min-width: 0;
|
||||
scrollbar-width: none;
|
||||
-ms-overflow-style: none;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
&__inner {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
&__dropdown {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
&__item {
|
||||
flex-shrink: 0;
|
||||
padding: 6px 14px;
|
||||
margin: 0 2px;
|
||||
font-size: 13px;
|
||||
color: #606266;
|
||||
border-radius: 4px;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
white-space: nowrap;
|
||||
|
||||
&:hover {
|
||||
background: rgba(0, 0, 0, 0.05);
|
||||
color: #111;
|
||||
}
|
||||
|
||||
&.is-active {
|
||||
background: var(--current-color, #409EFF);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
&.is-active:hover {
|
||||
background: var(--current-color, #409EFF);
|
||||
color: #fff;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
&--dropdown {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
|
||||
.el-icon--right {
|
||||
margin-left: 2px;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
::v-deep .sub-nav__dropdown .el-dropdown-menu__item.is-active {
|
||||
color: var(--current-color, #409EFF);
|
||||
font-weight: 600;
|
||||
}
|
||||
</style>
|
||||
@@ -35,6 +35,12 @@ export const constantRoutes = [
|
||||
component: Layout,
|
||||
hidden: true,
|
||||
children: [
|
||||
{
|
||||
path: 'subMenu',
|
||||
component: () => import('@/views/redirectMenu'),
|
||||
name: 'RedirectMenu',
|
||||
meta: { title: '子菜单' }
|
||||
},
|
||||
{
|
||||
path: '/redirect/:path(.*)',
|
||||
component: () => import('@/views/redirect')
|
||||
@@ -115,7 +121,7 @@ export const constantRoutes = [
|
||||
},
|
||||
{
|
||||
path: 'coil/:coilId',
|
||||
component: () => import('@/views/wms/coil/info'),
|
||||
component: () => import('@/views/wms/coil/info/index'),
|
||||
name: 'CoilInfo',
|
||||
meta: { title: '数字钢卷', icon: 'checkbox' }
|
||||
},
|
||||
|
||||
@@ -23,5 +23,6 @@ const getters = {
|
||||
bomMap: state => state.category.bomMap,
|
||||
financialAccounts: state => state.finance.financialAccounts,
|
||||
processList: state => state.craft.processList,
|
||||
productionLines: state => state.productionLine.productionLines,
|
||||
}
|
||||
export default getters
|
||||
|
||||
@@ -9,6 +9,7 @@ import settings from './modules/settings'
|
||||
import category from './modules/category'
|
||||
import finance from './modules/finance'
|
||||
import craft from './modules/craft'
|
||||
import productionLine from './modules/productionLine'
|
||||
import getters from './getters'
|
||||
|
||||
Vue.use(Vuex)
|
||||
@@ -23,7 +24,8 @@ const store = new Vuex.Store({
|
||||
settings,
|
||||
category,
|
||||
finance,
|
||||
craft
|
||||
craft,
|
||||
productionLine
|
||||
},
|
||||
getters
|
||||
})
|
||||
|
||||
27
klp-ui/src/store/modules/productionLine.js
Normal file
27
klp-ui/src/store/modules/productionLine.js
Normal file
@@ -0,0 +1,27 @@
|
||||
import { listProductionLine } from "@/api/wms/productionLine";
|
||||
|
||||
const state = {
|
||||
productionLines: [],
|
||||
}
|
||||
|
||||
const mutations = {
|
||||
SET_PRODUCTION_LINES(state, list) {
|
||||
state.productionLines = list;
|
||||
},
|
||||
}
|
||||
|
||||
const actions = {
|
||||
getProductionLines({ commit, state }) {
|
||||
if (state.productionLines.length > 0) return Promise.resolve()
|
||||
return listProductionLine({ pageNum: 1, pageSize: 100 }).then(response => {
|
||||
commit('SET_PRODUCTION_LINES', response.rows || []);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
namespaced: true,
|
||||
state,
|
||||
mutations,
|
||||
actions
|
||||
}
|
||||
@@ -489,7 +489,7 @@ export default {
|
||||
actx.fillStyle = '#ffffff';
|
||||
actx.fillRect(0, 0, canvasPageW, canvasTotalH);
|
||||
|
||||
const imgScale = Math.min(canvasPageW / img.width, canvasImageH / img.height, 1);
|
||||
const imgScale = Math.min(canvasPageW / img.width, canvasImageH / img.height);
|
||||
const imgDrawW = img.width * imgScale;
|
||||
const imgDrawH = img.height * imgScale;
|
||||
const imgX = (canvasPageW - imgDrawW) / 2;
|
||||
|
||||
@@ -494,9 +494,9 @@
|
||||
<script>
|
||||
import { listRollInfo, getRollInfo, addRollInfo, updateRollInfo, delRollInfo } from '@/api/mes/roll/rollInfo'
|
||||
import { listRollGrind, addRollGrind, updateRollGrind, delRollGrind, getMonthlyStats } from '@/api/mes/roll/rollGrind'
|
||||
import { listProductionLine } from '@/api/wms/productionLine'
|
||||
import { listData, addData, updateData, delData } from '@/api/system/dict/data'
|
||||
import rollLineMixin from '../rollLineMixin'
|
||||
import { mapGetters } from 'vuex'
|
||||
|
||||
export default {
|
||||
name: 'GrindRoom',
|
||||
@@ -504,7 +504,6 @@ export default {
|
||||
dicts: ['mes_roll_operator'],
|
||||
data() {
|
||||
return {
|
||||
productionLines: [],
|
||||
filterLineId: null,
|
||||
lineTabOrder: [],
|
||||
|
||||
@@ -549,6 +548,8 @@ export default {
|
||||
},
|
||||
|
||||
computed: {
|
||||
...mapGetters(['productionLines']),
|
||||
|
||||
currentUserName() {
|
||||
return this.$store.state.user.name || this.$store.getters.name || ''
|
||||
},
|
||||
@@ -606,9 +607,7 @@ export default {
|
||||
this.filterLineId = this.lineId
|
||||
const uid = this.$store.state.user?.userId || 0
|
||||
try { this.lineTabOrder = JSON.parse(localStorage.getItem(`grind_line_order_${uid}`) || '[]') } catch { /* ignore */ }
|
||||
listProductionLine({ pageNum: 1, pageSize: 100 }).then(res => {
|
||||
this.productionLines = res.rows || []
|
||||
})
|
||||
this.$store.dispatch('productionLine/getProductionLines')
|
||||
this.loadRolls()
|
||||
},
|
||||
|
||||
|
||||
252
klp-ui/src/views/redirectMenu.vue
Normal file
252
klp-ui/src/views/redirectMenu.vue
Normal file
@@ -0,0 +1,252 @@
|
||||
<template>
|
||||
<div class="submenu-page">
|
||||
<div class="submenu-page__header">
|
||||
<span class="submenu-page__parent-title">{{ parentTitle }}</span>
|
||||
</div>
|
||||
<div v-if="tree.length" class="submenu-page__list">
|
||||
<div v-for="group in tree" :key="group._ownPath" class="submenu-group">
|
||||
<div class="submenu-group__title" @click="goTo(group)">
|
||||
<span class="submenu-group__icon">
|
||||
<svg-icon v-if="group.meta && group.meta.icon" :icon-class="group.meta.icon" />
|
||||
<i v-else class="el-icon-folder-opened" />
|
||||
</span>
|
||||
<span class="submenu-group__text">{{ group._title }}</span>
|
||||
<span v-if="group._hasChildren" class="submenu-group__count">{{ group._children.length }}项</span>
|
||||
<i v-if="group._hasChildren" class="el-icon-arrow-right submenu-group__arrow" />
|
||||
</div>
|
||||
<div v-if="group._hasChildren" class="submenu-group__items">
|
||||
<div
|
||||
v-for="child in group._children"
|
||||
:key="child._ownPath"
|
||||
class="submenu-item"
|
||||
:class="{ 'submenu-item--leaf': !child._hasChildren }"
|
||||
@click="goTo(child)"
|
||||
>
|
||||
<i v-if="child._hasChildren" class="el-icon-folder submenu-item__folder" />
|
||||
<i v-else class="el-icon-document submenu-item__doc" />
|
||||
<span class="submenu-item__text">{{ child._title }}</span>
|
||||
<i v-if="child._hasChildren" class="el-icon-arrow-right submenu-item__arrow" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="submenu-page__empty">暂无可访问的菜单</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import path from 'path'
|
||||
|
||||
export default {
|
||||
name: 'RedirectMenu',
|
||||
computed: {
|
||||
parentPath() {
|
||||
return this.$route.query.parent || ''
|
||||
},
|
||||
parentTitle() {
|
||||
return this.$route.query.title || this.parentPath
|
||||
},
|
||||
tree() {
|
||||
const sidebarRouters = this.$store.state.permission.sidebarRouters
|
||||
const children = this.findChildren(sidebarRouters, this.parentPath, '')
|
||||
if (!children) return []
|
||||
return this.buildTree(children, this.parentPath)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
findChildren(routes, targetPath, basePath) {
|
||||
for (const route of routes) {
|
||||
if (route.hidden) continue
|
||||
const fullPath = basePath ? path.resolve(basePath, route.path) : route.path
|
||||
if (fullPath === targetPath) {
|
||||
return route.children || []
|
||||
}
|
||||
if (route.children && route.children.length > 0) {
|
||||
const result = this.findChildren(route.children, targetPath, fullPath)
|
||||
if (result) return result
|
||||
}
|
||||
}
|
||||
return null
|
||||
},
|
||||
buildTree(children, parentPath) {
|
||||
return children
|
||||
.filter(c => !c.hidden)
|
||||
.map(c => {
|
||||
const ownPath = c.path.startsWith('/') ? c.path : path.resolve(parentPath, c.path)
|
||||
const title = (c.meta && c.meta.title) || c.name || c.path || ''
|
||||
const hasChildren = c.children && c.children.length > 0 && c.children.some(gc => !gc.hidden)
|
||||
const item = {
|
||||
...c,
|
||||
_ownPath: ownPath,
|
||||
_fullPath: ownPath,
|
||||
_title: title,
|
||||
_hasChildren: hasChildren
|
||||
}
|
||||
if (hasChildren) {
|
||||
item._children = this.buildTree(c.children, ownPath)
|
||||
}
|
||||
return item
|
||||
})
|
||||
},
|
||||
goTo(item) {
|
||||
if (item._hasChildren) {
|
||||
this.$router.push({
|
||||
path: '/redirect/subMenu',
|
||||
query: { parent: item._ownPath, title: item._title }
|
||||
})
|
||||
} else if (item.path) {
|
||||
this.$router.push(item._fullPath)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.submenu-page {
|
||||
padding: 16px 20px;
|
||||
max-width: 900px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.submenu-page__header {
|
||||
margin-bottom: 16px;
|
||||
padding-bottom: 12px;
|
||||
border-bottom: 1px solid #ebeef5;
|
||||
}
|
||||
|
||||
.submenu-page__parent-title {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
.submenu-page__list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.submenu-page__empty {
|
||||
text-align: center;
|
||||
padding: 40px 0;
|
||||
color: #909399;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.submenu-group {
|
||||
width: calc(50% - 6px);
|
||||
min-width: 280px;
|
||||
background: #fafbfc;
|
||||
border: 1px solid #ebeef5;
|
||||
border-radius: 6px;
|
||||
overflow: hidden;
|
||||
transition: border-color 0.2s;
|
||||
|
||||
&:hover {
|
||||
border-color: #c0c4cc;
|
||||
}
|
||||
}
|
||||
|
||||
.submenu-group__title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 12px 16px;
|
||||
cursor: pointer;
|
||||
background: #fff;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
transition: background 0.15s;
|
||||
|
||||
&:hover {
|
||||
background: #f5f7fa;
|
||||
}
|
||||
}
|
||||
|
||||
.submenu-group__icon {
|
||||
font-size: 16px;
|
||||
color: var(--current-color, #409EFF);
|
||||
margin-right: 8px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.submenu-group__text {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: #303133;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.submenu-group__count {
|
||||
font-size: 11px;
|
||||
color: #909399;
|
||||
margin-right: 6px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.submenu-group__arrow {
|
||||
font-size: 12px;
|
||||
color: #c0c4cc;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.submenu-group__items {
|
||||
padding: 4px 0;
|
||||
}
|
||||
|
||||
.submenu-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 8px 16px 8px 24px;
|
||||
cursor: pointer;
|
||||
transition: background 0.15s;
|
||||
|
||||
&:hover {
|
||||
background: #f0f2f5;
|
||||
}
|
||||
|
||||
&:not(:last-child) {
|
||||
border-bottom: 1px solid #f5f5f5;
|
||||
}
|
||||
}
|
||||
|
||||
.submenu-item__folder {
|
||||
font-size: 13px;
|
||||
color: #e6a23c;
|
||||
margin-right: 8px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.submenu-item__doc {
|
||||
font-size: 13px;
|
||||
color: #909399;
|
||||
margin-right: 8px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.submenu-item__text {
|
||||
font-size: 13px;
|
||||
color: #606266;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.submenu-item__arrow {
|
||||
font-size: 12px;
|
||||
color: #c0c4cc;
|
||||
flex-shrink: 0;
|
||||
margin-left: 4px;
|
||||
}
|
||||
|
||||
.submenu-item--leaf {
|
||||
.submenu-item__doc {
|
||||
color: #c0c4cc;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
File diff suppressed because it is too large
Load Diff
1286
klp-ui/src/views/wms/coil/info/coil-info.scss
Normal file
1286
klp-ui/src/views/wms/coil/info/coil-info.scss
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,20 @@
|
||||
<template>
|
||||
<div class="section basic-info-section">
|
||||
<div class="section-header">
|
||||
<span class="section-icon">📦</span>
|
||||
<span class="section-title">基本信息</span>
|
||||
</div>
|
||||
<div class="section-body">
|
||||
<CoilInfoRender :coilInfo="coilInfo" :column="5" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'BasicInfoSection',
|
||||
props: {
|
||||
coilInfo: { type: Object, default: () => ({}) }
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,85 @@
|
||||
<template>
|
||||
<div :class="['coil-card-hover', variant]" v-if="coil && coil.currentCoilNo !== '-'">
|
||||
<div class="coil-mini-header">
|
||||
<span class="coil-mini-no">{{ coil.currentCoilNo }}</span>
|
||||
<span class="coil-mini-weight">{{ coil.netWeight ? coil.netWeight + 't' : '-' }}</span>
|
||||
</div>
|
||||
<div class="coil-mini-body">
|
||||
<span class="coil-mini-spec">{{ coil.specification || '-' }}</span>
|
||||
<span class="coil-mini-material">{{ coil.material || '-' }}</span>
|
||||
<span :class="['coil-mini-status', statusClass(coil.qualityStatus)]">{{ coil.qualityStatus || '-' }}</span>
|
||||
</div>
|
||||
<div class="coil-detail-popup">
|
||||
<div class="popup-header">
|
||||
<span class="popup-title">钢卷详情</span>
|
||||
<span class="popup-close">✕</span>
|
||||
</div>
|
||||
<div class="popup-content">
|
||||
<div class="popup-row">
|
||||
<span class="popup-label">入场卷号</span>
|
||||
<span class="popup-value">{{ coil.enterCoilNo || '-' }}</span>
|
||||
</div>
|
||||
<div class="popup-row">
|
||||
<span class="popup-label">当前卷号</span>
|
||||
<span class="popup-value">{{ coil.currentCoilNo || '-' }}</span>
|
||||
</div>
|
||||
<div class="popup-row">
|
||||
<span class="popup-label">物料名称</span>
|
||||
<span class="popup-value">{{ coil.itemName || '-' }}</span>
|
||||
</div>
|
||||
<div class="popup-row">
|
||||
<span class="popup-label">规格</span>
|
||||
<span class="popup-value">{{ coil.specification || '-' }}</span>
|
||||
</div>
|
||||
<div class="popup-row">
|
||||
<span class="popup-label">材质</span>
|
||||
<span class="popup-value">{{ coil.material || '-' }}</span>
|
||||
</div>
|
||||
<div class="popup-row">
|
||||
<span class="popup-label">净重</span>
|
||||
<span class="popup-value">{{ coil.netWeight || 0 }} t</span>
|
||||
</div>
|
||||
<div class="popup-row">
|
||||
<span class="popup-label">生产厂家</span>
|
||||
<span class="popup-value">{{ coil.manufacturer || '-' }}</span>
|
||||
</div>
|
||||
<div class="popup-row">
|
||||
<span class="popup-label">镀层</span>
|
||||
<span class="popup-value">{{ coil.zincLayer || '-' }}</span>
|
||||
</div>
|
||||
<div class="popup-row">
|
||||
<span class="popup-label">质量状态</span>
|
||||
<span class="popup-value">{{ coil.qualityStatus || '-' }}</span>
|
||||
</div>
|
||||
<div class="popup-row">
|
||||
<span class="popup-label">逻辑库位</span>
|
||||
<span class="popup-value">{{ coil.warehouseName || '-' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getFuturisticStatusClass } from '../statusUtils'
|
||||
|
||||
export default {
|
||||
name: 'CoilCardCompact',
|
||||
props: {
|
||||
coil: { type: Object, default: () => ({}) },
|
||||
variant: { type: String, default: '' }
|
||||
},
|
||||
methods: {
|
||||
statusClass(status) {
|
||||
const cls = getFuturisticStatusClass(status)
|
||||
const map = {
|
||||
'futuristic-status-success': 'status-success',
|
||||
'futuristic-status-danger': 'status-danger',
|
||||
'futuristic-status-warning': 'status-warning',
|
||||
'futuristic-status-default': 'status-default'
|
||||
}
|
||||
return map[cls] || 'status-default'
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
153
klp-ui/src/views/wms/coil/info/components/CoilCardFuturistic.vue
Normal file
153
klp-ui/src/views/wms/coil/info/components/CoilCardFuturistic.vue
Normal file
@@ -0,0 +1,153 @@
|
||||
<template>
|
||||
<div class="coil-card-futuristic">
|
||||
<div class="coil-futuristic-header">
|
||||
<span class="coil-futuristic-no">{{ coil.currentCoilNo || '-' }}</span>
|
||||
<span class="coil-futuristic-weight">{{ coil.netWeight ? coil.netWeight + ' t' : '-' }}</span>
|
||||
</div>
|
||||
<div class="coil-futuristic-body">
|
||||
<div class="coil-icon-container">
|
||||
<svg class="coil-svg" viewBox="0 0 200 200">
|
||||
<defs>
|
||||
<linearGradient :id="gradId1" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" :style="'stop-color:' + gradColor1 + ';stop-opacity:0.5'" />
|
||||
<stop offset="50%" :style="'stop-color:' + gradColor2 + ';stop-opacity:0.4'" />
|
||||
<stop offset="100%" :style="'stop-color:' + gradColor1 + ';stop-opacity:0.5'" />
|
||||
</linearGradient>
|
||||
<linearGradient :id="gradId2" x1="0%" y1="0%" x2="100%" y2="0%">
|
||||
<stop offset="0%" :style="'stop-color:' + gradColor3 + ';stop-opacity:0.4'" />
|
||||
<stop offset="100%" :style="'stop-color:' + gradColor4 + ';stop-opacity:0.3'" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<circle cx="100" cy="100" r="70" fill="none" :stroke="'url(#' + gradId1 + ')'" stroke-width="2.5">
|
||||
<animate attributeName="r" values="70;72;70" dur="2s" repeatCount="indefinite" />
|
||||
</circle>
|
||||
<circle cx="100" cy="100" r="60" fill="none" :stroke="'url(#' + gradId2 + ')'" stroke-width="1.8" opacity="0.6" :stroke-dasharray="dashArray">
|
||||
<animate attributeName="stroke-dashoffset" values="0;377" dur="4s" repeatCount="indefinite" />
|
||||
</circle>
|
||||
<circle cx="100" cy="100" r="45" :fill="'url(#' + gradId1 + ')'" opacity="0.15">
|
||||
<animate attributeName="opacity" values="0.15;0.3;0.15" dur="3s" repeatCount="indefinite" />
|
||||
</circle>
|
||||
<circle cx="100" cy="100" r="35" :fill="'url(#' + gradId2 + ')'" opacity="0.2" />
|
||||
<circle cx="100" cy="100" r="25" :fill="'url(#' + gradId1 + ')'" opacity="0.25" />
|
||||
<circle cx="100" cy="100" r="15" fill="white" opacity="0.4" />
|
||||
<line x1="100" y1="100" x2="100" y2="30" :stroke="'url(#' + gradId1 + ')'" stroke-width="1" opacity="0.4" />
|
||||
<line x1="100" y1="100" x2="170" y2="100" :stroke="'url(#' + gradId1 + ')'" stroke-width="1" opacity="0.4" />
|
||||
<line x1="100" y1="100" x2="100" y2="170" :stroke="'url(#' + gradId1 + ')'" stroke-width="1" opacity="0.4" />
|
||||
<line x1="100" y1="100" x2="30" y2="100" :stroke="'url(#' + gradId1 + ')'" stroke-width="1" opacity="0.4" />
|
||||
<circle cx="100" cy="100" r="4" :fill="gradColor1">
|
||||
<animate attributeName="r" values="4;6;4" dur="1.5s" repeatCount="indefinite" />
|
||||
</circle>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="coil-attr-tags">
|
||||
<div class="attr-tag attr-top">
|
||||
<div class="attr-content">
|
||||
<span class="attr-label">材质</span>
|
||||
<span class="attr-value">{{ coil.material || '-' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="attr-tag attr-right">
|
||||
<div class="attr-content">
|
||||
<span class="attr-label">规格</span>
|
||||
<span class="attr-value">{{ coil.specification || '-' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="attr-tag attr-left">
|
||||
<div class="attr-content">
|
||||
<span class="attr-label">{{ leftLabel }}</span>
|
||||
<span class="attr-value" :class="statusClass(coil.qualityStatus)">{{ coil.qualityStatus || '-' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="coil.enterCoilNo && coil.enterCoilNo !== '-'" class="attr-tag attr-top-left">
|
||||
<div class="attr-content attr-content-small">
|
||||
<span class="attr-label">入场卷号</span>
|
||||
<span class="attr-value">{{ coil.enterCoilNo }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="coil.supplierCoilNo && coil.supplierCoilNo !== '-'" class="attr-tag attr-top-right">
|
||||
<div class="attr-content attr-content-small">
|
||||
<span class="attr-label">厂家原料号</span>
|
||||
<span class="attr-value">{{ coil.supplierCoilNo }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="attr-tag attr-bottom-left">
|
||||
<div class="attr-content attr-content-small">
|
||||
<span class="attr-label">班组</span>
|
||||
<span class="attr-value">{{ coil.team || '-' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="attr-tag attr-bottom-right">
|
||||
<div class="attr-content attr-content-small">
|
||||
<span class="attr-label">厂家</span>
|
||||
<span class="attr-value">{{ coil.manufacturer || '-' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="attr-tag attr-remark">
|
||||
<div class="attr-content attr-content-remark">
|
||||
<span class="attr-label">备注</span>
|
||||
<span class="attr-value">{{ coil.remark || '-' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="coil-futuristic-footer">
|
||||
<div class="footer-item">
|
||||
<span class="footer-label">镀层</span>
|
||||
<span class="footer-value">{{ coil.zincLayer || '-' }}</span>
|
||||
</div>
|
||||
<div class="footer-item">
|
||||
<span class="footer-label">表面处理</span>
|
||||
<span class="footer-value">{{ coil.surfaceTreatmentDesc || '-' }}</span>
|
||||
</div>
|
||||
<div class="footer-item">
|
||||
<span class="footer-label">逻辑库位</span>
|
||||
<span class="footer-value">{{ coil.warehouseName || '-' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getFuturisticStatusClass } from '../statusUtils'
|
||||
|
||||
let uid = 0
|
||||
|
||||
export default {
|
||||
name: 'CoilCardFuturistic',
|
||||
props: {
|
||||
coil: { type: Object, default: () => ({}) },
|
||||
type: { type: String, default: 'inbound' }
|
||||
},
|
||||
data() {
|
||||
uid++
|
||||
return { _uid: uid }
|
||||
},
|
||||
computed: {
|
||||
gradId1() { return 'coilGradA_' + this._uid },
|
||||
gradId2() { return 'coilGradB_' + this._uid },
|
||||
gradConfig() {
|
||||
const configs = {
|
||||
inbound: { c1: '#3b82f6', c2: '#2563eb', c3: '#93c5fd', c4: '#bfdbfe' },
|
||||
old: { c1: '#6366f1', c2: '#8b5cf6', c3: '#a78bfa', c4: '#c084fc' },
|
||||
new: { c1: '#10b981', c2: '#34d399', c3: '#a7f3d0', c4: '#fbcfe8' },
|
||||
}
|
||||
return configs[this.type] || configs.inbound
|
||||
},
|
||||
gradColor1() { return this.gradConfig.c1 },
|
||||
gradColor2() { return this.gradConfig.c2 },
|
||||
gradColor3() { return this.gradConfig.c3 },
|
||||
gradColor4() { return this.gradConfig.c4 },
|
||||
dashArray() {
|
||||
return this.type === 'new' ? '50 50' : undefined
|
||||
},
|
||||
leftLabel() {
|
||||
return this.type === 'inbound' ? '质量状态' : '状态'
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
statusClass(status) {
|
||||
return getFuturisticStatusClass(status)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
43
klp-ui/src/views/wms/coil/info/components/ContractInfo.vue
Normal file
43
klp-ui/src/views/wms/coil/info/components/ContractInfo.vue
Normal file
@@ -0,0 +1,43 @@
|
||||
<template>
|
||||
<div class="section sales-section">
|
||||
<div class="section-header">
|
||||
<span class="section-icon">💰</span>
|
||||
<span class="section-title">{{ title }}</span>
|
||||
</div>
|
||||
<div class="section-body" v-if="info.orderId">
|
||||
<el-descriptions :column="3" border size="small">
|
||||
<el-descriptions-item label="合同号">{{ info.contractCode || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="客户名称">{{ info.customer || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="销售人员">{{ info.salesman || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="签订时间">{{ info.signTime || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="交货时间">{{ info.deliveryTime || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="订单总额">{{ info.orderAmount || '-' }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
|
||||
<h4>技术附件</h4>
|
||||
<FileList :oss-ids="info.techAnnex" />
|
||||
|
||||
<h4>排产函</h4>
|
||||
<FileList :oss-ids="info.productionSchedule" />
|
||||
</div>
|
||||
|
||||
<div v-else class="empty-state">
|
||||
<i class="el-icon-document"></i>
|
||||
<span>{{ emptyText }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import FileList from '@/components/FileList'
|
||||
|
||||
export default {
|
||||
name: 'ContractInfo',
|
||||
components: { FileList },
|
||||
props: {
|
||||
title: { type: String, default: '合同信息' },
|
||||
info: { type: Object, default: () => ({}) },
|
||||
emptyText: { type: String, default: '未找到相关合同信息' }
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,36 @@
|
||||
<template>
|
||||
<div class="section basic-info-section">
|
||||
<div class="section-header">
|
||||
<span class="section-icon">💰</span>
|
||||
<span class="section-title">成本信息</span>
|
||||
</div>
|
||||
<div class="section-body">
|
||||
<el-descriptions :column="3" border size="small">
|
||||
<el-descriptions-item label="囤积成本">
|
||||
<span class="cost-value">{{ hoardingCost }}</span>
|
||||
<span class="cost-unit">t·天</span>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="囤积天数">
|
||||
<span>{{ hoardingDays }} 天</span>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="钢卷净重">
|
||||
<span>{{ coilInfo.netWeight || 0 }} t</span>
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { formatTime } from '../statusUtils'
|
||||
|
||||
export default {
|
||||
name: 'CostInfoSection',
|
||||
props: {
|
||||
coilInfo: { type: Object, default: () => ({}) },
|
||||
traceResult: { type: Object, default: null },
|
||||
hoardingDays: { type: Number, default: 0 },
|
||||
hoardingCost: { type: [String, Number], default: '0.00' }
|
||||
}
|
||||
}
|
||||
</script>
|
||||
110
klp-ui/src/views/wms/coil/info/components/InspectionInfo.vue
Normal file
110
klp-ui/src/views/wms/coil/info/components/InspectionInfo.vue
Normal file
@@ -0,0 +1,110 @@
|
||||
<template>
|
||||
<div class="section inspection-section">
|
||||
<div class="section-header">
|
||||
<span class="section-icon">🔬</span>
|
||||
<span class="section-title">检验信息</span>
|
||||
</div>
|
||||
<div class="section-body">
|
||||
<el-table :data="taskList" v-loading="loading" size="small" border stripe style="width: 100%"
|
||||
@expand-change="onExpand" :row-key="row => row.taskId">
|
||||
<el-table-column type="expand">
|
||||
<template slot-scope="props">
|
||||
<div v-loading="itemLoadingMap[props.row.taskId]" style="padding: 8px;">
|
||||
<el-table :data="itemMap[props.row.taskId] || []" size="mini" border stripe style="width: 100%">
|
||||
<el-table-column label="检验项目名称" align="center" prop="itemName" min-width="120" />
|
||||
<el-table-column label="标准值" align="center" prop="standardValue" width="80" />
|
||||
<el-table-column label="上限" align="center" prop="upperLimit" width="80" />
|
||||
<el-table-column label="下限" align="center" prop="lowerLimit" width="80" />
|
||||
<el-table-column label="单位" align="center" prop="unit" width="60" />
|
||||
<el-table-column label="定性/定量" align="center" prop="itemType" width="80" />
|
||||
<el-table-column label="检验值" align="center" prop="inspectValue" width="100" />
|
||||
<el-table-column label="是否合格" align="center" prop="isQualified" width="80" />
|
||||
<el-table-column label="判定结果" align="center" prop="judgeResult" width="100" />
|
||||
<el-table-column label="检验人" align="center" prop="inspectUser" width="80" />
|
||||
<el-table-column label="检验时间" align="center" prop="inspectTime" width="160">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ parseTime(scope.row.inspectTime, '{y}-{m}-{d}') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="备注" align="center" prop="remark" min-width="100" />
|
||||
</el-table>
|
||||
<div v-if="!itemMap[props.row.taskId] || itemMap[props.row.taskId].length === 0"
|
||||
style="text-align:center;padding:16px;color:#999;font-size:13px;">
|
||||
暂无检验明细
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="任务编号" align="center" prop="taskCode" width="160" />
|
||||
<el-table-column label="任务类型" align="center" prop="taskType" width="100" />
|
||||
<el-table-column label="所属单位" align="center" prop="belongCompany" width="120" />
|
||||
<el-table-column label="方案名称" align="center" prop="schemeName" min-width="120" />
|
||||
<el-table-column label="状态" align="center" prop="status" width="90">
|
||||
<template slot-scope="scope">
|
||||
<el-tag v-if="scope.row.status === 0 || scope.row.status === '0'" type="info" size="small">待检验</el-tag>
|
||||
<el-tag v-else-if="scope.row.status === 1 || scope.row.status === '1'" type="warning" size="small">已检验</el-tag>
|
||||
<el-tag v-else-if="scope.row.status === 2 || scope.row.status === '2'" type="success" size="small">已审核</el-tag>
|
||||
<span v-else>{{ scope.row.status }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="检验人" align="center" prop="inspectUser" width="80" />
|
||||
<el-table-column label="审核人" align="center" prop="auditUser" width="80" />
|
||||
<el-table-column label="最终结果" align="center" prop="result" min-width="100" />
|
||||
<el-table-column label="关联钢卷" align="center" width="140">
|
||||
<template slot-scope="scope">
|
||||
<div v-if="scope.row.coilList && scope.row.coilList.length > 0"
|
||||
style="display: flex; flex-wrap: wrap; gap: 4px;">
|
||||
<div v-for="(coil, index) in scope.row.coilList" :key="coil.coilId || index">
|
||||
<CurrentCoilNo :currentCoilNo="coil.currentCoilNo || coil.coilNo || ''" />
|
||||
</div>
|
||||
</div>
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="备注" align="center" prop="remark" min-width="100" show-overflow-tooltip />
|
||||
</el-table>
|
||||
<div v-if="taskList.length === 0 && !loading"
|
||||
style="text-align:center;padding:24px 0;color:#999;font-size:14px;">
|
||||
<i class="el-icon-document" style="font-size:48px;display:block;margin-bottom:8px;color:#ddd;"></i>
|
||||
未找到相关检验信息
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { listInspectionItem } from '@/api/mes/qc/inspectionItem'
|
||||
import CurrentCoilNo from '@/components/KLPService/Renderer/CurrentCoilNo.vue'
|
||||
|
||||
export default {
|
||||
name: 'InspectionInfo',
|
||||
components: { CurrentCoilNo },
|
||||
props: {
|
||||
taskList: { type: Array, default: () => [] },
|
||||
loading: { type: Boolean, default: false }
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
itemMap: {},
|
||||
itemLoadingMap: {}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async onExpand(row, expandedRows) {
|
||||
if (!expandedRows || !expandedRows.includes(row)) return
|
||||
const taskId = row.taskId
|
||||
if (this.itemMap[taskId]) return
|
||||
this.$set(this.itemLoadingMap, taskId, true)
|
||||
try {
|
||||
const res = await listInspectionItem({ taskId, pageNum: 1, pageSize: 100 })
|
||||
this.$set(this.itemMap, taskId, res.rows || [])
|
||||
} catch (e) {
|
||||
console.error('获取检验明细异常:', e)
|
||||
this.$set(this.itemMap, taskId, [])
|
||||
} finally {
|
||||
this.$set(this.itemLoadingMap, taskId, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
158
klp-ui/src/views/wms/coil/info/components/LifecycleTrace.vue
Normal file
158
klp-ui/src/views/wms/coil/info/components/LifecycleTrace.vue
Normal file
@@ -0,0 +1,158 @@
|
||||
<template>
|
||||
<div class="section trace-section">
|
||||
<div class="section-header">
|
||||
<span class="section-icon">🔃</span>
|
||||
<span class="section-title">生命周期追踪</span>
|
||||
</div>
|
||||
<div class="section-body">
|
||||
<coil-trace-result :trace-result="traceResult">
|
||||
<template #stepBody="{ step, compact }">
|
||||
<div class="step-item">
|
||||
<template v-if="step.action === '创建'">
|
||||
<div v-if="compact" class="step-content step-content-compact step-content-inbound">
|
||||
<div class="coil-card-wrapper inbound-coil-wrapper">
|
||||
<div class="coil-card-header inbound-header">
|
||||
<span class="card-label">📦 入库</span>
|
||||
</div>
|
||||
<CoilCardCompact v-for="(coil, idx) in step.newCoilInfoList" :key="idx" :coil="coil"
|
||||
v-if="coil && coil.currentCoilNo !== '-'" variant="inbound" />
|
||||
<div v-if="isEmpty(step.newCoilInfoList)" class="empty-coil">
|
||||
<span>无钢卷信息</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="action-center-compact">
|
||||
<div class="action-arrow action-arrow-inbound">
|
||||
<i class="el-icon-download"></i>
|
||||
</div>
|
||||
<div class="action-info">
|
||||
<el-tag size="mini" type="primary" class="action-tag">入库</el-tag>
|
||||
<span class="action-operator">{{ step.operation }}</span>
|
||||
<span class="action-time">{{ step.time }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-else class="step-content step-content-inbound">
|
||||
<div class="coil-card-wrapper inbound-coil-wrapper">
|
||||
<div class="coil-card-header inbound-header">
|
||||
<span class="card-label">📦 入库</span>
|
||||
</div>
|
||||
<CoilCardFuturistic v-for="(coil, idx) in step.newCoilInfoList" :key="idx" :coil="coil" type="inbound"
|
||||
v-if="coil && coil.currentCoilNo !== '-'" />
|
||||
<div v-if="isEmpty(step.newCoilInfoList)" class="empty-coil">
|
||||
<span>无钢卷信息</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="action-center">
|
||||
<div class="action-arrow action-arrow-inbound">
|
||||
<i class="el-icon-download"></i>
|
||||
</div>
|
||||
<div class="action-info">
|
||||
<el-tag size="mini" type="primary" class="action-tag">入库</el-tag>
|
||||
<span class="action-operator">{{ step.operation }}</span>
|
||||
<span class="action-time">{{ step.time }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template v-else>
|
||||
<div v-if="compact" class="step-content-compact-change">
|
||||
<div class="coil-change-wrapper">
|
||||
<div class="coil-change-header">
|
||||
<span class="change-label">变更前</span>
|
||||
</div>
|
||||
<CoilCardCompact v-for="(coil, idx) in step.oldCoilInfoList" :key="idx" :coil="coil" variant="old"
|
||||
v-if="coil && coil.currentCoilNo !== '-'" />
|
||||
<div v-if="isEmpty(step.oldCoilInfoList)" class="empty-coil">
|
||||
<span>无钢卷信息</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="action-center-compact">
|
||||
<div class="action-arrow">
|
||||
<i class="el-icon-arrow-down"></i>
|
||||
</div>
|
||||
<div class="action-info">
|
||||
<el-tag size="mini" type="info" class="action-tag">{{ step.action }}</el-tag>
|
||||
<span class="action-operator">{{ step.operation }}</span>
|
||||
<span class="action-time">{{ step.time }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="coil-change-wrapper new">
|
||||
<div class="coil-change-header">
|
||||
<span class="change-label">变更后</span>
|
||||
</div>
|
||||
<CoilCardCompact v-for="(coil, idx) in step.newCoilInfoList" :key="idx" :coil="coil" variant="new"
|
||||
v-if="coil && coil.currentCoilNo !== '-'" />
|
||||
<div v-if="isEmpty(step.newCoilInfoList)" class="empty-coil">
|
||||
<span>无钢卷信息</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-else class="step-content">
|
||||
<div class="coil-card-wrapper old-coil-wrapper">
|
||||
<div class="coil-card-header">
|
||||
<span class="card-label">变更前</span>
|
||||
</div>
|
||||
<CoilCardFuturistic v-for="(coil, idx) in step.oldCoilInfoList" :key="idx" :coil="coil" type="old"
|
||||
v-if="coil && coil.currentCoilNo !== '-'" />
|
||||
<div v-if="isEmpty(step.oldCoilInfoList)" class="empty-coil">
|
||||
<span>无钢卷信息</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="action-center">
|
||||
<div class="action-arrow">
|
||||
<i class="el-icon-right"></i>
|
||||
</div>
|
||||
<div class="action-info">
|
||||
<el-tag size="mini" type="info" class="action-tag">{{ step.action }}</el-tag>
|
||||
<span class="action-operator">{{ step.operation }}</span>
|
||||
<span class="action-time">{{ step.time }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="coil-card-wrapper new-coil-wrapper">
|
||||
<div class="coil-card-header">
|
||||
<span class="card-label">变更后</span>
|
||||
</div>
|
||||
<CoilCardFuturistic v-for="(coil, idx) in step.newCoilInfoList" :key="idx" :coil="coil" type="new"
|
||||
v-if="coil && coil.currentCoilNo !== '-'" />
|
||||
<div v-if="isEmpty(step.newCoilInfoList)" class="empty-coil">
|
||||
<span>无钢卷信息</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
</coil-trace-result>
|
||||
|
||||
<ShipmentCard :coilInfo="coilInfo" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import CoilTraceResult from '@/views/wms/coil/panels/CoilTraceResult.vue'
|
||||
import CoilCardFuturistic from './CoilCardFuturistic.vue'
|
||||
import CoilCardCompact from './CoilCardCompact.vue'
|
||||
import ShipmentCard from './ShipmentCard.vue'
|
||||
|
||||
export default {
|
||||
name: 'LifecycleTrace',
|
||||
components: { CoilTraceResult, CoilCardFuturistic, CoilCardCompact, ShipmentCard },
|
||||
props: {
|
||||
traceResult: { type: Object, default: null },
|
||||
coilInfo: { type: Object, default: () => ({}) }
|
||||
},
|
||||
methods: {
|
||||
isEmpty(list) {
|
||||
return !list || list.length === 0 || list.every(c => !c || c.currentCoilNo === '-')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
434
klp-ui/src/views/wms/coil/info/components/ProductionCharts.vue
Normal file
434
klp-ui/src/views/wms/coil/info/components/ProductionCharts.vue
Normal file
@@ -0,0 +1,434 @@
|
||||
<template>
|
||||
<div class="section production-section">
|
||||
<div class="section-header">
|
||||
<span class="section-icon">📈</span>
|
||||
<span class="section-title">生产工艺数据</span>
|
||||
<el-tag v-if="segLoading || realtimeLoading" size="mini" type="info" style="margin-left:8px">加载中…</el-tag>
|
||||
<span v-if="hasPerfData" class="perf-count">({{ perfSegCount }} 段)</span>
|
||||
</div>
|
||||
<div class="section-body">
|
||||
<el-tabs v-model="activeTab" size="small" class="perf-tabs" @tab-click="handleTabSwitch">
|
||||
|
||||
<el-tab-pane label="趋势参数" name="trend">
|
||||
<div v-if="!hasPerfData && !segLoading" class="no-data-hint">暂无生产数据</div>
|
||||
<div v-else-if="segLoading" class="no-data-hint">加载中…</div>
|
||||
<div v-else class="trend-layout">
|
||||
<div class="trend-tree">
|
||||
<div v-for="group in trendGroups" :key="group.label" class="tree-group">
|
||||
<div class="tree-group-label" @click="toggleGroup(group.label)">
|
||||
<i :class="expandedGroups[group.label] ? 'el-icon-caret-bottom' : 'el-icon-caret-right'" />
|
||||
{{ group.label }}
|
||||
</div>
|
||||
<div v-show="expandedGroups[group.label]" class="tree-children">
|
||||
<div v-for="item in group.children" :key="item.col" class="tree-item"
|
||||
:class="{ active: selectedParam && selectedParam.col === item.col }"
|
||||
@click="selectParam(item)">{{ item.label }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="trend-chart-area">
|
||||
<div v-if="!selectedParam" class="no-data-hint">← 点击左侧参数查看曲线</div>
|
||||
<div ref="trendChart"
|
||||
:style="{ display: selectedParam ? 'block' : 'none', height: '100%', width: '100%' }" />
|
||||
</div>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="厚度曲线" name="thickness">
|
||||
<div v-if="!gaugeRows || !gaugeRows.length" class="no-data-hint">暂无厚度数据</div>
|
||||
<div v-else class="charts-scroll charts-grid">
|
||||
<div ref="chartGauge1" class="chart-box" />
|
||||
<div ref="chartGauge2" class="chart-box" />
|
||||
<div ref="chartGauge3" class="chart-box" />
|
||||
<div ref="chartGauge4" class="chart-box" />
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="带钢板形" name="flatness3d">
|
||||
<div v-if="!shapeRows || !shapeRows.length" class="no-data-hint">暂无板形数据</div>
|
||||
<div v-else class="charts-scroll">
|
||||
<div ref="chartFlatness3d" class="chart-box chart-box-tall" />
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="板形曲线" name="flatness">
|
||||
<div v-if="!shapeRows || !shapeRows.length" class="no-data-hint">暂无板形数据</div>
|
||||
<div v-else class="charts-scroll charts-grid">
|
||||
<div ref="chartFlatDev" class="chart-box" />
|
||||
<div ref="chartTilt" class="chart-box" />
|
||||
<div ref="chartWrBend" class="chart-box" />
|
||||
<div ref="chartIrBend" class="chart-box" />
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import * as echarts from 'echarts'
|
||||
import 'echarts-gl'
|
||||
|
||||
const TREND_GROUPS = [
|
||||
{
|
||||
label: '张力',
|
||||
children: [
|
||||
{ label: '开卷张力', col: 'PORTENS' },
|
||||
{ label: '入口活套张力', col: 'ENLTENS' },
|
||||
{ label: '拉矫张力', col: 'TLTENS' },
|
||||
{ label: '酸洗张力', col: 'PLTENS' },
|
||||
{ label: '出口活套张力', col: 'CXLTENS' },
|
||||
{ label: '圆盘剪张力', col: 'TRIMTENS' }
|
||||
]
|
||||
},
|
||||
{
|
||||
label: '速度',
|
||||
children: [
|
||||
{ label: '开卷速度', col: 'PORSPEED' },
|
||||
{ label: '酸洗速度', col: 'PLSPEED' },
|
||||
{ label: '圆盘剪速度', col: 'TRIMSPEED' },
|
||||
{ label: '轧机入口速度', col: 'MILLENTRYSPEED' },
|
||||
{ label: '轧机出口速度', col: 'MILLEXITSPEED' }
|
||||
]
|
||||
},
|
||||
{
|
||||
label: '拉矫机',
|
||||
children: [
|
||||
{ label: '1#插入量', col: 'TLMESH1' },
|
||||
{ label: '2#插入量', col: 'TLMESH2' },
|
||||
{ label: '3#插入量', col: 'TLMESH3' },
|
||||
{ label: '延伸率', col: 'TLELONG' }
|
||||
]
|
||||
},
|
||||
{
|
||||
label: '酸洗段',
|
||||
children: [
|
||||
{ label: '1#温度', col: 'TK1TEMP' },
|
||||
{ label: '2#温度', col: 'TK2TEMP' },
|
||||
{ label: '3#温度', col: 'TK3TEMP' },
|
||||
{ label: '漂洗温度', col: 'RINSETEMP' }
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
const GAUGE_COLS = [
|
||||
{ col: 'THICK0', title: '入口测厚仪 [mm]' },
|
||||
{ col: 'THICK1', title: '1架出口厚度 [mm]' },
|
||||
{ col: 'THICK4', title: '末架出口厚度 [mm]' },
|
||||
{ col: 'EXIT_SPEED', title: '轧制速度 [m/min]' }
|
||||
]
|
||||
|
||||
const SHAPE_SCALAR_COLS = [
|
||||
{ col: 'ABSDEVIATION', title: '总板形偏差 [IU]' },
|
||||
{ col: 'TILT', title: '末架倾斜量 [mm]' },
|
||||
{ col: 'WRBEND', title: '工作辊弯辊力 [kN]' },
|
||||
{ col: 'IRBEND', title: '中间辊弯辊力 [kN]' }
|
||||
]
|
||||
|
||||
function calcYRange(vals) {
|
||||
const nums = vals.filter(v => v != null && isFinite(Number(v))).map(Number)
|
||||
if (!nums.length) return {}
|
||||
const min = Math.min(...nums)
|
||||
const max = Math.max(...nums)
|
||||
if (min === max) {
|
||||
const base = Math.abs(min) || 1
|
||||
return { min: parseFloat((min - base * 0.2).toFixed(4)), max: parseFloat((max + base * 0.2).toFixed(4)) }
|
||||
}
|
||||
const pad = (max - min) * 0.15
|
||||
return { min: parseFloat((min - pad).toFixed(4)), max: parseFloat((max + pad).toFixed(4)) }
|
||||
}
|
||||
|
||||
function makeLine(title, xData, yData) {
|
||||
const range = calcYRange(yData)
|
||||
return {
|
||||
title: { text: title, textStyle: { fontSize: 12, fontWeight: 'normal' }, top: 4, left: 8 },
|
||||
tooltip: { trigger: 'axis' },
|
||||
grid: { top: 36, bottom: 28, left: 8, right: 16, containLabel: true },
|
||||
xAxis: { type: 'category', data: xData, name: 'pos(m)', nameTextStyle: { fontSize: 10 }, axisLabel: { fontSize: 10 } },
|
||||
yAxis: { type: 'value', min: range.min, max: range.max, nameTextStyle: { fontSize: 10 }, axisLabel: { fontSize: 10 } },
|
||||
dataZoom: [
|
||||
{ type: 'inside', xAxisIndex: 0, zoomOnMouseWheel: true, moveOnMouseMove: true },
|
||||
{ type: 'inside', yAxisIndex: 0, zoomOnMouseWheel: false, moveOnMouseMove: true }
|
||||
],
|
||||
series: [{ name: title, type: 'line', smooth: false, symbol: 'none', lineStyle: { width: 1 }, data: yData }]
|
||||
}
|
||||
}
|
||||
|
||||
function getRowVal(row, col) {
|
||||
const v = row[col] !== undefined ? row[col] : row[col.toLowerCase()]
|
||||
return v == null ? null : Number(v)
|
||||
}
|
||||
|
||||
function xLocData(rows) {
|
||||
return rows.map(r => {
|
||||
const v = r.XLOCATION !== undefined ? r.XLOCATION : r.xlocation
|
||||
return v == null ? '' : Number(v).toFixed(1)
|
||||
})
|
||||
}
|
||||
|
||||
export default {
|
||||
name: 'ProductionCharts',
|
||||
props: {
|
||||
segData: { type: Object, default: null },
|
||||
gaugeRows: { type: Array, default: null },
|
||||
shapeRows: { type: Array, default: null },
|
||||
perfSegCount: { type: Number, default: 0 },
|
||||
segLoading: { type: Boolean, default: false },
|
||||
realtimeLoading: { type: Boolean, default: false }
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
activeTab: 'trend',
|
||||
trendGroups: TREND_GROUPS,
|
||||
expandedGroups: { '张力': true, '速度': true, '拉矫机': true, '酸洗段': true },
|
||||
selectedParam: null,
|
||||
trendChartInst: null,
|
||||
_trendResizeFn: null,
|
||||
chartInstances: [],
|
||||
resizeHandler: null
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
hasPerfData() {
|
||||
return this.segData && this.perfSegCount > 0
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
if (this.hasPerfData) {
|
||||
this.selectParam(TREND_GROUPS[0].children[0])
|
||||
this.renderCurrentTab()
|
||||
} else if (this.gaugeRows?.length || this.shapeRows?.length) {
|
||||
this.$nextTick(() => this.renderCurrentTab())
|
||||
}
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.disposeAll()
|
||||
},
|
||||
watch: {
|
||||
segData(val) {
|
||||
if (val && this.perfSegCount > 0) {
|
||||
this.$nextTick(() => {
|
||||
this.selectParam(this.trendGroups[0].children[0])
|
||||
})
|
||||
}
|
||||
},
|
||||
gaugeRows(val) {
|
||||
if (val && val.length && this.activeTab === 'thickness') {
|
||||
this.$nextTick(() => this.renderCurrentTab())
|
||||
}
|
||||
},
|
||||
shapeRows(val) {
|
||||
if (val && val.length && (this.activeTab === 'flatness3d' || this.activeTab === 'flatness')) {
|
||||
this.$nextTick(() => this.renderCurrentTab())
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
toggleGroup(label) {
|
||||
this.$set(this.expandedGroups, label, !this.expandedGroups[label])
|
||||
},
|
||||
selectParam(item) {
|
||||
this.selectedParam = item
|
||||
this.$nextTick(() => this.renderTrendChart())
|
||||
},
|
||||
handleTabSwitch() {
|
||||
this.$nextTick(() => {
|
||||
if (this.activeTab === 'trend') {
|
||||
if (this.selectedParam && this.segData) this.renderTrendChart()
|
||||
} else {
|
||||
this.renderCurrentTab()
|
||||
}
|
||||
})
|
||||
},
|
||||
renderCurrentTab() {
|
||||
this.disposeSideCharts()
|
||||
if (this.activeTab === 'thickness' && this.gaugeRows?.length) this.renderGauge()
|
||||
if (this.activeTab === 'flatness3d' && this.shapeRows?.length) this.renderFlatness3d()
|
||||
if (this.activeTab === 'flatness' && this.shapeRows?.length) this.renderFlatnessScalar()
|
||||
},
|
||||
renderTrendChart() {
|
||||
if (!this.selectedParam || !this.segData) return
|
||||
const el = this.$refs.trendChart
|
||||
if (!el) return
|
||||
|
||||
if (!this.trendChartInst || this.trendChartInst.isDisposed()) {
|
||||
this.trendChartInst = echarts.init(el)
|
||||
const resizeFn = () => this.trendChartInst && !this.trendChartInst.isDisposed() && this.trendChartInst.resize()
|
||||
window.addEventListener('resize', resizeFn)
|
||||
this._trendResizeFn = resizeFn
|
||||
}
|
||||
const x = this.segX()
|
||||
const yData = this.seg(this.selectedParam.col)
|
||||
this.trendChartInst.setOption(makeLine(this.selectedParam.label, x, yData), true)
|
||||
},
|
||||
seg(col) {
|
||||
const s = this.segData
|
||||
const arr = s[col] !== undefined ? s[col] : (s[col.toLowerCase()] || [])
|
||||
return arr.map(v => v == null ? null : Number(Number(v).toFixed(3)))
|
||||
},
|
||||
segX() {
|
||||
const s = this.segData
|
||||
const arr = s['STARTPOS'] !== undefined ? s['STARTPOS'] : (s['startpos'] || [])
|
||||
return arr.map(v => v == null ? '' : Number(v).toFixed(1))
|
||||
},
|
||||
makeChart(ref, option) {
|
||||
const el = this.$refs[ref]
|
||||
if (!el) return null
|
||||
const chart = echarts.init(el)
|
||||
chart.setOption(option)
|
||||
return chart
|
||||
},
|
||||
setupResize() {
|
||||
this.resizeHandler = () => this.chartInstances.forEach(c => {
|
||||
if (c && !c.isDisposed()) c.resize()
|
||||
})
|
||||
window.addEventListener('resize', this.resizeHandler)
|
||||
},
|
||||
renderGauge() {
|
||||
const rows = this.gaugeRows
|
||||
if (!rows || !rows.length) return
|
||||
const xData = xLocData(rows)
|
||||
const refs = ['chartGauge1', 'chartGauge2', 'chartGauge3', 'chartGauge4']
|
||||
const charts = refs.map((ref, i) => {
|
||||
const { col, title } = GAUGE_COLS[i]
|
||||
const yData = rows.map(r => {
|
||||
const v = getRowVal(r, col)
|
||||
return v == null ? null : parseFloat(v.toFixed(4))
|
||||
})
|
||||
return this.makeChart(ref, makeLine(title, xData, yData))
|
||||
})
|
||||
this.chartInstances = charts.filter(Boolean)
|
||||
this.setupResize()
|
||||
},
|
||||
renderFlatness3d() {
|
||||
const rows = this.shapeRows
|
||||
if (!rows || !rows.length) return
|
||||
const firstRow = rows[0]
|
||||
const high = parseInt(getRowVal(firstRow, 'HIGHZONEID')) || 26
|
||||
const low = parseInt(getRowVal(firstRow, 'LOWZONEID')) || 1
|
||||
const numZones = Math.min(Math.max(high - low + 1, 1), 26)
|
||||
const zoneCols = Array.from({ length: numZones }, (_, i) =>
|
||||
`VALUES${String(low + i).padStart(2, '0')}`
|
||||
)
|
||||
const step = Math.max(1, Math.floor(rows.length / 200))
|
||||
const sampled = rows.filter((_, i) => i % step === 0)
|
||||
const numX = sampled.length
|
||||
|
||||
const xLabels = sampled.map(r => {
|
||||
const v = r.XLOCATION !== undefined ? r.XLOCATION : r.xlocation
|
||||
return v == null ? '' : Number(v).toFixed(0)
|
||||
})
|
||||
|
||||
let minV = Infinity, maxV = -Infinity
|
||||
sampled.forEach(row => {
|
||||
zoneCols.forEach(col => {
|
||||
const v = getRowVal(row, col)
|
||||
if (v != null) {
|
||||
if (v < minV) minV = v
|
||||
if (v > maxV) maxV = v
|
||||
}
|
||||
})
|
||||
})
|
||||
if (!isFinite(minV)) { minV = -30; maxV = 30 }
|
||||
const absMax = Math.max(Math.abs(minV), Math.abs(maxV))
|
||||
|
||||
const channelLines = zoneCols.map((col, yi) => ({
|
||||
type: 'line3D',
|
||||
coordinateSystem: 'cartesian3D',
|
||||
data: sampled.map((row, xi) => {
|
||||
const v = getRowVal(row, col)
|
||||
return v == null ? null : [xi, yi, parseFloat(v.toFixed(2))]
|
||||
}).filter(Boolean),
|
||||
lineStyle: { width: 2, opacity: 1 }
|
||||
}))
|
||||
|
||||
const xStride = Math.max(1, Math.floor(numX / 60))
|
||||
const crossLines = []
|
||||
for (let xi = 0; xi < numX; xi += xStride) {
|
||||
const pts = zoneCols.map((col, yi) => {
|
||||
const v = getRowVal(sampled[xi], col)
|
||||
return v == null ? null : [xi, yi, parseFloat(v.toFixed(2))]
|
||||
}).filter(Boolean)
|
||||
if (pts.length > 1) {
|
||||
crossLines.push({ type: 'line3D', coordinateSystem: 'cartesian3D', data: pts, lineStyle: { width: 1.5, opacity: 1 } })
|
||||
}
|
||||
}
|
||||
|
||||
const series = [...channelLines, ...crossLines]
|
||||
|
||||
const option = {
|
||||
title: { text: '实测平直度 [IU]', textStyle: { fontSize: 13, fontWeight: 'normal' }, top: 6, left: 10 },
|
||||
tooltip: {},
|
||||
visualMap: {
|
||||
show: true, dimension: 2, min: -absMax, max: absMax, calculable: true,
|
||||
orient: 'vertical', right: 10, top: 'center', textStyle: { fontSize: 10 },
|
||||
inRange: {
|
||||
color: ['#8B0000', '#CC2200', '#E84C00', '#F46D43',
|
||||
'#FDAE61', '#FEE08B', '#66BD63', '#1A9850', '#006837',
|
||||
'#3288BD', '#5E4FA2', '#762A83']
|
||||
}
|
||||
},
|
||||
grid3D: {
|
||||
boxWidth: 200, boxHeight: 60, boxDepth: 80,
|
||||
viewControl: { projection: 'orthographic', autoRotate: false, rotateSensitivity: 1, zoomSensitivity: 1 },
|
||||
light: { main: { intensity: 1.2, shadow: false }, ambient: { intensity: 0.3 } }
|
||||
},
|
||||
xAxis3D: {
|
||||
type: 'value', name: '位置', min: 0, max: numX - 1,
|
||||
nameTextStyle: { fontSize: 10 },
|
||||
axisLabel: { fontSize: 9, formatter: v => xLabels[Math.round(v)] || '' }
|
||||
},
|
||||
yAxis3D: {
|
||||
type: 'value', name: '通道', min: 0, max: numZones - 1,
|
||||
nameTextStyle: { fontSize: 10 },
|
||||
axisLabel: { fontSize: 9, formatter: v => String(low + Math.round(v)) }
|
||||
},
|
||||
zAxis3D: { type: 'value', name: 'IU', nameTextStyle: { fontSize: 10 }, axisLabel: { fontSize: 9 } },
|
||||
series
|
||||
}
|
||||
|
||||
const el = this.$refs.chartFlatness3d
|
||||
if (!el) return
|
||||
const chart = echarts.init(el)
|
||||
chart.setOption(option)
|
||||
this.chartInstances = [chart]
|
||||
this.setupResize()
|
||||
},
|
||||
renderFlatnessScalar() {
|
||||
const rows = this.shapeRows
|
||||
if (!rows || !rows.length) return
|
||||
const xData = xLocData(rows)
|
||||
const refs = ['chartFlatDev', 'chartTilt', 'chartWrBend', 'chartIrBend']
|
||||
const charts = refs.map((ref, i) => {
|
||||
const { col, title } = SHAPE_SCALAR_COLS[i]
|
||||
const yData = rows.map(r => {
|
||||
const v = getRowVal(r, col)
|
||||
return v == null ? null : parseFloat(v.toFixed(3))
|
||||
})
|
||||
return this.makeChart(ref, makeLine(title, xData, yData))
|
||||
})
|
||||
this.chartInstances = charts.filter(Boolean)
|
||||
this.setupResize()
|
||||
},
|
||||
disposeSideCharts() {
|
||||
if (this.resizeHandler) {
|
||||
window.removeEventListener('resize', this.resizeHandler)
|
||||
this.resizeHandler = null
|
||||
}
|
||||
this.chartInstances.forEach(c => { if (c && !c.isDisposed()) c.dispose() })
|
||||
this.chartInstances = []
|
||||
},
|
||||
disposeAll() {
|
||||
this.disposeSideCharts()
|
||||
if (this._trendResizeFn) {
|
||||
window.removeEventListener('resize', this._trendResizeFn)
|
||||
this._trendResizeFn = null
|
||||
}
|
||||
if (this.trendChartInst && !this.trendChartInst.isDisposed()) {
|
||||
this.trendChartInst.dispose()
|
||||
this.trendChartInst = null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,47 @@
|
||||
<template>
|
||||
<div class="section sales-section">
|
||||
<div class="section-header">
|
||||
<span class="section-icon">💰</span>
|
||||
<span class="section-title">订单异议</span>
|
||||
</div>
|
||||
<div class="section-body" v-if="list.length > 0">
|
||||
<el-table :data="list" size="small" border stripe style="width: 100%">
|
||||
<el-table-column label="产品类别" align="center" prop="productCategory" />
|
||||
<el-table-column label="反馈日期" align="center" prop="returnDate" width="180">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ parseTime(scope.row.returnDate, '{y}-{m}-{d}') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="投诉情况" align="center" prop="complaintContent" show-overflow-tooltip />
|
||||
<el-table-column label="客户诉求" align="center" prop="customerDemand" show-overflow-tooltip />
|
||||
<el-table-column label="状态" align="center" prop="objectionStatus">
|
||||
<template slot-scope="scope">
|
||||
<el-tag v-if="scope.row.objectionStatus === 0" type="danger">待处理</el-tag>
|
||||
<el-tag v-else-if="scope.row.objectionStatus === 1" type="success">已处理</el-tag>
|
||||
<el-tag v-else-if="scope.row.objectionStatus === 2" type="info">已关闭</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="处理人" align="center" prop="handleUser" />
|
||||
<el-table-column label="处理时间" align="center" prop="handleTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ parseTime(scope.row.handleTime, '{y}-{m}-{d}') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="备注" align="center" prop="remark" />
|
||||
</el-table>
|
||||
</div>
|
||||
<div v-else class="empty-state">
|
||||
<i class="el-icon-document"></i>
|
||||
<span>未找到相关订单异议记录</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'SalesObjectionTable',
|
||||
props: {
|
||||
list: { type: Array, default: () => [] }
|
||||
}
|
||||
}
|
||||
</script>
|
||||
91
klp-ui/src/views/wms/coil/info/components/ShipmentCard.vue
Normal file
91
klp-ui/src/views/wms/coil/info/components/ShipmentCard.vue
Normal file
@@ -0,0 +1,91 @@
|
||||
<template>
|
||||
<div class="step-item">
|
||||
<div class="step-content">
|
||||
<div class="coil-card-wrapper inbound-coil-wrapper">
|
||||
<div class="coil-card-header inbound-header">
|
||||
<span class="card-label">🚚 发货</span>
|
||||
</div>
|
||||
<div class="coil-card-futuristic">
|
||||
<div class="coil-futuristic-body">
|
||||
<div class="coil-icon-container">
|
||||
<svg class="coil-svg" viewBox="0 0 200 200">
|
||||
<defs>
|
||||
<linearGradient id="coilGradShip1" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#f59e0b;stop-opacity:0.5" />
|
||||
<stop offset="50%" style="stop-color:#ef4444;stop-opacity:0.4" />
|
||||
<stop offset="100%" style="stop-color:#f59e0b;stop-opacity:0.5" />
|
||||
</linearGradient>
|
||||
<linearGradient id="coilGradShip2" x1="0%" y1="0%" x2="100%" y2="0%">
|
||||
<stop offset="0%" style="stop-color:#fcd34d;stop-opacity:0.4" />
|
||||
<stop offset="100%" style="stop-color:#fca5a5;stop-opacity:0.3" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<circle cx="100" cy="100" r="70" fill="none" stroke="url(#coilGradShip1)" stroke-width="2.5">
|
||||
<animate attributeName="r" values="70;72;70" dur="2s" repeatCount="indefinite" />
|
||||
</circle>
|
||||
<circle cx="100" cy="100" r="60" fill="none" stroke="url(#coilGradShip2)" stroke-width="1.8" opacity="0.6">
|
||||
<animate attributeName="stroke-dashoffset" values="0;377" dur="4s" repeatCount="indefinite" />
|
||||
</circle>
|
||||
<circle cx="100" cy="100" r="45" fill="url(#coilGradShip1)" opacity="0.15">
|
||||
<animate attributeName="opacity" values="0.15;0.3;0.15" dur="3s" repeatCount="indefinite" />
|
||||
</circle>
|
||||
<circle cx="100" cy="100" r="35" fill="url(#coilGradShip2)" opacity="0.2" />
|
||||
<circle cx="100" cy="100" r="25" fill="url(#coilGradShip1)" opacity="0.25" />
|
||||
<circle cx="100" cy="100" r="15" fill="white" opacity="0.4" />
|
||||
<line x1="100" y1="100" x2="100" y2="30" stroke="url(#coilGradShip1)" stroke-width="1" opacity="0.4" />
|
||||
<line x1="100" y1="100" x2="170" y2="100" stroke="url(#coilGradShip1)" stroke-width="1" opacity="0.4" />
|
||||
<line x1="100" y1="100" x2="100" y2="170" stroke="url(#coilGradShip1)" stroke-width="1" opacity="0.4" />
|
||||
<line x1="100" y1="100" x2="30" y2="100" stroke="url(#coilGradShip1)" stroke-width="1" opacity="0.4" />
|
||||
<circle cx="100" cy="100" r="4" fill="#f59e0b">
|
||||
<animate attributeName="r" values="4;6;4" dur="1.5s" repeatCount="indefinite" />
|
||||
</circle>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="coil-attr-tags">
|
||||
<div class="attr-tag attr-top">
|
||||
<div class="attr-content">
|
||||
<span class="attr-label">发货状态</span>
|
||||
<span class="attr-value" :class="coilInfo.status === 1 ? 'futuristic-status-success' : 'futuristic-status-warning'">
|
||||
{{ coilInfo.status === 1 ? '已发货' : '未发货' }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="coil-futuristic-footer" v-if="coilInfo.status === 1">
|
||||
<div class="footer-item">
|
||||
<span class="footer-label">发货人</span>
|
||||
<span class="footer-value">{{ coilInfo.exportByName || '-' }}</span>
|
||||
</div>
|
||||
<div class="footer-item">
|
||||
<span class="footer-label">发货时间</span>
|
||||
<span class="footer-value">{{ formatTime(coilInfo.exportTime) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="coil-futuristic-footer" v-else>
|
||||
<div class="footer-item" style="flex: 1;">
|
||||
<span class="footer-label">状态</span>
|
||||
<span class="footer-value">等待发货</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { formatTime } from '../statusUtils'
|
||||
|
||||
export default {
|
||||
name: 'ShipmentCard',
|
||||
props: {
|
||||
coilInfo: { type: Object, default: () => ({}) }
|
||||
},
|
||||
methods: {
|
||||
formatTime(time) {
|
||||
return formatTime(time)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,68 @@
|
||||
<template>
|
||||
<div class="section combined-section">
|
||||
<div class="section-header">
|
||||
<span class="section-icon">📋</span>
|
||||
<span class="section-title">移库记录 & 调拨记录</span>
|
||||
</div>
|
||||
<div class="section-body tables-row">
|
||||
<div class="table-wrapper warehouse-table">
|
||||
<div class="table-title">移库记录</div>
|
||||
<div class="table-container">
|
||||
<el-table :data="warehouseList" size="small" border stripe style="width: 100%">
|
||||
<el-table-column prop="createTime" label="操作时间"></el-table-column>
|
||||
<el-table-column prop="operationType" label="操作类型">
|
||||
<template slot-scope="scope">
|
||||
<span v-if="scope.row.operationType === 1">收货</span>
|
||||
<span v-else-if="scope.row.operationType === 2">加工</span>
|
||||
<span v-else-if="scope.row.operationType === 3">调拨</span>
|
||||
<span v-else-if="scope.row.operationType === 4">发货</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="inOutType" label="出入库">
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.inOutType === 1 ? '入库' : '出库' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="warehouse.actualWarehouseName" label="库位">
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.warehouse.actualWarehouseName || '-' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="remark" label="备注" show-overflow-tooltip></el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="table-wrapper transfer-table">
|
||||
<div class="table-title">调拨记录</div>
|
||||
<div class="table-container">
|
||||
<el-table :data="transferList" size="small" border stripe style="width: 100%">
|
||||
<el-table-column prop="type" label="类型" min-width="100"></el-table-column>
|
||||
<el-table-column label="变更前">
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.before }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="变更后">
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.after }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="createTime" label="时间"></el-table-column>
|
||||
<el-table-column prop="remark" label="备注" show-overflow-tooltip></el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'TransferRecords',
|
||||
props: {
|
||||
warehouseList: { type: Array, default: () => [] },
|
||||
transferList: { type: Array, default: () => [] }
|
||||
}
|
||||
}
|
||||
</script>
|
||||
451
klp-ui/src/views/wms/coil/info/index.vue
Normal file
451
klp-ui/src/views/wms/coil/info/index.vue
Normal file
@@ -0,0 +1,451 @@
|
||||
<template>
|
||||
<div class="coil-info-page" v-loading="loading">
|
||||
<div class="content-container">
|
||||
<BasicInfoSection :coilInfo="coilInfo" />
|
||||
|
||||
<CostInfoSection :coilInfo="coilInfo" :traceResult="traceResult"
|
||||
:hoardingDays="hoardingDays" :hoardingCost="hoardingCost" />
|
||||
|
||||
<LifecycleTrace :traceResult="traceResult" :coilInfo="coilInfo" />
|
||||
|
||||
<ContractInfo title="生产合同信息" :info="salesInfo" emptyText="未找到相关生产合同信息" />
|
||||
|
||||
<ContractInfo title="发货合同信息" :info="deliveryOrderInfo" emptyText="未找到相关发货合同信息" />
|
||||
|
||||
<SalesObjectionTable :list="salesObjectionInfo" />
|
||||
|
||||
<TransferRecords :warehouseList="warehouseTranferList" :transferList="tranferList" />
|
||||
|
||||
<div class="section abnormal-section">
|
||||
<div class="section-header">
|
||||
<span class="section-icon">⚠️</span>
|
||||
<span class="section-title">异常信息</span>
|
||||
</div>
|
||||
<div class="section-body">
|
||||
<abnormal-table ref="abnormalTable" :list="abmornalList" :editable="false" :show-coil="false"
|
||||
:coil-info="coilInfo" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section abnormal-section">
|
||||
<div class="section-header">
|
||||
<span class="section-icon">📚</span>
|
||||
<span class="section-title">异常历史</span>
|
||||
</div>
|
||||
<div class="section-body">
|
||||
<abnormal-table ref="historyAbnormalTable" :list="allAbmornalList" :editable="false" :show-coil="true" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<InspectionInfo :taskList="inspectionTaskList" :loading="inspectionLoading" />
|
||||
|
||||
<ProductionCharts v-if="isColdHardCoil"
|
||||
:segData="segData" :gaugeRows="gaugeRows" :shapeRows="shapeRows"
|
||||
:perfSegCount="perfSegCount" :segLoading="segLoading" :realtimeLoading="realtimeLoading" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getMaterialCoil, listMaterialCoil, getMaterialCoilTrace, getDeliveryOrderInfo } from '@/api/wms/coil'
|
||||
import { getCoilWarehouseOperationLogByCoilId } from '@/api/wms/coilWarehouseOperationLog'
|
||||
import { listSalesObjection } from "@/api/crm/salesObjection"
|
||||
import { listCoilAbnormal } from '@/api/wms/coilAbnormal'
|
||||
import { listTransferOrderItem } from '@/api/wms/transferOrderItem'
|
||||
import { listCoilQualityRejudge } from '@/api/wms/coilQualityRejudge'
|
||||
import { getTimingSegByEncoilId, getTimingPlanDetailByHotcoilId, getTimingRealtimeData } from '@/api/l2/timing'
|
||||
import { listInspectionTask } from "@/api/mes/qc/inspectionTask"
|
||||
import AbnormalTable from '@/views/wms/coil/components/AbnormalTable.vue'
|
||||
import { formatTime } from './statusUtils'
|
||||
|
||||
import BasicInfoSection from './components/BasicInfoSection.vue'
|
||||
import CostInfoSection from './components/CostInfoSection.vue'
|
||||
import LifecycleTrace from './components/LifecycleTrace.vue'
|
||||
import ContractInfo from './components/ContractInfo.vue'
|
||||
import SalesObjectionTable from './components/SalesObjectionTable.vue'
|
||||
import TransferRecords from './components/TransferRecords.vue'
|
||||
import InspectionInfo from './components/InspectionInfo.vue'
|
||||
import ProductionCharts from './components/ProductionCharts.vue'
|
||||
|
||||
export default {
|
||||
name: 'CoilInfo',
|
||||
components: {
|
||||
AbnormalTable,
|
||||
BasicInfoSection,
|
||||
CostInfoSection,
|
||||
LifecycleTrace,
|
||||
ContractInfo,
|
||||
SalesObjectionTable,
|
||||
TransferRecords,
|
||||
InspectionInfo,
|
||||
ProductionCharts
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
coilInfoLoading: false,
|
||||
traceLoading: false,
|
||||
traceResult: null,
|
||||
coilDetails: {},
|
||||
loadingCoilDetails: false,
|
||||
coilInfo: {},
|
||||
coilList: [],
|
||||
coilId: '',
|
||||
warehouseTranferList: [],
|
||||
abmornalList: [],
|
||||
allAbmornalList: [],
|
||||
transferOrderItemList: [],
|
||||
coilQualityRejudgeList: [],
|
||||
tranferList: [],
|
||||
perfLoading: false,
|
||||
perfSeries: null,
|
||||
perfSegCount: 0,
|
||||
segLoading: false,
|
||||
realtimeLoading: false,
|
||||
segData: null,
|
||||
gaugeRows: null,
|
||||
shapeRows: null,
|
||||
deliveryOrderInfo: {},
|
||||
salesObjectionInfo: [],
|
||||
inspectionTaskList: [],
|
||||
inspectionLoading: false,
|
||||
standardSteps: [],
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
salesInfo() {
|
||||
return this.coilInfo.orderList?.[0] || {}
|
||||
},
|
||||
isColdHardCoil() {
|
||||
return this.coilInfo.itemName && this.coilInfo.itemName.includes('冷硬卷')
|
||||
},
|
||||
hasPerfData() {
|
||||
return this.perfSeries && this.perfSegCount > 0
|
||||
},
|
||||
hoardingDays() {
|
||||
const inboundTime = this.getInboundTime()
|
||||
if (!inboundTime) return 0
|
||||
const inboundDate = new Date(inboundTime)
|
||||
const endDate = this.coilInfo.status == 1
|
||||
? new Date(this.coilInfo.exportTime)
|
||||
: new Date()
|
||||
const diffTime = endDate.getTime() - inboundDate.getTime()
|
||||
return Math.max(0, Math.floor(diffTime / (1000 * 60 * 60 * 24)))
|
||||
},
|
||||
hoardingCost() {
|
||||
const netWeight = parseFloat(this.coilInfo.netWeight) || 0
|
||||
return (this.hoardingDays * netWeight).toFixed(2)
|
||||
}
|
||||
},
|
||||
async created() {
|
||||
this.coilId = this.$route.params.coilId
|
||||
this.loading = true
|
||||
await this.getCoilInfo()
|
||||
await this.getTraceInfo()
|
||||
await this.getHistoryAbnormalList()
|
||||
await this.getAbnormalList()
|
||||
await this.getWarehouseTransferList()
|
||||
await this.getTransferOrderItemList()
|
||||
await this.getCoilQualityRejudgeList()
|
||||
await this.fetchDeliveryOrderInfo()
|
||||
this.mergeTransferList()
|
||||
await this.getSalesObjectionList()
|
||||
await this.getInspectionTasks()
|
||||
if (this.isColdHardCoil) {
|
||||
await this.loadProductionData()
|
||||
}
|
||||
this.loading = false
|
||||
},
|
||||
methods: {
|
||||
async getCoilInfo() {
|
||||
const res = await getMaterialCoil(this.coilId)
|
||||
this.coilInfo = res.data || {}
|
||||
},
|
||||
async fetchDeliveryOrderInfo() {
|
||||
const res = await getDeliveryOrderInfo(this.coilId)
|
||||
this.deliveryOrderInfo = res.data || {}
|
||||
},
|
||||
async getSalesObjectionList() {
|
||||
const res = await listSalesObjection({ coilIds: this.coilId })
|
||||
this.salesObjectionInfo = res.rows || []
|
||||
},
|
||||
async getTraceInfo() {
|
||||
const res = await getMaterialCoilTrace({
|
||||
coilId: this.coilId,
|
||||
currentCoilNo: this.coilInfo.currentCoilNo
|
||||
})
|
||||
this.traceResult = res.data
|
||||
this.standardSteps = this.traceResult.steps?.map(step => this.formatStep(step)) || []
|
||||
this.fetchCoilDetails()
|
||||
},
|
||||
async getAbnormalList() {
|
||||
const res = await listCoilAbnormal({ coilId: this.coilId })
|
||||
this.abmornalList = res.rows || []
|
||||
},
|
||||
async getHistoryAbnormalList() {
|
||||
const coilIds = this.traceResult.steps?.map(step => step.old_coil_id).filter(Boolean) || []
|
||||
if (coilIds.length === 0) return
|
||||
const res = await listCoilAbnormal({ coilIds: coilIds.join(',') })
|
||||
this.allAbmornalList = res.rows || []
|
||||
},
|
||||
async getWarehouseTransferList() {
|
||||
const res = await getCoilWarehouseOperationLogByCoilId({ coilId: this.coilId })
|
||||
this.warehouseTranferList = res.data || []
|
||||
},
|
||||
async getTransferOrderItemList() {
|
||||
const res = await listTransferOrderItem({ coilId: this.coilId, pageNum: 1, pageSize: 100 })
|
||||
this.transferOrderItemList = res.rows || []
|
||||
},
|
||||
async getCoilQualityRejudgeList() {
|
||||
const res = await listCoilQualityRejudge({ coilId: this.coilId, pageNum: 1, pageSize: 100 })
|
||||
this.coilQualityRejudgeList = res.rows || []
|
||||
},
|
||||
mergeTransferList() {
|
||||
const list = []
|
||||
this.transferOrderItemList.forEach(item => {
|
||||
list.push({
|
||||
type: '批量调拨',
|
||||
before: '逻辑库:' + (item.warehouseNameBefore || '-'),
|
||||
after: '逻辑库:' + (item.warehouseNameAfter || '-'),
|
||||
createTime: item.createTime || '-',
|
||||
remark: item.remark || '-',
|
||||
...item
|
||||
})
|
||||
})
|
||||
this.coilQualityRejudgeList.forEach(item => {
|
||||
list.push({
|
||||
...item,
|
||||
type: '技术部改判',
|
||||
before: '质量状态:' + (item.beforeQuality || '-'),
|
||||
after: '质量状态:' + (item.afterQuality || '-'),
|
||||
createTime: item.createTime || '-',
|
||||
remark: item.rejudgeReason,
|
||||
})
|
||||
})
|
||||
list.sort((a, b) => {
|
||||
const timeA = new Date(a.createTime || 0).getTime()
|
||||
const timeB = new Date(b.createTime || 0).getTime()
|
||||
return timeB - timeA
|
||||
})
|
||||
this.tranferList = list
|
||||
},
|
||||
async getInspectionTasks() {
|
||||
this.inspectionLoading = true
|
||||
try {
|
||||
const res = await listInspectionTask({ enterCoilNos: this.coilInfo.enterCoilNo, pageNum: 1, pageSize: 100 })
|
||||
this.inspectionTaskList = res.rows || []
|
||||
} catch (e) {
|
||||
console.error('获取检验任务异常:', e)
|
||||
this.inspectionTaskList = []
|
||||
} finally {
|
||||
this.inspectionLoading = false
|
||||
}
|
||||
},
|
||||
getInboundTime() {
|
||||
if (!this.traceResult || !this.traceResult.steps) {
|
||||
return this.coilInfo.createTime || null
|
||||
}
|
||||
const createStep = this.traceResult.steps.find(step =>
|
||||
step.action === '新增' || step.action === '创建'
|
||||
)
|
||||
if (createStep) {
|
||||
return createStep.create_time || createStep.update_time || createStep.time
|
||||
}
|
||||
return this.coilInfo.createTime || null
|
||||
},
|
||||
formatTime(timeStamp) {
|
||||
return formatTime(timeStamp)
|
||||
},
|
||||
formatStep(originalStep) {
|
||||
const standardStep = {
|
||||
action: '', time: '-', operation: '-',
|
||||
oldCoilIds: [], newCoilIds: [],
|
||||
changedFields: '', oldCoilInfoList: [], newCoilInfoList: [],
|
||||
deletedCoilInfo: null, restoredCoilInfo: null,
|
||||
original: originalStep
|
||||
}
|
||||
|
||||
if (originalStep.action === '新增') {
|
||||
standardStep.action = '创建'
|
||||
} else if (originalStep.action === '更新') {
|
||||
if (originalStep.operation === '信息更新') standardStep.action = '更新'
|
||||
else if (originalStep.operation === '分卷') standardStep.action = '分卷'
|
||||
else if (originalStep.operation === '合卷') standardStep.action = '合卷'
|
||||
else standardStep.action = '更新'
|
||||
} else if (originalStep.action === '回滚') {
|
||||
standardStep.action = '回滚'
|
||||
} else {
|
||||
standardStep.action = originalStep.action || '-'
|
||||
}
|
||||
|
||||
if (originalStep.update_time || originalStep.create_time) {
|
||||
standardStep.time = this.formatTime(originalStep.update_time || originalStep.create_time)
|
||||
} else if (originalStep.rollback_time) {
|
||||
standardStep.time = this.formatTime(originalStep.rollback_time)
|
||||
}
|
||||
standardStep.operation = originalStep.operator_nickname || originalStep.operator || originalStep.create_by || '-'
|
||||
|
||||
switch (standardStep.action) {
|
||||
case '创建': standardStep.oldCoilIds = []; break
|
||||
case '更新': standardStep.oldCoilIds = originalStep.old_coil_id ? [originalStep.old_coil_id.trim()] : []; break
|
||||
case '退火': standardStep.oldCoilIds = originalStep.old_coil_id ? [originalStep.old_coil_id.trim()] : []; break
|
||||
case '分卷': standardStep.oldCoilIds = originalStep.old_coil_id ? [originalStep.old_coil_id.trim()] : []; break
|
||||
case '合卷':
|
||||
standardStep.oldCoilIds = originalStep.parent_coil_ids
|
||||
? originalStep.parent_coil_ids.split(',').map(id => id.trim()).filter(Boolean) : []
|
||||
break
|
||||
case '回滚':
|
||||
standardStep.oldCoilIds = originalStep.deleted_coil_id ? [originalStep.deleted_coil_id.trim()] : []; break
|
||||
default: standardStep.oldCoilIds = []
|
||||
}
|
||||
|
||||
switch (standardStep.action) {
|
||||
case '创建': standardStep.newCoilIds = originalStep.current_coil_id ? [originalStep.current_coil_id.trim()] : []; break
|
||||
case '更新': standardStep.newCoilIds = originalStep.new_coil_id ? [originalStep.new_coil_id.trim()] : []; break
|
||||
case '退火': standardStep.newCoilIds = originalStep.new_coil_id ? [originalStep.new_coil_id.trim()] : []; break
|
||||
case '分卷':
|
||||
standardStep.newCoilIds = originalStep.child_coil_ids
|
||||
? originalStep.child_coil_ids.split(',').map(id => id.trim()).filter(Boolean) : []
|
||||
break
|
||||
case '合卷': standardStep.newCoilIds = originalStep.new_coil_id ? [originalStep.new_coil_id.trim()] : []; break
|
||||
case '回滚':
|
||||
standardStep.newCoilIds = originalStep.restored_coil_id ? [originalStep.restored_coil_id.trim()] : []; break
|
||||
default: standardStep.newCoilIds = []
|
||||
}
|
||||
|
||||
if (standardStep.action === '更新') {
|
||||
standardStep.changedFields = originalStep.changed_fields || ''
|
||||
}
|
||||
if (standardStep.action === '创建') {
|
||||
listMaterialCoil({ currentCoilNo: originalStep.current_coil_no, enterCoilNo: originalStep.current_coil_no }).then(res => {
|
||||
standardStep.newCoilInfoList = res.rows || []
|
||||
})
|
||||
}
|
||||
standardStep.newCoilInfoList = this.mapCoilInfoList(standardStep.newCoilIds)
|
||||
if (standardStep.action === '回滚') {
|
||||
standardStep.deletedCoilInfo = standardStep.oldCoilInfoList[0] || null
|
||||
standardStep.restoredCoilInfo = standardStep.newCoilInfoList[0] || null
|
||||
}
|
||||
return standardStep
|
||||
},
|
||||
mapCoilInfoList(coilIds) {
|
||||
if (!coilIds || !coilIds.length) return []
|
||||
return coilIds.map(coilId => {
|
||||
const coil = this.coilDetails[coilId] || {}
|
||||
const safeCoil = coil || {}
|
||||
return {
|
||||
...safeCoil,
|
||||
enterCoilNo: safeCoil.enterCoilNo || '-',
|
||||
currentCoilNo: safeCoil.currentCoilNo || '-',
|
||||
materialType: safeCoil.materialType || '-',
|
||||
itemName: safeCoil.itemName || '-',
|
||||
specification: safeCoil.specification || '-',
|
||||
material: safeCoil.material || '-',
|
||||
netWeight: safeCoil.netWeight || 0,
|
||||
warehouseName: safeCoil.warehouseName || '-',
|
||||
actualWarehouseName: safeCoil.actualWarehouseName || '-',
|
||||
manufacturer: safeCoil.manufacturer || '-',
|
||||
zincLayer: safeCoil.zincLayer || '-',
|
||||
qualityStatus: safeCoil.qualityStatus || '-',
|
||||
createTime: safeCoil.createTime || '-',
|
||||
status: safeCoil.status || 0,
|
||||
exportByName: safeCoil.exportByName || '-',
|
||||
exportTime: safeCoil.exportTime || '-',
|
||||
}
|
||||
}).filter(coil => coil)
|
||||
},
|
||||
collectCoilIds() {
|
||||
if (!this.standardSteps.length) return []
|
||||
const coilIds = new Set()
|
||||
this.standardSteps.forEach(step => {
|
||||
step.oldCoilIds.forEach(id => id && coilIds.add(id))
|
||||
step.newCoilIds.forEach(id => id && coilIds.add(id))
|
||||
})
|
||||
return [...coilIds]
|
||||
},
|
||||
async fetchCoilDetails() {
|
||||
const coilIds = this.collectCoilIds().filter(id => id)
|
||||
if (coilIds.length === 0) { this.coilDetails = {}; return }
|
||||
if (this.loadingCoilDetails) return
|
||||
this.loadingCoilDetails = true
|
||||
try {
|
||||
const res = await listMaterialCoil({ coilIds: coilIds.join(',') })
|
||||
if (res && res.code === 200 && res.rows) {
|
||||
const coilIdMap = {}
|
||||
res.rows.forEach(coil => { if (coil.coilId) coilIdMap[coil.coilId] = coil })
|
||||
this.coilDetails = coilIdMap
|
||||
this.standardSteps.forEach(step => {
|
||||
step.oldCoilInfoList = this.mapCoilInfoList(step.oldCoilIds)
|
||||
step.newCoilInfoList = this.mapCoilInfoList(step.newCoilIds)
|
||||
if (step.action === '回滚') {
|
||||
step.deletedCoilInfo = step.oldCoilInfoList[0] || null
|
||||
step.restoredCoilInfo = step.newCoilInfoList[0] || null
|
||||
}
|
||||
})
|
||||
} else {
|
||||
this.$message.warning('获取钢卷详情失败')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取钢卷详情接口异常:', error)
|
||||
this.$message.error('获取钢卷详情异常,请刷新重试')
|
||||
} finally {
|
||||
this.loadingCoilDetails = false
|
||||
}
|
||||
},
|
||||
async loadProductionData() {
|
||||
const hotCoilId = this.coilInfo.enterCoilNo
|
||||
if (!hotCoilId) return
|
||||
this.segLoading = true
|
||||
this.realtimeLoading = true
|
||||
try {
|
||||
const detail = await getTimingPlanDetailByHotcoilId(hotCoilId)
|
||||
const encoilId = detail?.data?.firstRow?.coilid || ''
|
||||
|
||||
const [segRes, realtimeRes] = await Promise.all([
|
||||
encoilId ? getTimingSegByEncoilId(encoilId) : Promise.resolve({ data: null }),
|
||||
encoilId ? getTimingRealtimeData(encoilId) : Promise.resolve({ data: null })
|
||||
])
|
||||
|
||||
const series = segRes?.data?.series || null
|
||||
const rows = segRes?.data?.rows || []
|
||||
this.perfSegCount = rows.length
|
||||
this.perfSeries = series
|
||||
this.segData = series
|
||||
|
||||
const g = realtimeRes?.data?.gauge?.result
|
||||
const s = realtimeRes?.data?.shape?.result
|
||||
this.gaugeRows = Array.isArray(g) ? g : null
|
||||
this.shapeRows = Array.isArray(s) ? s : null
|
||||
} catch (error) {
|
||||
console.error('获取生产数据异常:', error)
|
||||
this.perfSeries = null
|
||||
this.perfSegCount = 0
|
||||
this.segData = null
|
||||
this.gaugeRows = null
|
||||
this.shapeRows = null
|
||||
} finally {
|
||||
this.segLoading = false
|
||||
this.realtimeLoading = false
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@import './coil-info.scss';
|
||||
</style>
|
||||
|
||||
<style scoped>
|
||||
.coil-info-page {
|
||||
background: linear-gradient(180deg, #f0f4f8 0%, #e2e8f0 50%, #f7fafc 100%);
|
||||
min-height: 100vh;
|
||||
padding: 8px 12px;
|
||||
}
|
||||
|
||||
.content-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
</style>
|
||||
20
klp-ui/src/views/wms/coil/info/statusUtils.js
Normal file
20
klp-ui/src/views/wms/coil/info/statusUtils.js
Normal file
@@ -0,0 +1,20 @@
|
||||
export function getFuturisticStatusClass(status) {
|
||||
if (!status) return ''
|
||||
const statusLower = status.toLowerCase()
|
||||
if (statusLower.includes('合格')) return 'futuristic-status-success'
|
||||
if (statusLower.includes('不合格')) return 'futuristic-status-danger'
|
||||
if (statusLower.includes('待检')) return 'futuristic-status-warning'
|
||||
return 'futuristic-status-default'
|
||||
}
|
||||
|
||||
export function formatTime(timeStamp) {
|
||||
if (!timeStamp) return '-'
|
||||
const date = new Date(timeStamp)
|
||||
const year = date.getFullYear()
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0')
|
||||
const day = String(date.getDate()).padStart(2, '0')
|
||||
const hour = String(date.getHours()).padStart(2, '0')
|
||||
const minute = String(date.getMinutes()).padStart(2, '0')
|
||||
const second = String(date.getSeconds()).padStart(2, '0')
|
||||
return `${year}-${month}-${day} ${hour}:${minute}:${second}`
|
||||
}
|
||||
@@ -41,15 +41,24 @@
|
||||
<muti-select v-model="queryParams.qualityStatusCsv" :options="dict.type.coil_quality_status"
|
||||
placeholder="请选择品质" clearable />
|
||||
</el-form-item>
|
||||
<!-- <el-form-item label="导出模式" prop="abnormalExportCount">
|
||||
<el-radio-group v-model="abnormalExportCount">
|
||||
<el-radio-button :label="0">多行导出</el-radio-button>
|
||||
<el-radio-button :label="1">单行拼接</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item> -->
|
||||
<el-form-item>
|
||||
<!-- <el-button type="primary" @click="getList">查询</el-button> -->
|
||||
<el-dropdown split-button type="primary" @click="getList" @command="handleCommand">
|
||||
查询
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item command="exportData">导出产出钢卷</el-dropdown-item>
|
||||
<el-dropdown-item command="exportLossData">导出消耗钢卷</el-dropdown-item>
|
||||
<el-dropdown-item command="saveOutputReport">保存产出报表</el-dropdown-item>
|
||||
<el-dropdown-item command="saveLossReport">保存消耗报表</el-dropdown-item>
|
||||
<el-dropdown-item command="exportData">导出产出钢卷(单行)</el-dropdown-item>
|
||||
<el-dropdown-item command="exportLossData">导出消耗钢卷(单行)</el-dropdown-item>
|
||||
|
||||
<el-dropdown-item command="exportDataMore">导出产出钢卷(多行)</el-dropdown-item>
|
||||
<el-dropdown-item command="exportLossDataMore">导出消耗钢卷(多行)</el-dropdown-item>
|
||||
<!-- <el-dropdown-item command="saveOutputReport">保存产出报表</el-dropdown-item>
|
||||
<el-dropdown-item command="saveLossReport">保存消耗报表</el-dropdown-item> -->
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
<!-- <el-button type="primary" @click="exportData">导出产出钢卷</el-button>
|
||||
@@ -191,6 +200,7 @@ export default {
|
||||
lossColumns: [],
|
||||
outputColumns: [],
|
||||
actionTypes: '',
|
||||
abnormalExportCount: 1,
|
||||
lineOptions: [
|
||||
{ label: '酸轧线', value: '11,120,201,520' },
|
||||
{ label: '镀锌线', value: '202,501,521' },
|
||||
@@ -317,7 +327,8 @@ export default {
|
||||
return
|
||||
}
|
||||
this.download('wms/materialCoil/exportAbnormal', {
|
||||
coilIds: this.outList.map(item => item.coilId).join(',')
|
||||
coilIds: this.outList.map(item => item.coilId).join(','),
|
||||
abnormalExportCount: 1
|
||||
}, `abnormalCoil_${new Date().getTime()}.xlsx`)
|
||||
},
|
||||
|
||||
@@ -328,7 +339,32 @@ export default {
|
||||
return
|
||||
}
|
||||
this.download('wms/materialCoil/export', {
|
||||
coilIds: this.lossList.map(item => item.coilId).join(',')
|
||||
coilIds: this.lossList.map(item => item.coilId).join(','),
|
||||
abnormalExportCount: 1
|
||||
}, `materialCoil_${new Date().getTime()}.xlsx`)
|
||||
},
|
||||
|
||||
// 导出产出钢卷(多行)
|
||||
exportDataMore() {
|
||||
if (this.outList.length === 0) {
|
||||
this.$message.warning('暂无数据可导出')
|
||||
return
|
||||
}
|
||||
this.download('wms/materialCoil/exportAbnormal', {
|
||||
coilIds: this.outList.map(item => item.coilId).join(','),
|
||||
abnormalExportCount: 0
|
||||
}, `abnormalCoil_${new Date().getTime()}.xlsx`)
|
||||
},
|
||||
|
||||
// 导出消耗钢卷(多行)
|
||||
exportLossDataMore() {
|
||||
if (this.lossList.length === 0) {
|
||||
this.$message.warning('暂无数据可导出')
|
||||
return
|
||||
}
|
||||
this.download('wms/materialCoil/export', {
|
||||
coilIds: this.lossList.map(item => item.coilId).join(','),
|
||||
abnormalExportCount: 0
|
||||
}, `materialCoil_${new Date().getTime()}.xlsx`)
|
||||
},
|
||||
// 处理命令
|
||||
@@ -341,6 +377,10 @@ export default {
|
||||
this.saveOutputReport()
|
||||
} else if (command === 'saveLossReport') {
|
||||
this.saveLossReport()
|
||||
} else if (command === 'exportDataMore') {
|
||||
this.exportDataMore()
|
||||
} else if (command === 'exportLossDataMore') {
|
||||
this.exportLossDataMore()
|
||||
}
|
||||
},
|
||||
// 加载列设置
|
||||
|
||||
Reference in New Issue
Block a user