Browse Source

2026-07-17

异常规则配置
master
fengyuan_yang 2 weeks ago
parent
commit
c49f84b250
  1. 10
      src/api/schedule/todoCenter.js
  2. 85
      src/views/main-sidebar-hover.vue
  3. 43
      src/views/main-sidebar-sub-menu.vue
  4. 37
      src/views/main-sidebar.vue
  5. 21
      src/views/modules/sys/sceneExceptionRuleConfig.vue
  6. 850
      src/views/modules/todo/todoCenter.vue

10
src/api/schedule/todoCenter.js

@ -0,0 +1,10 @@
import { createAPI } from '@/utils/httpRequest.js'
// 功能代办中心:查询统计
export const getTodoCenterStats = data => createAPI('schedule/getTodoCenterStats', 'POST', data)
// 功能代办中心:分页查询代办列表
export const searchTodoCenterList = data => createAPI('schedule/searchTodoCenterList', 'POST', data)
// 功能代办中心:查询创建分卷缓存详情
export const getTodoCacheDetail = data => createAPI('schedule/shiftChange/getUnprocessedData', 'POST', data)

85
src/views/main-sidebar-hover.vue

@ -29,7 +29,14 @@
:class="{ 'active': hoveredMenu && hoveredMenu.menuId === menu.menuId }"
@click="handleMainMenuClick(menu, $event)">
<icon-svg :name="menu.icon || ''" class="sidebar-menu-icon"></icon-svg>
<span class="menu-title">{{ menu.name }}</span>
<el-badge
v-if="showTodoBadge(menu)"
class="hover-todo-badge"
:value="todoPendingCount"
:max="99">
<span class="menu-title">{{ menu.name }}</span>
</el-badge>
<span v-else class="menu-title">{{ menu.name }}</span>
<i v-if="menu.list && menu.list.length > 0" class="el-icon-arrow-right menu-arrow"></i>
</div>
@ -41,7 +48,14 @@
:class="{ 'active': hoveredMenu && hoveredMenu.menuId === menu.menuId }"
@click="handleMainMenuClick(menu, $event)">
<icon-svg :name="menu.icon || 'star-on'" class="sidebar-menu-icon"></icon-svg>
<span class="menu-title">{{ menu.name }}</span>
<el-badge
v-if="showTodoBadge(menu)"
class="hover-todo-badge"
:value="todoPendingCount"
:max="99">
<span class="menu-title">{{ menu.name }}</span>
</el-badge>
<span v-else class="menu-title">{{ menu.name }}</span>
<i v-if="menu.list && menu.list.length > 0" class="el-icon-arrow-right menu-arrow"></i>
</div>
</div>
@ -63,7 +77,14 @@
:class="{ 'active': hoveredMenu && hoveredMenu.menuId === menu.menuId }"
@click="handleMainMenuClick(menu, $event)"
:title="menu.name">
<icon-svg :name="menu.icon || ''" class="sidebar-menu-icon"></icon-svg>
<el-badge
v-if="showTodoBadge(menu)"
class="hover-todo-badge-icon"
:value="todoPendingCount"
:max="99">
<icon-svg :name="menu.icon || ''" class="sidebar-menu-icon"></icon-svg>
</el-badge>
<icon-svg v-else :name="menu.icon || ''" class="sidebar-menu-icon"></icon-svg>
</div>
<!-- 收藏夹图标 -->
@ -74,7 +95,14 @@
:class="{ 'active': hoveredMenu && hoveredMenu.menuId === menu.menuId }"
@click="handleMainMenuClick(menu, $event)"
:title="menu.name">
<icon-svg :name="menu.icon || 'star-on'" class="sidebar-menu-icon"></icon-svg>
<el-badge
v-if="showTodoBadge(menu)"
class="hover-todo-badge-icon"
:value="todoPendingCount"
:max="99">
<icon-svg :name="menu.icon || 'star-on'" class="sidebar-menu-icon"></icon-svg>
</el-badge>
<icon-svg v-else :name="menu.icon || 'star-on'" class="sidebar-menu-icon"></icon-svg>
</div>
</div>
</template>
@ -141,6 +169,7 @@
import { isURL } from '@/utils/validate'
import { userFavoriteList } from '@/api/userFavorite.js'
import { searchFunctionButtonList } from '@/api/sysLanguage.js'
import { getTodoCenterStats } from '@/api/schedule/todoCenter.js'
export default {
name: 'MainSidebarHover',
@ -149,6 +178,8 @@ export default {
dynamicMenuRoutes: [],
search: '',
favoriteList: [],
todoPendingCount: 0,
todoBadgeTimer: null,
hoveredMenu: null,
submenuStyle: {},
hideTimer: null,
@ -211,17 +242,49 @@ export default {
this.userFavorites()
this.dynamicMenuRoutes = JSON.parse(sessionStorage.getItem('dynamicMenuRoutes') || '[]')
this.routeHandle(this.$route)
this.refreshTodoPendingCount()
// this.getFunctionButtonList()
},
mounted() {
//
document.addEventListener('click', this.handleDocumentClick)
this.todoBadgeTimer = setInterval(this.refreshTodoPendingCount, 60000)
},
beforeDestroy() {
//
document.removeEventListener('click', this.handleDocumentClick)
if (this.todoBadgeTimer) {
clearInterval(this.todoBadgeTimer)
this.todoBadgeTimer = null
}
},
methods: {
refreshTodoPendingCount () {
const site = this.$store.state.user.site
if (!site) {
this.todoPendingCount = 0
return
}
const query = {
site: site,
buNo: ''
}
getTodoCenterStats(query).then(({ data }) => {
if (data && data.code === 200 && data.resultMap) {
this.todoPendingCount = Number(data.resultMap.lockPendingCount || 0)
}
}).catch(() => {
this.todoPendingCount = 0
})
},
showTodoBadge (menu) {
if (!menu || this.todoPendingCount <= 0) {
return false
}
const menuName = String(menu.name || '')
const menuUrl = String(menu.url || '').toLowerCase()
return menuName.indexOf('代办中心') !== -1 || menuUrl.indexOf('todocenter') !== -1 || menuUrl.indexOf('todo-center') !== -1
},
//
getFunctionButtonList() {
let queryButton = {
@ -662,6 +725,20 @@ export default {
}
}
.hover-todo-badge {
width: calc(100% - 36px);
}
.hover-todo-badge /deep/ .el-badge__content.is-fixed {
top: 11px;
right: -14px;
}
.hover-todo-badge-icon /deep/ .el-badge__content.is-fixed {
top: 7px;
right: 0;
}
.hover-submenu {
position: fixed;
z-index: 1030;

43
src/views/main-sidebar-sub-menu.vue

@ -5,18 +5,33 @@
:popper-class="'site-sidebar--' + sidebarLayoutSkin + '-popper'">
<template slot="title">
<icon-svg :name="menu.icon || ''" class="site-sidebar__menu-icon"></icon-svg>
<span>{{ menu.name }}</span>
<el-badge
v-if="showTodoBadge(menu)"
class="todo-menu-badge"
:value="todoPendingCount"
:max="99">
<span>{{ menu.name }}</span>
</el-badge>
<span v-else>{{ menu.name }}</span>
</template>
<sub-menu
v-for="item in menu.list"
:key="item.menuId"
:menu="item"
:dynamicMenuRoutes="dynamicMenuRoutes">
:dynamicMenuRoutes="dynamicMenuRoutes"
:todoPendingCount="todoPendingCount">
</sub-menu>
</el-submenu>
<el-menu-item v-else :index="menu.menuId + ''" @click="gotoRouteHandle(menu)">
<icon-svg :name="menu.icon || ''" class="site-sidebar__menu-icon"></icon-svg>
<span>{{ menu.name }}</span>
<el-badge
v-if="showTodoBadge(menu)"
class="todo-menu-badge"
:value="todoPendingCount"
:max="99">
<span>{{ menu.name }}</span>
</el-badge>
<span v-else>{{ menu.name }}</span>
</el-menu-item>
</template>
@ -32,6 +47,10 @@
dynamicMenuRoutes: {
type: Array,
required: true
},
todoPendingCount: {
type: Number,
default: 0
}
},
components: {
@ -43,6 +62,14 @@
}
},
methods: {
showTodoBadge (menu) {
if (!menu || this.todoPendingCount <= 0) {
return false
}
const menuName = String(menu.name || '')
const menuUrl = String(menu.url || '').toLowerCase()
return menuName.indexOf('代办中心') !== -1 || menuUrl.indexOf('todocenter') !== -1 || menuUrl.indexOf('todo-center') !== -1
},
// menuId()
gotoRouteHandle (menu) {
var route = this.dynamicMenuRoutes.filter(item => item.meta.menuId === menu.menuId)
@ -53,3 +80,13 @@
}
}
</script>
<style scoped>
.todo-menu-badge {
vertical-align: middle;
}
.todo-menu-badge /deep/ .el-badge__content {
top: 10px;
}
</style>

37
src/views/main-sidebar.vue

@ -21,14 +21,16 @@
v-for="menu in menuList"
:key="menu.menuId"
:menu="menu"
:dynamicMenuRoutes="dynamicMenuRoutes">
:dynamicMenuRoutes="dynamicMenuRoutes"
:todoPendingCount="todoPendingCount">
</sub-menu>
<sub-menu
v-for="menu in this.favoriteList"
:key="menu.menuId"
:menu="menu"
:dynamicMenuRoutes="dynamicMenuRoutes">
:dynamicMenuRoutes="dynamicMenuRoutes"
:todoPendingCount="todoPendingCount">
</sub-menu>
</el-menu>
@ -44,6 +46,7 @@ import {treeDataTranslate} from '@/utils'
import {
searchFunctionButtonList,
} from "@/api/sysLanguage.js"
import { getTodoCenterStats } from '@/api/schedule/todoCenter.js'
export default {
data() {
@ -55,6 +58,8 @@ export default {
favoriteList: [],
newMenuList: [],
list: [],
todoPendingCount: 0,
todoBadgeTimer: null,
pageLanguage: {
homePage: '首页'
}
@ -119,8 +124,36 @@ export default {
this.dynamicMenuRoutes = JSON.parse(sessionStorage.getItem('dynamicMenuRoutes') || '[]')
this.routeHandle(this.$route)
this.getFunctionButtonList()
this.refreshTodoPendingCount()
},
mounted () {
this.todoBadgeTimer = setInterval(this.refreshTodoPendingCount, 60000)
},
beforeDestroy () {
if (this.todoBadgeTimer) {
clearInterval(this.todoBadgeTimer)
this.todoBadgeTimer = null
}
},
methods: {
refreshTodoPendingCount () {
const site = this.$store.state.user.site
if (!site) {
this.todoPendingCount = 0
return
}
const query = {
site: site,
buNo: ''
}
getTodoCenterStats(query).then(({data}) => {
if (data && data.code === 200 && data.resultMap) {
this.todoPendingCount = Number(data.resultMap.lockPendingCount || 0)
}
}).catch(() => {
this.todoPendingCount = 0
})
},
//
//
getFunctionButtonList() {

21
src/views/modules/sys/sceneExceptionRuleConfig.vue

@ -156,7 +156,11 @@
<el-table-column prop="forceType" label="是否强制" header-align="center" align="center" min-width="100"></el-table-column>
<el-table-column prop="warningLevel" label="预警等级" header-align="center" align="center" min-width="100"></el-table-column>
<el-table-column prop="animationEffect" label="动画效果" header-align="center" align="center" min-width="100"></el-table-column>
<el-table-column prop="responsibilityDept" label="责任部门" header-align="center" align="center" min-width="120"></el-table-column>
<el-table-column label="责任部门" header-align="center" align="center" min-width="120">
<template slot-scope="scope">
{{ getResponsibilityDeptLabel(scope.row) }}
</template>
</el-table-column>
<el-table-column label="触发异常事件" header-align="center" align="center" min-width="110">
<template slot-scope="scope">
{{ scope.row.parentConditionNo ? scope.row.triggerEvent : '' }}
@ -234,7 +238,7 @@
v-for="item in roleList"
:key="item.roleId"
:label="item.roleName"
:value="item.roleName">
:value="String(item.roleId)">
</el-option>
</el-select>
</el-form-item>
@ -376,6 +380,19 @@ export default {
}
})
},
getResponsibilityDeptLabel (row) {
if (!row) {
return ''
}
if (row.responsibilityDeptName) {
return row.responsibilityDeptName
}
if (!row.responsibilityDept) {
return ''
}
const matched = (this.roleList || []).find(item => String(item.roleId) === String(row.responsibilityDept))
return matched ? matched.roleName : row.responsibilityDept
},
queryRule () {
this.pageIndex = 1
this.getDataList()

850
src/views/modules/todo/todoCenter.vue

@ -0,0 +1,850 @@
<template>
<div class="mod-todo-center" v-loading="dataListLoading">
<div class="todo-top-fixed">
<div class="todo-filter-panel">
<el-form :inline="true" :model="searchData" @keyup.enter.native="queryHandle">
<el-form-item label="BU">
<el-select v-model="searchData.buNo" clearable filterable placeholder="全部" style="width: 180px">
<el-option
v-for="item in buList"
:key="item.buNo"
:label="(item.sitename || item.site || '') + '-' + (item.buDesc || item.buNo || '')"
:value="item.buNo">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="是否强制">
<el-select v-model="searchData.forceType" clearable placeholder="全部" style="width: 140px">
<el-option
v-for="item in forceTypeOptions"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="代办状态">
<el-select v-model="searchData.todoStatus" clearable placeholder="全部" style="width: 140px">
<el-option
v-for="item in todoStatusOptions"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="责任部门">
<el-select v-model="searchData.responsibilityDept" clearable filterable placeholder="全部" style="width: 160px">
<el-option
v-for="item in roleList"
:key="item.roleId"
:label="item.roleName"
:value="String(item.roleId)">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="关键字">
<el-input
v-model="searchData.keyword"
clearable
placeholder="信息编码/派工单号/订单号"
style="width: 260px">
</el-input>
</el-form-item>
<el-form-item label=" ">
<el-button type="primary" @click="queryHandle">查询</el-button>
<el-button @click="resetHandle">重置</el-button>
</el-form-item>
</el-form>
</div>
<div class="todo-summary-row">
<div class="summary-card blue">
<div class="summary-icon"><i class="el-icon-document"></i></div>
<div class="summary-content">
<div class="summary-value">{{ stats.pendingAuditCount }}</div>
<div class="summary-label">待审核</div>
</div>
</div>
<div class="summary-card green">
<div class="summary-icon"><i class="el-icon-success"></i></div>
<div class="summary-content">
<div class="summary-value">{{ stats.pendingOperateCount }}</div>
<div class="summary-label">可报告</div>
</div>
</div>
<div class="summary-card orange">
<div class="summary-icon"><i class="el-icon-time"></i></div>
<div class="summary-content">
<div class="summary-value">{{ stats.completedCount }}</div>
<div class="summary-label">已完成</div>
</div>
</div>
</div>
</div>
<div class="todo-main-scroll">
<div class="todo-card-grid" v-if="dataList.length > 0">
<div class="todo-card" v-for="item in dataList" :key="item.infoCode">
<div class="todo-card-head">
<div class="risk-tag" :class="{ high: item.lockFlag === 'Y' }">
{{ item.lockFlag === 'Y' ? '强制' : '提示' }}
</div>
<div class="todo-code">{{ item.infoCode || '-' }}</div>
<el-tag size="mini" :type="statusType(item)">
{{ statusLabel(item) }}
</el-tag>
</div>
<div class="todo-source">{{ item.messageSource || '-' }}</div>
<div class="todo-main-title">
{{ item.orderNo || '-' }}
</div>
<div class="todo-detail-line">
<span class="label">工序</span>
<span class="value">工序 1创建分卷</span>
</div>
<div class="todo-detail-line">
<span class="label">派工单号</span>
<span class="value">{{ item.seqNo || '-' }}</span>
</div>
<div class="todo-detail-line">
<span class="label">责任部门</span>
<span class="value">{{ item.responsibilityDeptName || item.responsibilityDept || '-' }}</span>
</div>
<div class="todo-step-row">
<div class="step-item done">生成</div>
<div class="step-item" :class="{ done: item.auditFlag === 'Y' }">审核</div>
<div class="step-item" :class="{ done: item.operateFlag === 'Y' }">完成</div>
</div>
<div class="todo-footer">
<span class="todo-time">{{ formatDateTime(item.createdTime) }}</span>
<el-button
size="mini"
type="primary"
plain
@click="showDetail(item)">
详情
</el-button>
</div>
</div>
</div>
<div v-else class="todo-empty">
暂无代办数据
</div>
</div>
<div class="todo-bottom-fixed">
<el-pagination
class="todo-pagination"
@size-change="sizeChangeHandle"
@current-change="currentChangeHandle"
:current-page="pageIndex"
:page-sizes="[15, 30, 60, 100]"
:page-size="pageSize"
:total="totalPage"
layout="total, sizes, prev, pager, next, jumper">
</el-pagination>
</div>
<el-dialog
:title="detailDialogTitle"
:close-on-click-modal="false"
:visible.sync="detailDialogVisible"
width="820px"
class="customer-dialog todo-cache-detail-dialog"
append-to-body>
<div v-loading="detailDialogLoading">
<el-form :inline="true" label-position="top" style="height: auto;">
<el-row style="margin-top: -10px;">
<el-col :span="11">
<el-form-item label="时间">
<el-input :value="detailData.cacheTime" readonly style="width: 220px;"></el-input>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="固定载具">
<el-input :value="detailData.fixture" readonly style="width: 120px;"></el-input>
</el-form-item>
</el-col>
<el-col :span="3">
<el-form-item label="排数">
<el-input-number :controls="false" :step="0" :value="detailData.rowCount" disabled style="width: 60px;"></el-input-number>
</el-form-item>
</el-col>
<el-col :span="3">
<el-form-item label="分切卷数">
<el-input-number :controls="false" :step="0" :value="detailData.rollCount" disabled style="width: 60px;"></el-input-number>
</el-form-item>
</el-col>
</el-row>
<div class="table-content-container">
<el-table
:data="detailRowList"
border
style="width: 100%; margin-top: 10px;"
max-height="360">
<el-table-column prop="rowNumber" label="NO." width="50" align="center"></el-table-column>
<el-table-column label="良品数" width="80" align="center">
<template slot-scope="scope">
<el-input-number
:controls="false"
:step="0"
:value="scope.row.goodQty"
disabled
style="width: 100%;"
class="readonly-qty-input good-qty-input">
</el-input-number>
</template>
</el-table-column>
<el-table-column label="面损" width="80" align="center">
<template slot-scope="scope">
<el-input-number
:controls="false"
:step="0"
:value="scope.row.surfaceLossQty"
disabled
style="width: 100%;"
class="readonly-qty-input">
</el-input-number>
</template>
</el-table-column>
<el-table-column label="性能不良" width="80" align="center">
<template slot-scope="scope">
<el-input-number
:controls="false"
:step="0"
:value="scope.row.poorPerformanceQty"
disabled
style="width: 100%;"
class="readonly-qty-input">
</el-input-number>
</template>
</el-table-column>
<el-table-column label="不良数" width="80" align="right">
<template slot-scope="scope">
<div class="readonly-number defect" style="text-align: right; padding-right: 10px;">
{{ scope.row.defectQty }}
</div>
</template>
</el-table-column>
<el-table-column label="良率" width="80" align="center">
<template slot-scope="scope">
<div class="readonly-number yield">{{ scope.row.yieldRate }}</div>
</template>
</el-table-column>
<el-table-column label="总数" width="80" align="right">
<template slot-scope="scope">
<div class="readonly-number" style="text-align: right; padding-right: 10px;">
{{ scope.row.totalQty }}
</div>
</template>
</el-table-column>
<el-table-column label="Packing List" align="center" class-name="packing-list-column">
<template slot-scope="scope">
<div class="remark-wrapper">
<el-input
type="textarea"
:value="scope.row.packingList"
resize="none"
:autosize="false"
readonly
class="remark-textarea">
</el-input>
</div>
</template>
</el-table-column>
<el-table-column label="备注" align="center" class-name="remark-column">
<template slot-scope="scope">
<div class="remark-wrapper">
<el-input
type="textarea"
:value="scope.row.remark"
resize="none"
:autosize="false"
readonly
class="remark-textarea">
</el-input>
</div>
</template>
</el-table-column>
</el-table>
</div>
</el-form>
</div>
<span slot="footer" class="dialog-footer">
<el-button type="primary" @click="detailDialogVisible = false">关闭</el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import { getSiteAndBuByUserName, getUserRoleList } from '@/api/qc/qc'
import { getTodoCenterStats, searchTodoCenterList, getTodoCacheDetail } from '@/api/schedule/todoCenter'
export default {
data () {
return {
dataListLoading: false,
pageIndex: 1,
pageSize: 15,
totalPage: 0,
buList: [],
roleList: [],
searchData: {
site: this.$store.state.user.site,
buNo: '',
forceType: 'force',
todoStatus: 'pendingAudit',
responsibilityDept: '',
keyword: ''
},
forceTypeOptions: [
{ label: '强制', value: 'force' },
{ label: '提示', value: 'hint' }
],
todoStatusOptions: [
{ label: '待审核', value: 'pendingAudit' },
{ label: '可报告', value: 'reportable' },
{ label: '已完成', value: 'completed' }
],
stats: {
totalCount: 0,
pendingAuditCount: 0,
pendingOperateCount: 0,
pendingTotalCount: 0,
completedCount: 0,
lockPendingCount: 0
},
dataList: [],
detailDialogVisible: false,
detailDialogLoading: false,
detailDialogTitle: '创建分卷详情',
detailData: {
cacheTime: '',
fixture: '',
rowCount: 0,
rollCount: 0
},
detailRowList: []
}
},
created () {
this.loadBuList()
this.loadRoleList()
this.getTodoStats()
this.getDataList()
},
methods: {
loadBuList () {
const params = { username: this.$store.state.user.name }
getSiteAndBuByUserName(params).then(({ data }) => {
if (data && data.code === 0) {
this.buList = data.rows || []
} else {
this.buList = []
}
}).catch(() => {
this.buList = []
})
},
loadRoleList () {
getUserRoleList({}).then(({ data }) => {
if (data && data.code === 0) {
this.roleList = data.rows || []
} else {
this.roleList = []
}
}).catch(() => {
this.roleList = []
})
},
queryHandle () {
this.pageIndex = 1
this.getTodoStats()
this.getDataList()
},
resetHandle () {
this.searchData.buNo = ''
this.searchData.forceType = 'force'
this.searchData.todoStatus = 'pendingAudit'
this.searchData.responsibilityDept = ''
this.searchData.keyword = ''
this.queryHandle()
},
getTodoStats () {
const query = this.buildQuery()
getTodoCenterStats(query).then(({ data }) => {
if (data && data.code === 200 && data.resultMap) {
this.stats = Object.assign({}, this.stats, data.resultMap)
}
})
},
getDataList () {
this.dataListLoading = true
const query = this.buildQuery()
query.page = this.pageIndex
query.limit = this.pageSize
searchTodoCenterList(query).then(({ data }) => {
if (data && data.code === 200 && data.page) {
this.dataList = data.page.list || []
this.totalPage = data.page.totalCount || 0
} else {
this.dataList = []
this.totalPage = 0
}
}).finally(() => {
this.dataListLoading = false
})
},
buildQuery () {
const currentSite = this.searchData.site || this.$store.state.user.site || ''
return {
site: currentSite,
buNo: this.searchData.buNo,
forceType: this.searchData.forceType,
todoStatus: this.searchData.todoStatus,
responsibilityDept: this.searchData.responsibilityDept,
keyword: this.searchData.keyword
}
},
sizeChangeHandle (val) {
this.pageSize = val
this.pageIndex = 1
this.getDataList()
},
currentChangeHandle (val) {
this.pageIndex = val
this.getDataList()
},
statusLabel (row) {
if (!row) {
return ''
}
if (row.auditFlag === 'Y' && row.operateFlag === 'Y') {
return '已完成'
}
if (row.auditFlag === 'Y' && row.operateFlag === 'N') {
return '可报告'
}
if (row.auditFlag === 'N' && row.operateFlag === 'N') {
return '待审核'
}
return '处理中'
},
statusType (row) {
if (!row) {
return 'info'
}
if (row.auditFlag === 'Y' && row.operateFlag === 'Y') {
return 'success'
}
if (row.auditFlag === 'Y' && row.operateFlag === 'N') {
return 'warning'
}
if (row.auditFlag === 'N' && row.operateFlag === 'N') {
return 'danger'
}
return 'info'
},
formatDateTime (val) {
if (!val) {
return '-'
}
return String(val).replace('T', ' ').substring(0, 19)
},
resetDetailData () {
this.detailData = {
cacheTime: '',
fixture: '',
rowCount: 0,
rollCount: 0
}
this.detailRowList = []
},
toInt (value) {
const num = Number(value)
if (Number.isNaN(num)) {
return null
}
return parseInt(num, 10)
},
toNum (value) {
const num = Number(value)
if (Number.isNaN(num)) {
return 0
}
return num
},
calcYieldRate (goodQty, defectQty) {
const totalQty = goodQty + defectQty
if (totalQty <= 0) {
return '0.00%'
}
return ((goodQty / totalQty) * 100).toFixed(2) + '%'
},
mapCacheDetailRows (cacheList) {
return (cacheList || []).map(item => {
const goodQty = this.toNum(item.goodQty)
const defectQty = this.toNum(item.defectQty)
const totalQty = goodQty + defectQty
return {
rowNumber: this.toInt(item.rowBumber) || 0,
goodQty: goodQty,
surfaceLossQty: this.toNum(item.surfaceLossQty),
poorPerformanceQty: this.toNum(item.poorPerformanceQty),
defectQty: defectQty,
yieldRate: this.calcYieldRate(goodQty, defectQty),
totalQty: totalQty,
packingList: item.packingList || '',
remark: item.remark || ''
}
})
},
showDetail (row) {
if (!row) {
return
}
this.detailDialogTitle = `创建分卷缓存详情 - ${row.infoCode || ''}`
this.detailDialogVisible = true
this.detailDialogLoading = true
this.resetDetailData()
const seqNo = this.toInt(row.seqNo)
if (seqNo === null) {
this.detailDialogLoading = false
this.$message.error('派工单号格式不正确,无法查询缓存数据')
return
}
const query = {
site: row.site || this.searchData.site || this.$store.state.user.site,
orderNo: row.orderNo,
seqNo: seqNo
}
getTodoCacheDetail(query).then(({ data }) => {
if (!(data && data.code === 0)) {
this.$message.error((data && data.msg) || '缓存数据查询失败')
return
}
const cacheList = data.data || []
if (cacheList.length === 0) {
this.$message.warning('当前代办暂无缓存数据')
return
}
const first = cacheList[0]
this.detailData.cacheTime = this.formatDateTime(first.createdTime)
this.detailData.fixture = first.fixtureNo || ''
this.detailData.rowCount = this.toInt(first.rowCount) || cacheList.length
this.detailData.rollCount = this.toInt(first.rollCount) || 0
this.detailRowList = this.mapCacheDetailRows(cacheList)
}).catch(() => {
this.$message.error('缓存数据查询失败')
}).finally(() => {
this.detailDialogLoading = false
})
}
}
}
</script>
<style scoped>
.mod-todo-center {
height: calc(100vh - 120px);
min-height: calc(100vh - 120px);
background: #f3f5f9;
padding: 10px;
box-sizing: border-box;
display: flex;
flex-direction: column;
overflow: hidden;
}
.todo-top-fixed {
flex-shrink: 0;
background: #f3f5f9;
z-index: 2;
}
.todo-main-scroll {
flex: 1;
min-height: 0;
overflow-y: auto;
overflow-x: hidden;
padding-right: 2px;
}
.todo-bottom-fixed {
flex-shrink: 0;
background: #f3f5f9;
padding-top: 10px;
}
.todo-filter-panel {
background: #fff;
border: 1px solid #ebeef5;
border-radius: 6px;
padding: 12px 14px 2px;
margin-bottom: 10px;
}
.panel-title {
font-size: 14px;
color: #606266;
margin-bottom: 8px;
font-weight: 600;
}
.todo-summary-row {
display: flex;
gap: 10px;
margin-bottom: 10px;
}
.summary-card {
flex: 1;
min-width: 0;
background: #fff;
border: 1px solid #ebeef5;
border-radius: 6px;
padding: 10px 12px;
display: flex;
align-items: center;
}
.summary-icon {
width: 36px;
height: 36px;
border-radius: 8px;
text-align: center;
line-height: 36px;
font-size: 18px;
margin-right: 10px;
color: #fff;
}
.summary-card.blue .summary-icon {
background: #3a8ee6;
}
.summary-card.green .summary-icon {
background: #67c23a;
}
.summary-card.orange .summary-icon {
background: #e6a23c;
}
.summary-value {
font-size: 24px;
line-height: 24px;
font-weight: 700;
color: #303133;
}
.summary-label {
margin-top: 4px;
font-size: 12px;
color: #909399;
}
.todo-card-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 10px;
}
.todo-card {
background: #fff;
border: 1px solid #ebeef5;
border-radius: 6px;
padding: 10px 12px;
min-height: 248px;
display: flex;
flex-direction: column;
}
.todo-card-head {
display: flex;
align-items: center;
gap: 6px;
}
.risk-tag {
font-size: 12px;
color: #909399;
background: #f4f4f5;
border-radius: 10px;
padding: 2px 8px;
}
.risk-tag.high {
color: #fff;
background: #f56c6c;
}
.todo-code {
font-size: 13px;
color: #303133;
font-weight: 600;
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.todo-source {
margin-top: 8px;
font-size: 12px;
color: #909399;
}
.todo-main-title {
margin-top: 8px;
font-size: 16px;
color: #303133;
font-weight: 700;
}
.todo-detail-line {
margin-top: 8px;
display: flex;
justify-content: space-between;
font-size: 12px;
}
.todo-detail-line .label {
color: #909399;
}
.todo-detail-line .value {
color: #303133;
max-width: 60%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
text-align: right;
}
.todo-step-row {
margin-top: auto;
display: flex;
justify-content: space-between;
gap: 8px;
padding-top: 14px;
}
.step-item {
flex: 1;
text-align: center;
font-size: 12px;
color: #c0c4cc;
border: 1px solid #ebeef5;
border-radius: 14px;
padding: 4px 0;
}
.step-item.done {
color: #67c23a;
border-color: #b3e19d;
background: #f0f9eb;
}
.todo-footer {
margin-top: 10px;
display: flex;
justify-content: space-between;
align-items: center;
}
.todo-time {
font-size: 12px;
color: #909399;
}
.todo-empty {
margin-top: 20px;
text-align: center;
color: #909399;
min-height: 320px;
display: flex;
align-items: center;
justify-content: center;
}
.todo-pagination {
margin-top: 0;
text-align: right;
}
.todo-cache-detail-dialog /deep/ .el-dialog__body {
padding-top: 10px;
}
.todo-cache-detail-dialog .table-content-container {
width: 100%;
}
.todo-cache-detail-dialog .readonly-number {
width: 100%;
text-align: center;
color: #606266;
}
.todo-cache-detail-dialog /deep/ .readonly-qty-input .el-input__inner {
text-align: center;
color: #606266;
padding: 0 6px;
}
.todo-cache-detail-dialog /deep/ .readonly-qty-input.good-qty-input .el-input__inner {
color: #67c23a;
font-weight: bold;
}
.todo-cache-detail-dialog .readonly-number.good {
color: #67c23a;
font-weight: bold;
}
.todo-cache-detail-dialog .readonly-number.defect {
color: #f56c6c;
font-weight: bold;
}
.todo-cache-detail-dialog .readonly-number.yield {
color: #409eff;
font-weight: bold;
}
.todo-cache-detail-dialog /deep/ .el-table td.remark-column,
.todo-cache-detail-dialog /deep/ .el-table td.packing-list-column {
padding: 0 !important;
position: relative !important;
}
.todo-cache-detail-dialog .remark-wrapper {
width: 100%;
height: 100%;
}
.todo-cache-detail-dialog .remark-textarea {
width: 100%;
height: 100%;
}
.todo-cache-detail-dialog /deep/ .remark-textarea .el-textarea__inner {
border: none !important;
resize: none !important;
border-radius: 0 !important;
min-height: 68px !important;
background: #fafafa !important;
}
</style>
Loading…
Cancel
Save