15 changed files with 5481 additions and 23 deletions
-
83src/App.vue
-
103src/api/erf/erf.js
-
104src/assets/scss/approval-notification.scss
-
1src/assets/scss/index.scss
-
564src/components/ApprovalNotificationManager.vue
-
192src/config/approval-notification.config.js
-
154src/views/modules/erf/components/approvalHistory.vue
-
537src/views/modules/erf/components/erfAttachmentManager.vue
-
354src/views/modules/erf/components/expApplyForm.vue
-
344src/views/modules/erf/components/expProjectDetail.vue
-
569src/views/modules/erf/components/expTriConfirm.vue
-
490src/views/modules/erf/expApplyApproval.vue
-
1311src/views/modules/erf/expApplyList.vue
-
372src/views/modules/erf/plannerSchedule.vue
-
324src/views/modules/erf/triConfirm.vue
@ -1,35 +1,74 @@ |
|||||
<template> |
<template> |
||||
<transition name="fade"> |
|
||||
<router-view></router-view> |
|
||||
</transition> |
|
||||
|
<div id="app"> |
||||
|
<!-- 主内容区域 --> |
||||
|
<transition name="fade"> |
||||
|
<router-view></router-view> |
||||
|
</transition> |
||||
|
|
||||
|
<!-- 全局审批通知管理器 --> |
||||
|
<approval-notification-manager ref="approvalNotificationManager"></approval-notification-manager> |
||||
|
</div> |
||||
</template> |
</template> |
||||
|
|
||||
<script> |
<script> |
||||
|
import ApprovalNotificationManager from '@/components/ApprovalNotificationManager.vue' |
||||
|
|
||||
|
export default { |
||||
|
name: 'App', |
||||
|
|
||||
|
components: { |
||||
|
ApprovalNotificationManager |
||||
|
}, |
||||
|
|
||||
|
data() { |
||||
|
return { |
||||
|
query: {}, |
||||
|
version: '1.3.3' |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
created () { |
||||
|
this.versionReload() |
||||
|
}, |
||||
|
|
||||
export default { |
|
||||
data() { |
|
||||
return { |
|
||||
query: {}, |
|
||||
version: '1.3.3' |
|
||||
|
methods: { |
||||
|
/** |
||||
|
* 版本检查及自动刷新 |
||||
|
*/ |
||||
|
versionReload(){ |
||||
|
let version = this.version //版本号(每次上线前需要更新下版本号) |
||||
|
console.log('最新系统版本: ',version) |
||||
|
console.log('当前系统版本: ',this.version) |
||||
|
let versionLocal = localStorage.getItem('_version_'); |
||||
|
if(version!=versionLocal){ |
||||
|
localStorage.setItem('_version_',version); |
||||
|
this.version=versionLocal; |
||||
|
location.reload(); |
||||
} |
} |
||||
}, |
}, |
||||
created () { |
|
||||
this.versionReload() |
|
||||
|
|
||||
|
/** |
||||
|
* 手动触发审批通知检查(供外部调用) |
||||
|
*/ |
||||
|
checkApprovalNotifications() { |
||||
|
if (this.$refs.approvalNotificationManager) { |
||||
|
this.$refs.approvalNotificationManager.manualCheck() |
||||
|
} |
||||
}, |
}, |
||||
methods: { |
|
||||
versionReload(){ |
|
||||
let version = this.version //版本号(每次上线前需要更新下版本号) |
|
||||
console.log('最新系统版本: ',version) |
|
||||
console.log('当前系统版本: ',this.version) |
|
||||
let versionLocal = localStorage.getItem('_version_'); |
|
||||
if(version!=versionLocal){ |
|
||||
localStorage.setItem('_version_',version); |
|
||||
this.version=versionLocal; |
|
||||
location.reload(); |
|
||||
} |
|
||||
|
|
||||
|
/** |
||||
|
* 清除指定申请单的通知记录(审批完成后调用) |
||||
|
* @param {string} applyNo - 申请单号 |
||||
|
*/ |
||||
|
clearApprovalNotification(applyNo) { |
||||
|
if (this.$refs.approvalNotificationManager) { |
||||
|
this.$refs.approvalNotificationManager.clearNotification(applyNo) |
||||
} |
} |
||||
} |
} |
||||
} |
} |
||||
|
|
||||
|
} |
||||
</script> |
</script> |
||||
|
|
||||
|
<style> |
||||
|
/* 全局样式 */ |
||||
|
</style> |
||||
@ -0,0 +1,103 @@ |
|||||
|
|
||||
|
import { createAPI } from "@/utils/httpRequest.js"; |
||||
|
|
||||
|
// =====================================================
|
||||
|
// 工程实验申请单 API接口定义
|
||||
|
// =====================================================
|
||||
|
|
||||
|
/** |
||||
|
* 查询申请单列表 |
||||
|
*/ |
||||
|
export const searchExpApplyList = data => createAPI(`/erf/expApply/searchExpApplyList`, 'post', data) |
||||
|
|
||||
|
/** |
||||
|
* 根据申请单号查询详情 |
||||
|
*/ |
||||
|
export const getExpApplyDetail = data => createAPI(`/erf/expApply/getExpApplyDetail`, 'post', data) |
||||
|
|
||||
|
/** |
||||
|
* 保存申请单(新增或修改) |
||||
|
*/ |
||||
|
export const saveExpApply = data => createAPI(`/erf/expApply/saveExpApply`, 'post', data) |
||||
|
|
||||
|
/** |
||||
|
* 下达申请单 |
||||
|
*/ |
||||
|
export const submitExpApply = data => createAPI(`/erf/expApply/submitExpApply`, 'post', data) |
||||
|
|
||||
|
/** |
||||
|
* 获取下达时的审批人信息 |
||||
|
*/ |
||||
|
export const getSubmitApprovers = data => createAPI(`/erf/expApply/getSubmitApprovers`, 'post', data) |
||||
|
|
||||
|
/** |
||||
|
* 审批申请单 |
||||
|
*/ |
||||
|
export const approveExpApply = data => createAPI(`/erf/expApply/approveExpApply`, 'post', data) |
||||
|
|
||||
|
/** |
||||
|
* 撤回申请单 |
||||
|
*/ |
||||
|
export const withdrawExpApply = data => createAPI(`/erf/expApply/withdrawExpApply`, 'post', data) |
||||
|
|
||||
|
/** |
||||
|
* 删除申请单 |
||||
|
*/ |
||||
|
export const deleteExpApply = data => createAPI(`/erf/expApply/deleteExpApply`, 'post', data) |
||||
|
|
||||
|
/** |
||||
|
* 查询用户的待办申请单 |
||||
|
*/ |
||||
|
export const getPendingApplyList = data => createAPI(`/erf/expApply/getPendingApplyList`, 'post', data) |
||||
|
|
||||
|
// =====================================================
|
||||
|
// High Risk三方确认 API接口定义
|
||||
|
// =====================================================
|
||||
|
|
||||
|
/** |
||||
|
* 查询三方确认列表 |
||||
|
*/ |
||||
|
export const getTriConfirmList = data => createAPI(`/erf/triConfirm/getTriConfirmList`, 'post', data) |
||||
|
|
||||
|
/** |
||||
|
* 三方确认操作 |
||||
|
*/ |
||||
|
export const confirmTriApproval = data => createAPI(`/erf/triConfirm/confirmTriApproval`, 'post', data) |
||||
|
|
||||
|
// =====================================================
|
||||
|
// 审批历史 API接口定义
|
||||
|
// =====================================================
|
||||
|
|
||||
|
/** |
||||
|
* 查询审批历史 |
||||
|
*/ |
||||
|
export const getApprovalHistory = data => createAPI(`/erf/expApply/getApprovalHistory`, 'post', data) |
||||
|
|
||||
|
/** |
||||
|
* 获取流程状态详情(包含流程进度和审批历史) |
||||
|
*/ |
||||
|
export const getFlowStatus = data => createAPI(`/erf/expApply/getFlowStatus`, 'post', data) |
||||
|
|
||||
|
/** |
||||
|
* 计划员排产 |
||||
|
*/ |
||||
|
export const plannerSchedule = data => createAPI(`/erf/expApply/plannerSchedule`, 'post', data) |
||||
|
|
||||
|
/** |
||||
|
* 获取字段权限 |
||||
|
*/ |
||||
|
export const getFieldAuth = data => createAPI(`/erf/expApply/getFieldAuth`, 'post', data) |
||||
|
|
||||
|
// =====================================================
|
||||
|
// 三方确认 API接口定义
|
||||
|
// =====================================================
|
||||
|
|
||||
|
/** |
||||
|
* 保存三方确认工序 |
||||
|
*/ |
||||
|
export const saveTriConfirmProcess = data => createAPI(`/erf/triConfirm/saveTriConfirmProcess`, 'post', data) |
||||
|
|
||||
|
/** |
||||
|
* 删除三方确认工序 |
||||
|
*/ |
||||
|
export const deleteTriConfirmProcess = data => createAPI(`/erf/triConfirm/deleteTriConfirmProcess`, 'post', data) |
||||
@ -0,0 +1,104 @@ |
|||||
|
/** |
||||
|
* 审批通知样式 |
||||
|
* 优化 Element UI Notification 组件的显示效果 |
||||
|
*/ |
||||
|
|
||||
|
/* 审批通知容器 */ |
||||
|
.el-notification.approval-notification { |
||||
|
width: 380px; |
||||
|
padding: 20px; |
||||
|
border-left: 3px solid #E6A23C; |
||||
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); |
||||
|
|
||||
|
&:hover { |
||||
|
cursor: pointer; |
||||
|
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.25); |
||||
|
transform: translateX(-5px); |
||||
|
transition: all 0.3s ease; |
||||
|
} |
||||
|
|
||||
|
/* 标题样式 */ |
||||
|
.el-notification__title { |
||||
|
font-size: 16px; |
||||
|
font-weight: bold; |
||||
|
color: #E6A23C; |
||||
|
margin-bottom: 10px; |
||||
|
} |
||||
|
|
||||
|
/* 内容样式 */ |
||||
|
.el-notification__content { |
||||
|
font-size: 14px; |
||||
|
line-height: 1.8; |
||||
|
color: #303133; |
||||
|
|
||||
|
p { |
||||
|
margin: 5px 0; |
||||
|
} |
||||
|
|
||||
|
strong { |
||||
|
color: #303133; |
||||
|
font-weight: 600; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/* 关闭按钮 */ |
||||
|
.el-notification__closeBtn { |
||||
|
color: #909399; |
||||
|
font-size: 18px; |
||||
|
|
||||
|
&:hover { |
||||
|
color: #E6A23C; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/* 新申请单通知 */ |
||||
|
.el-notification.new-approval-notification { |
||||
|
border-left-color: #409EFF; |
||||
|
|
||||
|
.el-notification__title { |
||||
|
color: #409EFF; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/* 汇总通知 */ |
||||
|
.el-notification.summary-notification { |
||||
|
border-left-color: #E6A23C; |
||||
|
|
||||
|
.el-notification__title { |
||||
|
color: #E6A23C; |
||||
|
} |
||||
|
|
||||
|
/* 数字高亮 */ |
||||
|
.count-highlight { |
||||
|
color: #E6A23C; |
||||
|
font-size: 18px; |
||||
|
font-weight: bold; |
||||
|
padding: 0 5px; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/* 通知动画 */ |
||||
|
@keyframes notification-shake { |
||||
|
0%, 100% { |
||||
|
transform: translateX(0); |
||||
|
} |
||||
|
25% { |
||||
|
transform: translateX(-5px); |
||||
|
} |
||||
|
75% { |
||||
|
transform: translateX(5px); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.el-notification.shake-animation { |
||||
|
animation: notification-shake 0.5s ease; |
||||
|
} |
||||
|
|
||||
|
/* 响应式适配 */ |
||||
|
@media screen and (max-width: 768px) { |
||||
|
.el-notification.approval-notification { |
||||
|
width: 320px; |
||||
|
padding: 15px; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,564 @@ |
|||||
|
<template> |
||||
|
<!-- 全局审批通知管理组件 - 无界面,纯逻辑 --> |
||||
|
<div style="display: none;"></div> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import { getPendingApplyList } from '@/api/erf/erf' |
||||
|
import approvalConfig from '@/config/approval-notification.config' |
||||
|
|
||||
|
/** |
||||
|
* 全局审批通知管理器 |
||||
|
* |
||||
|
* 功能: |
||||
|
* 1. 登录后自动检查待审批项 |
||||
|
* 2. 定时轮询检查新的待审批项(默认5分钟,可在配置文件中修改) |
||||
|
* 3. 在浏览器右下角弹出通知提示 |
||||
|
* 4. 点击通知跳转到审批页面 |
||||
|
* |
||||
|
* 配置文件:src/config/approval-notification.config.js |
||||
|
*/ |
||||
|
export default { |
||||
|
name: 'ApprovalNotificationManager', |
||||
|
|
||||
|
data() { |
||||
|
return { |
||||
|
pollingTimer: null, // 轮询定时器 |
||||
|
pollingInterval: approvalConfig.polling.interval, // 轮询间隔(从配置文件读取) |
||||
|
lastCheckTime: null, // 上次检查时间 |
||||
|
notifiedApplications: new Set(), // 已通知过的申请单号集合(避免重复通知) |
||||
|
isInitialized: false, // 是否已初始化 |
||||
|
audioContext: null, // 音频上下文(用于提示音) |
||||
|
config: approvalConfig, // 配置对象 |
||||
|
isChecking: false, // 是否正在检查中(防止重复调用) |
||||
|
firstCheckTimeout: null, // 首次检查的定时器 |
||||
|
activeNotifications: [], // 当前显示的所有通知实例 |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
computed: { |
||||
|
/** |
||||
|
* 当前登录用户ID |
||||
|
*/ |
||||
|
currentUserId() { |
||||
|
return this.$store.state.user.id |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 当前登录用户名称 |
||||
|
*/ |
||||
|
currentUserName() { |
||||
|
return this.$store.state.user.name |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 当前站点 |
||||
|
*/ |
||||
|
currentSite() { |
||||
|
return this.$store.state.user.site |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 是否已登录 |
||||
|
*/ |
||||
|
isLoggedIn() { |
||||
|
return this.currentUserId && this.currentUserId !== 0 |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
watch: { |
||||
|
/** |
||||
|
* 监听用户登录状态变化 |
||||
|
*/ |
||||
|
isLoggedIn(newVal, oldVal) { |
||||
|
console.log(`[审批通知] 登录状态变化: ${oldVal} -> ${newVal}, 已初始化: ${this.isInitialized}`) |
||||
|
|
||||
|
// 只有真正的登录状态变化才处理 |
||||
|
if (oldVal === newVal) { |
||||
|
console.log('[审批通知] 登录状态未变化,跳过') |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
if (newVal && !this.isInitialized) { |
||||
|
console.log('[审批通知] 用户已登录(watch触发),初始化通知系统') |
||||
|
// 延迟执行,避免与 mounted 冲突 |
||||
|
this.$nextTick(() => { |
||||
|
if (!this.isInitialized) { |
||||
|
this.initializeNotificationSystem() |
||||
|
} |
||||
|
}) |
||||
|
} else if (!newVal && this.isInitialized) { |
||||
|
console.log('[审批通知] 用户已登出,停止通知系统') |
||||
|
this.stopNotificationSystem() |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
mounted() { |
||||
|
console.log(`[审批通知] 组件挂载, 登录状态: ${this.isLoggedIn}, 已初始化: ${this.isInitialized}`) |
||||
|
// 组件挂载时,如果已登录且未初始化则初始化 |
||||
|
// 使用延迟确保只执行一次 |
||||
|
this.$nextTick(() => { |
||||
|
if (this.isLoggedIn && !this.isInitialized) { |
||||
|
console.log('[审批通知] 组件挂载时初始化通知系统') |
||||
|
this.initializeNotificationSystem() |
||||
|
} |
||||
|
}) |
||||
|
}, |
||||
|
|
||||
|
beforeDestroy() { |
||||
|
// 组件销毁时清理资源 |
||||
|
console.log('[审批通知] 组件销毁,清理所有资源') |
||||
|
this.stopNotificationSystem() |
||||
|
}, |
||||
|
|
||||
|
methods: { |
||||
|
/** |
||||
|
* 初始化通知系统 |
||||
|
*/ |
||||
|
initializeNotificationSystem() { |
||||
|
// 双重检查锁 |
||||
|
if (this.isInitialized) { |
||||
|
console.log('[审批通知] 系统已初始化,跳过重复初始化') |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
console.log('[审批通知] 开始初始化通知系统...') |
||||
|
|
||||
|
// 立即设置标志,防止并发调用 |
||||
|
this.isInitialized = true |
||||
|
|
||||
|
// 先清理可能存在的旧资源 |
||||
|
this.stopPolling() |
||||
|
this.closeAllNotifications() |
||||
|
|
||||
|
// 清理可能存在的首次检查定时器 |
||||
|
if (this.firstCheckTimeout) { |
||||
|
clearTimeout(this.firstCheckTimeout) |
||||
|
this.firstCheckTimeout = null |
||||
|
} |
||||
|
|
||||
|
// 延迟后首次检查(避免登录时的接口压力) |
||||
|
this.firstCheckTimeout = setTimeout(() => { |
||||
|
if (this.isInitialized && this.isLoggedIn) { |
||||
|
this.checkPendingApprovals(true) |
||||
|
} |
||||
|
this.firstCheckTimeout = null |
||||
|
}, this.config.polling.firstCheckDelay) |
||||
|
|
||||
|
// 启动定时轮询 |
||||
|
this.startPolling() |
||||
|
|
||||
|
console.log('[审批通知] 通知系统初始化完成') |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 停止通知系统 |
||||
|
*/ |
||||
|
stopNotificationSystem() { |
||||
|
console.log('[审批通知] 停止通知系统...') |
||||
|
|
||||
|
// 关闭所有已显示的通知窗口 |
||||
|
this.closeAllNotifications() |
||||
|
|
||||
|
// 停止轮询 |
||||
|
this.stopPolling() |
||||
|
|
||||
|
// 清理首次检查定时器 |
||||
|
if (this.firstCheckTimeout) { |
||||
|
clearTimeout(this.firstCheckTimeout) |
||||
|
this.firstCheckTimeout = null |
||||
|
} |
||||
|
|
||||
|
// 重置状态 |
||||
|
this.isInitialized = false |
||||
|
this.isChecking = false |
||||
|
this.notifiedApplications.clear() |
||||
|
this.lastCheckTime = null |
||||
|
|
||||
|
console.log('[审批通知] 通知系统已停止') |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 关闭所有活动的通知窗口 |
||||
|
*/ |
||||
|
closeAllNotifications() { |
||||
|
console.log(`[审批通知] 关闭所有通知窗口,当前数量: ${this.activeNotifications.length}`) |
||||
|
|
||||
|
// 关闭所有通知 |
||||
|
this.activeNotifications.forEach(notification => { |
||||
|
try { |
||||
|
if (notification && typeof notification.close === 'function') { |
||||
|
notification.close() |
||||
|
} |
||||
|
} catch (error) { |
||||
|
console.error('[审批通知] 关闭通知失败:', error) |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
// 清空通知列表 |
||||
|
this.activeNotifications = [] |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 从活动列表中移除指定的通知实例 |
||||
|
* @param {Object} notification - 要移除的通知实例 |
||||
|
*/ |
||||
|
removeNotification(notification) { |
||||
|
const index = this.activeNotifications.indexOf(notification) |
||||
|
if (index > -1) { |
||||
|
this.activeNotifications.splice(index, 1) |
||||
|
console.log(`[审批通知] 已移除通知实例,剩余活动通知数: ${this.activeNotifications.length}`) |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 启动定时轮询 |
||||
|
*/ |
||||
|
startPolling() { |
||||
|
if (this.pollingTimer) { |
||||
|
console.log('[审批通知] 定时器已存在,跳过创建') |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
this.pollingTimer = setInterval(() => { |
||||
|
if (this.isLoggedIn && this.isInitialized) { |
||||
|
this.checkPendingApprovals(false) |
||||
|
} |
||||
|
}, this.pollingInterval) |
||||
|
|
||||
|
console.log(`[审批通知] 定时轮询已启动,定时器ID: ${this.pollingTimer}, 间隔 ${this.pollingInterval / 1000} 秒`) |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 停止定时轮询 |
||||
|
*/ |
||||
|
stopPolling() { |
||||
|
if (this.pollingTimer) { |
||||
|
console.log(`[审批通知] 清除定时器,ID: ${this.pollingTimer}`) |
||||
|
clearInterval(this.pollingTimer) |
||||
|
this.pollingTimer = null |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 检查待审批的申请单 |
||||
|
* @param {boolean} isFirstCheck - 是否首次检查 |
||||
|
*/ |
||||
|
checkPendingApprovals(isFirstCheck = false) { |
||||
|
if (!this.isLoggedIn) { |
||||
|
console.log('[审批通知] 用户未登录,跳过检查') |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
// 防止重复调用 |
||||
|
if (this.isChecking) { |
||||
|
console.log('[审批通知] 正在检查中,跳过本次调用') |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
this.isChecking = true |
||||
|
console.log(`[审批通知] 开始检查待审批申请单... (首次检查: ${isFirstCheck})`) |
||||
|
|
||||
|
const requestData = { |
||||
|
site: this.currentSite, |
||||
|
currentUserId: this.currentUserId, |
||||
|
userName: this.currentUserName, |
||||
|
page :1, |
||||
|
limit :20 |
||||
|
} |
||||
|
|
||||
|
getPendingApplyList(requestData).then(({data}) => { |
||||
|
if (data && data.code === 0) { |
||||
|
const pendingList = data.rows || data.page.list || [] |
||||
|
|
||||
|
console.log(`[审批通知] 发现 ${pendingList.length} 个待审批申请单`) |
||||
|
|
||||
|
if (pendingList.length > 0) { |
||||
|
if (isFirstCheck) { |
||||
|
// 首次检查:显示汇总通知 |
||||
|
this.showSummaryNotification(pendingList) |
||||
|
} else { |
||||
|
// 轮询检查:只通知新的申请单 |
||||
|
this.showNewApprovalNotifications(pendingList) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
this.lastCheckTime = new Date() |
||||
|
} |
||||
|
this.isChecking = false |
||||
|
}).catch(error => { |
||||
|
console.error('[审批通知] 检查待审批申请单失败:', error) |
||||
|
this.isChecking = false |
||||
|
}) |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 显示汇总通知(首次登录时) |
||||
|
* @param {Array} approvalList - 待审批列表 |
||||
|
*/ |
||||
|
showSummaryNotification(approvalList) { |
||||
|
const count = approvalList.length |
||||
|
|
||||
|
// 记录所有申请单号(避免重复通知) |
||||
|
approvalList.forEach(item => { |
||||
|
this.notifiedApplications.add(item.applyNo || item.applicationNo) |
||||
|
}) |
||||
|
|
||||
|
// 创建通知实例 |
||||
|
const notificationInstance = this.$notify({ |
||||
|
title: '待审批提醒', |
||||
|
customClass: this.config.style.summaryClass, |
||||
|
dangerouslyUseHTMLString: true, |
||||
|
message: ` |
||||
|
<div style="line-height: 1.6;"> |
||||
|
<p style="margin: 0 0 8px 0;"> |
||||
|
您有 <span class="count-highlight">${count}</span> 个工程实验申请单待审批 |
||||
|
</p> |
||||
|
<p style="margin: 0; font-size: 12px; color: #909399;"> |
||||
|
点击此通知查看详情 |
||||
|
</p> |
||||
|
</div> |
||||
|
`, |
||||
|
type: 'warning', |
||||
|
position: this.config.notification.position, |
||||
|
duration: this.config.notification.summaryDuration, |
||||
|
showClose: this.config.notification.showClose, |
||||
|
onClick: () => { |
||||
|
// 点击后立即关闭通知并从列表中移除 |
||||
|
this.removeNotification(notificationInstance) |
||||
|
notificationInstance.close() |
||||
|
// 跳转到审批列表 |
||||
|
this.navigateToApprovalList() |
||||
|
}, |
||||
|
onClose: () => { |
||||
|
// 通知关闭时从列表中移除 |
||||
|
this.removeNotification(notificationInstance) |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
// 保存通知实例 |
||||
|
this.activeNotifications.push(notificationInstance) |
||||
|
console.log(`[审批通知] 已创建汇总通知,当前活动通知数: ${this.activeNotifications.length}`) |
||||
|
|
||||
|
// 播放提示音 |
||||
|
// this.playNotificationSound() |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 显示新的审批通知(轮询时) |
||||
|
* @param {Array} approvalList - 待审批列表 |
||||
|
*/ |
||||
|
showNewApprovalNotifications(approvalList) { |
||||
|
// 筛选出新的申请单 |
||||
|
const newApprovals = approvalList.filter(item => { |
||||
|
const applyNo = item.applyNo || item.applicationNo |
||||
|
return !this.notifiedApplications.has(applyNo) |
||||
|
}) |
||||
|
|
||||
|
if (newApprovals.length === 0) { |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
console.log(`[审批通知] 发现 ${newApprovals.length} 个新的待审批申请单`) |
||||
|
|
||||
|
// 显示每个新申请单的通知 |
||||
|
newApprovals.forEach((item, index) => { |
||||
|
// 延迟显示,避免多个通知同时弹出 |
||||
|
setTimeout(() => { |
||||
|
this.showSingleApprovalNotification(item) |
||||
|
}, index * 500) |
||||
|
}) |
||||
|
|
||||
|
// 播放提示音 |
||||
|
//this.playNotificationSound() |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 显示单个审批通知 |
||||
|
* @param {Object} approvalItem - 审批项信息 |
||||
|
*/ |
||||
|
showSingleApprovalNotification(approvalItem) { |
||||
|
const applyNo = approvalItem.applyNo || approvalItem.applicationNo |
||||
|
const applicant = approvalItem.creatorName || approvalItem.applyUserName || '未知' |
||||
|
const applyType = approvalItem.experimentType || approvalItem.applicationType || '未知' |
||||
|
const createdDate = approvalItem.createTime || approvalItem.applyDate || '' |
||||
|
|
||||
|
// 记录已通知 |
||||
|
this.notifiedApplications.add(applyNo) |
||||
|
|
||||
|
// 创建通知实例 |
||||
|
const notificationInstance = this.$notify({ |
||||
|
title: '新的审批申请', |
||||
|
customClass: this.config.style.singleClass, |
||||
|
dangerouslyUseHTMLString: true, |
||||
|
message: ` |
||||
|
<div style="line-height: 1.8;"> |
||||
|
<p style="margin: 0 0 5px 0;"> |
||||
|
<strong style="color: #303133;">申请单号:</strong> |
||||
|
<span style="color: #409EFF;">${applyNo}</span> |
||||
|
</p> |
||||
|
<p style="margin: 0 0 5px 0;"> |
||||
|
<strong style="color: #303133;">申请人:</strong>${applicant} |
||||
|
</p> |
||||
|
<p style="margin: 0 0 5px 0;"> |
||||
|
<strong style="color: #303133;">申请类型:</strong>${applyType} |
||||
|
</p> |
||||
|
${createdDate ? ` |
||||
|
<p style="margin: 0 0 5px 0;"> |
||||
|
<strong style="color: #303133;">申请时间:</strong> |
||||
|
<span style="font-size: 12px;">${createdDate}</span> |
||||
|
</p> |
||||
|
` : ''} |
||||
|
<p style="margin: 8px 0 0 0; font-size: 12px; color: #909399; border-top: 1px dashed #DCDFE6; padding-top: 8px;"> |
||||
|
点击处理审批 |
||||
|
</p> |
||||
|
</div> |
||||
|
`, |
||||
|
type: 'info', |
||||
|
position: this.config.notification.position, |
||||
|
duration: this.config.notification.singleDuration, |
||||
|
showClose: this.config.notification.showClose, |
||||
|
onClick: () => { |
||||
|
// 点击后立即关闭通知并从列表中移除 |
||||
|
this.removeNotification(notificationInstance) |
||||
|
notificationInstance.close() |
||||
|
// 跳转到审批详情 |
||||
|
this.navigateToApprovalDetail(approvalItem) |
||||
|
}, |
||||
|
onClose: () => { |
||||
|
// 通知关闭时从列表中移除 |
||||
|
this.removeNotification(notificationInstance) |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
// 保存通知实例 |
||||
|
this.activeNotifications.push(notificationInstance) |
||||
|
console.log(`[审批通知] 已创建单个通知 (${applyNo}),当前活动通知数: ${this.activeNotifications.length}`) |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 跳转到审批列表页面 |
||||
|
*/ |
||||
|
navigateToApprovalList() { |
||||
|
// 根据配置文件中的路由进行跳转 |
||||
|
this.$router.push({ |
||||
|
path: 'erf-expApplyApproval' |
||||
|
}).catch(err => { |
||||
|
this.log('路由跳转失败:', err.message, 'warn') |
||||
|
}) |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 跳转到审批详情页面 |
||||
|
* @param {Object} approvalItem - 审批项信息 |
||||
|
*/ |
||||
|
navigateToApprovalDetail(approvalItem) { |
||||
|
const applyNo = approvalItem.applyNo || approvalItem.applicationNo |
||||
|
|
||||
|
// 根据配置文件中的路由进行跳转 |
||||
|
const queryParam = this.config.routes.detailQueryParam |
||||
|
const query = { |
||||
|
[queryParam]: applyNo, |
||||
|
mode: 'approve' |
||||
|
} |
||||
|
|
||||
|
this.$router.push({ |
||||
|
path: 'erf-expApplyApproval', |
||||
|
query: query |
||||
|
}).catch(err => { |
||||
|
// 如果没有专门的审批详情页,跳转到列表页 |
||||
|
this.log('路由跳转失败,尝试跳转到列表页: ' + err.message, 'warn') |
||||
|
this.navigateToApprovalList() |
||||
|
}) |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 播放通知提示音 |
||||
|
*/ |
||||
|
playNotificationSound() { |
||||
|
// 检查是否启用提示音 |
||||
|
if (!this.config.sound.enabled || !this.config.features.playSound) { |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
try { |
||||
|
// 使用 Web Audio API 生成简单的提示音 |
||||
|
const audioContext = new (window.AudioContext || window.webkitAudioContext)() |
||||
|
const oscillator = audioContext.createOscillator() |
||||
|
const gainNode = audioContext.createGain() |
||||
|
|
||||
|
oscillator.connect(gainNode) |
||||
|
gainNode.connect(audioContext.destination) |
||||
|
|
||||
|
oscillator.frequency.value = this.config.sound.frequency // 频率 |
||||
|
oscillator.type = 'sine' // 正弦波 |
||||
|
|
||||
|
const duration = this.config.sound.duration |
||||
|
const volume = this.config.sound.volume |
||||
|
|
||||
|
gainNode.gain.setValueAtTime(volume, audioContext.currentTime) |
||||
|
gainNode.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + duration) |
||||
|
|
||||
|
oscillator.start(audioContext.currentTime) |
||||
|
oscillator.stop(audioContext.currentTime + duration) |
||||
|
} catch (error) { |
||||
|
this.log('播放提示音失败: ' + error.message, 'warn') |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 手动触发检查(供外部调用) |
||||
|
*/ |
||||
|
manualCheck() { |
||||
|
console.log('[审批通知] 手动触发检查') |
||||
|
this.checkPendingApprovals(false) |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 清除指定申请单的通知记录(审批完成后调用) |
||||
|
* @param {string} applyNo - 申请单号 |
||||
|
*/ |
||||
|
clearNotification(applyNo) { |
||||
|
this.notifiedApplications.delete(applyNo) |
||||
|
this.log(`已清除申请单 ${applyNo} 的通知记录`, 'info') |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 日志输出辅助方法 |
||||
|
* @param {string} message - 日志消息 |
||||
|
* @param {string} level - 日志级别 ('info', 'warn', 'error', 'debug') |
||||
|
*/ |
||||
|
log(message, level = 'info') { |
||||
|
if (!this.config.logging.enabled) { |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
const prefix = this.config.logging.prefix |
||||
|
const fullMessage = `${prefix} ${message}` |
||||
|
|
||||
|
switch (level) { |
||||
|
case 'error': |
||||
|
console.error(fullMessage) |
||||
|
break |
||||
|
case 'warn': |
||||
|
console.warn(fullMessage) |
||||
|
break |
||||
|
case 'debug': |
||||
|
if (this.config.debug.enabled) { |
||||
|
console.debug(fullMessage) |
||||
|
} |
||||
|
break |
||||
|
case 'info': |
||||
|
default: |
||||
|
console.log(fullMessage) |
||||
|
break |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style scoped> |
||||
|
/* 无需样式 */ |
||||
|
</style> |
||||
@ -0,0 +1,192 @@ |
|||||
|
/** |
||||
|
* 审批通知系统配置文件 |
||||
|
* |
||||
|
* 可以在这里统一配置通知系统的各项参数 |
||||
|
* 修改后需要重新编译项目才能生效 |
||||
|
*/ |
||||
|
|
||||
|
export default { |
||||
|
/** |
||||
|
* 轮询配置 |
||||
|
*/ |
||||
|
polling: { |
||||
|
// 轮询间隔(毫秒)
|
||||
|
// 默认: 5分钟 (5 * 60 * 1000)
|
||||
|
// 建议: 不低于3分钟,避免频繁请求
|
||||
|
interval: 5 * 60 * 1000, |
||||
|
|
||||
|
// 首次检查延迟(毫秒)
|
||||
|
// 登录后延迟多久进行首次检查
|
||||
|
// 默认: 3秒
|
||||
|
firstCheckDelay: 3000, |
||||
|
|
||||
|
// 是否启用轮询
|
||||
|
// 设置为 false 可以禁用定时轮询功能
|
||||
|
enabled: true |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 通知显示配置 |
||||
|
*/ |
||||
|
notification: { |
||||
|
// 通知位置
|
||||
|
// 可选值: 'top-right', 'top-left', 'bottom-right', 'bottom-left'
|
||||
|
position: 'bottom-right', |
||||
|
|
||||
|
// 汇总通知持续时间(毫秒)
|
||||
|
// 0 表示不自动关闭
|
||||
|
summaryDuration: 0, |
||||
|
|
||||
|
// 单个通知持续时间(毫秒)
|
||||
|
// 0 表示不自动关闭
|
||||
|
singleDuration: 10000, |
||||
|
|
||||
|
// 是否显示关闭按钮
|
||||
|
showClose: true, |
||||
|
|
||||
|
// 多个通知同时弹出时的间隔(毫秒)
|
||||
|
stackDelay: 500, |
||||
|
|
||||
|
// 最大同时显示的通知数量
|
||||
|
// 超过此数量的通知将排队显示
|
||||
|
maxStack: 3 |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 提示音配置 |
||||
|
*/ |
||||
|
sound: { |
||||
|
// 是否启用提示音
|
||||
|
enabled: true, |
||||
|
|
||||
|
// 提示音频率(Hz)
|
||||
|
frequency: 800, |
||||
|
|
||||
|
// 提示音持续时间(秒)
|
||||
|
duration: 0.5, |
||||
|
|
||||
|
// 提示音音量(0-1)
|
||||
|
volume: 0.3 |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 路由配置 |
||||
|
*/ |
||||
|
routes: { |
||||
|
// 审批列表页面路由
|
||||
|
approvalList: '/erf/approval-list', |
||||
|
|
||||
|
// 审批详情页面路由
|
||||
|
approvalDetail: '/erf/approval-detail', |
||||
|
|
||||
|
// 详情页面查询参数名称
|
||||
|
detailQueryParam: 'applyNo' |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 日志配置 |
||||
|
*/ |
||||
|
logging: { |
||||
|
// 是否启用控制台日志
|
||||
|
enabled: true, |
||||
|
|
||||
|
// 日志前缀
|
||||
|
prefix: '[审批通知]', |
||||
|
|
||||
|
// 日志级别
|
||||
|
// 'debug', 'info', 'warn', 'error'
|
||||
|
level: 'info' |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 样式配置 |
||||
|
*/ |
||||
|
style: { |
||||
|
// 汇总通知自定义样式类名
|
||||
|
summaryClass: 'approval-notification summary-notification', |
||||
|
|
||||
|
// 单个通知自定义样式类名
|
||||
|
singleClass: 'approval-notification new-approval-notification shake-animation', |
||||
|
|
||||
|
// 是否启用动画效果
|
||||
|
enableAnimation: true |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* API配置 |
||||
|
*/ |
||||
|
api: { |
||||
|
// 获取待审批列表的API路径
|
||||
|
pendingListUrl: '/erf/expApply/getPendingApplyList', |
||||
|
|
||||
|
// 请求超时时间(毫秒)
|
||||
|
timeout: 10000, |
||||
|
|
||||
|
// 请求失败后是否重试
|
||||
|
enableRetry: false, |
||||
|
|
||||
|
// 重试次数
|
||||
|
retryCount: 3, |
||||
|
|
||||
|
// 重试间隔(毫秒)
|
||||
|
retryDelay: 2000 |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 功能开关 |
||||
|
*/ |
||||
|
features: { |
||||
|
// 是否在登录时检查待审批项
|
||||
|
checkOnLogin: true, |
||||
|
|
||||
|
// 是否在申请单下达后立即通知
|
||||
|
notifyOnSubmit: true, |
||||
|
|
||||
|
// 是否启用定时轮询
|
||||
|
enablePolling: true, |
||||
|
|
||||
|
// 是否避免重复通知
|
||||
|
preventDuplicate: true, |
||||
|
|
||||
|
// 是否在所有页面显示通知
|
||||
|
globalNotification: true, |
||||
|
|
||||
|
// 是否播放提示音
|
||||
|
playSound: true |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 用户权限配置 |
||||
|
*/ |
||||
|
permission: { |
||||
|
// 哪些角色可以接收审批通知
|
||||
|
// 空数组表示所有用户都可以接收
|
||||
|
allowedRoles: [], |
||||
|
|
||||
|
// 哪些用户可以接收审批通知
|
||||
|
// 空数组表示所有用户都可以接收
|
||||
|
allowedUsers: [] |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 调试配置 |
||||
|
*/ |
||||
|
debug: { |
||||
|
// 是否启用调试模式
|
||||
|
enabled: false, |
||||
|
|
||||
|
// 是否在控制台输出详细信息
|
||||
|
verbose: false, |
||||
|
|
||||
|
// 是否模拟通知(用于测试)
|
||||
|
mockNotification: false, |
||||
|
|
||||
|
// 模拟数据
|
||||
|
mockData: { |
||||
|
applyNo: 'TEST202501150001', |
||||
|
applicant: '测试用户', |
||||
|
applyType: 'High Risk', |
||||
|
createdDate: '2025-01-15 10:30:00' |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,154 @@ |
|||||
|
<template> |
||||
|
<div class="approval-history"> |
||||
|
<el-timeline> |
||||
|
<el-timeline-item |
||||
|
v-for="(item, index) in historyList" |
||||
|
:key="index" |
||||
|
:timestamp="item.logTime" |
||||
|
:type="getTimelineType(item.action)" |
||||
|
placement="top"> |
||||
|
|
||||
|
<el-card> |
||||
|
<h4>{{ getActionText(item.action) }}</h4> |
||||
|
<p> |
||||
|
<el-tag :type="getActionTagType(item.action)" size="small"> |
||||
|
{{ getActionText(item.action) }} |
||||
|
</el-tag> |
||||
|
<span style="margin-left: 10px;">操作人:{{ item.operatorName }}</span> |
||||
|
</p> |
||||
|
<p v-if="item.nodeCode"> |
||||
|
<span>节点:{{ getNodeName(item.nodeCode) }}</span> |
||||
|
</p> |
||||
|
<p v-if="item.comment" style="color: #666; margin-top: 8px;"> |
||||
|
意见:{{ item.comment }} |
||||
|
</p> |
||||
|
</el-card> |
||||
|
</el-timeline-item> |
||||
|
</el-timeline> |
||||
|
|
||||
|
<el-empty v-if="historyList.length === 0" description="暂无审批历史"></el-empty> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import { getApprovalHistory } from '@/api/erf/erf' |
||||
|
|
||||
|
export default { |
||||
|
name: 'ApprovalHistory', |
||||
|
|
||||
|
props: { |
||||
|
applyNo: { |
||||
|
type: String, |
||||
|
required: true |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
data() { |
||||
|
return { |
||||
|
historyList: [], |
||||
|
loading: false |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
mounted() { |
||||
|
this.loadHistory() |
||||
|
}, |
||||
|
|
||||
|
watch: { |
||||
|
applyNo(newVal) { |
||||
|
if (newVal) { |
||||
|
this.loadHistory() |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
methods: { |
||||
|
/** |
||||
|
* 加载审批历史 |
||||
|
*/ |
||||
|
loadHistory() { |
||||
|
if (!this.applyNo) { |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
this.loading = true |
||||
|
getApprovalHistory({ applyNo: this.applyNo }).then(({data}) => { |
||||
|
this.loading = false |
||||
|
if (data && data.code === 0) { |
||||
|
this.historyList = data.list || [] |
||||
|
} |
||||
|
}).catch(error => { |
||||
|
this.loading = false |
||||
|
}) |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 获取操作文本 |
||||
|
*/ |
||||
|
getActionText(action) { |
||||
|
const texts = { |
||||
|
'SUBMIT': '提交申请', |
||||
|
'APPROVE': '审批通过', |
||||
|
'REJECT': '审批驳回', |
||||
|
'WITHDRAW': '撤回申请', |
||||
|
'SCHEDULE': '计划排产', |
||||
|
'CONFIRM': '三方确认', |
||||
|
'COMPLETE': '完成流程' |
||||
|
} |
||||
|
return texts[action] || action |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 获取时间线类型 |
||||
|
*/ |
||||
|
getTimelineType(action) { |
||||
|
const types = { |
||||
|
'SUBMIT': 'primary', |
||||
|
'APPROVE': 'success', |
||||
|
'REJECT': 'danger', |
||||
|
'WITHDRAW': 'warning', |
||||
|
'SCHEDULE': 'primary', |
||||
|
'CONFIRM': 'success', |
||||
|
'COMPLETE': 'success' |
||||
|
} |
||||
|
return types[action] || 'info' |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 获取标签类型 |
||||
|
*/ |
||||
|
getActionTagType(action) { |
||||
|
const types = { |
||||
|
'SUBMIT': '', |
||||
|
'APPROVE': 'success', |
||||
|
'REJECT': 'danger', |
||||
|
'WITHDRAW': 'warning', |
||||
|
'SCHEDULE': '', |
||||
|
'CONFIRM': 'success', |
||||
|
'COMPLETE': 'success' |
||||
|
} |
||||
|
return types[action] || 'info' |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 获取节点名称 |
||||
|
*/ |
||||
|
getNodeName(nodeCode) { |
||||
|
const names = { |
||||
|
'TECH_MANAGER_APPROVAL': '技术经理审批', |
||||
|
'PROD_MANAGER_APPROVAL': '生产经理审批', |
||||
|
'PLANNER_SCHEDULE': '计划员排产', |
||||
|
'TRI_CONFIRM': 'High Risk三方确认', |
||||
|
'COMPLETE': '流程完成' |
||||
|
} |
||||
|
return names[nodeCode] || nodeCode |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style scoped> |
||||
|
.approval-history { |
||||
|
padding: 20px; |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,537 @@ |
|||||
|
<template> |
||||
|
<div class="erf-attachment-manager"> |
||||
|
<!-- 操作按钮 --> |
||||
|
<div style="margin-bottom: 10px"> |
||||
|
<el-button |
||||
|
type="primary" |
||||
|
size="small" |
||||
|
v-if="!disabled" |
||||
|
@click="handleUpload"> |
||||
|
上传附件 |
||||
|
</el-button> |
||||
|
<el-button |
||||
|
type="primary" |
||||
|
size="small" |
||||
|
@click="handleDownload"> |
||||
|
下载 |
||||
|
</el-button> |
||||
|
</div> |
||||
|
|
||||
|
<!-- 附件列表表格 --> |
||||
|
<el-table |
||||
|
:height="height" |
||||
|
:data="dataList" |
||||
|
ref="table" |
||||
|
v-loading="queryLoading" |
||||
|
border |
||||
|
@selection-change="handleSelectionChange" |
||||
|
style="width: 100%;"> |
||||
|
|
||||
|
<el-table-column type="selection" width="55" align="center"/> |
||||
|
|
||||
|
<el-table-column |
||||
|
prop="fileName" |
||||
|
label="文件名" |
||||
|
min-width="200" |
||||
|
align="left" |
||||
|
header-align="center" |
||||
|
show-overflow-tooltip> |
||||
|
</el-table-column> |
||||
|
|
||||
|
<el-table-column |
||||
|
prop="fileType" |
||||
|
label="文件类型" |
||||
|
min-width="100" |
||||
|
align="center" |
||||
|
header-align="center"> |
||||
|
</el-table-column> |
||||
|
|
||||
|
<el-table-column |
||||
|
prop="createdBy" |
||||
|
label="上传人" |
||||
|
min-width="100" |
||||
|
align="center" |
||||
|
header-align="center"> |
||||
|
</el-table-column> |
||||
|
|
||||
|
<el-table-column |
||||
|
prop="createDate" |
||||
|
label="上传时间" |
||||
|
min-width="160" |
||||
|
align="center" |
||||
|
header-align="center"> |
||||
|
</el-table-column> |
||||
|
|
||||
|
<el-table-column |
||||
|
prop="orderRef5" |
||||
|
label="备注" |
||||
|
min-width="160" |
||||
|
align="center" |
||||
|
header-align="center"> |
||||
|
</el-table-column> |
||||
|
|
||||
|
<el-table-column |
||||
|
label="操作" |
||||
|
width="150" |
||||
|
align="center" |
||||
|
header-align="center" |
||||
|
fixed="right"> |
||||
|
<template slot-scope="{row}"> |
||||
|
<el-link |
||||
|
style="cursor:pointer; margin-right: 10px;" |
||||
|
@click="previewFile(row)"> |
||||
|
预览 |
||||
|
</el-link> |
||||
|
<el-link |
||||
|
style="cursor:pointer; color: #F56C6C;" |
||||
|
v-if="!disabled" |
||||
|
@click="handleRemove(row)"> |
||||
|
删除 |
||||
|
</el-link> |
||||
|
</template> |
||||
|
</el-table-column> |
||||
|
</el-table> |
||||
|
|
||||
|
<!-- 上传对话框 --> |
||||
|
<el-dialog |
||||
|
title="上传附件" |
||||
|
:visible.sync="ossVisible" |
||||
|
width="380px" |
||||
|
append-to-body |
||||
|
:close-on-click-modal="false"> |
||||
|
|
||||
|
<el-form :inline="true" label-position="top" label-width="80px"> |
||||
|
<el-row> |
||||
|
<el-form-item label="申请单号"> |
||||
|
<el-input v-model="ossForm.orderRef2" readonly style="width: 310px"></el-input> |
||||
|
</el-form-item> |
||||
|
</el-row> |
||||
|
</el-form> |
||||
|
|
||||
|
<!-- 文件上传区域 --> |
||||
|
<div style="margin: 15px 0;"> |
||||
|
<div style="margin-bottom: 10px;"> |
||||
|
选择文件: |
||||
|
<span style="color: #67C23A; font-size: 12px; margin-left: 10px;"> |
||||
|
<i class="el-icon-picture-outline"></i> 可支持直接 Ctrl+V 粘贴图片,不需要点击上传按钮 |
||||
|
</span> |
||||
|
</div> |
||||
|
<el-upload |
||||
|
drag |
||||
|
action="#" |
||||
|
ref="upload" |
||||
|
:file-list="fileList" |
||||
|
:on-remove="onRemoveFile" |
||||
|
:on-change="onChangeFile" |
||||
|
multiple |
||||
|
:auto-upload="false" |
||||
|
style="text-align: left;"> |
||||
|
<i class="el-icon-upload"></i> |
||||
|
<div class="el-upload__text">将文件拖到此处,或<em>点击上传</em>,或<em style="color: #67C23A;">Ctrl+V 粘贴图片</em></div> |
||||
|
<div class="el-upload__tip" slot="tip"> |
||||
|
支持pdf、dwg、dxf、doc、docx、xls、xlsx、jpg、png格式 |
||||
|
</div> |
||||
|
</el-upload> |
||||
|
</div> |
||||
|
|
||||
|
<!-- 备注区域 --> |
||||
|
<div style="margin: 15px 0;"> |
||||
|
<div style="margin-bottom: 10px;">备注:</div> |
||||
|
<el-input |
||||
|
type="textarea" |
||||
|
v-model="ossForm.remark" |
||||
|
resize="none" |
||||
|
:autosize="{minRows: 2, maxRows: 2}"> |
||||
|
</el-input> |
||||
|
</div> |
||||
|
|
||||
|
<span slot="footer" class="dialog-footer"> |
||||
|
<el-button type="primary" :loading="uploadLoading" @click="handleUploadFiles">保存</el-button> |
||||
|
<el-button @click="ossVisible = false">关闭</el-button> |
||||
|
</span> |
||||
|
</el-dialog> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import { ossUpload, queryOss, removeOss, previewOssFileById, previewOssFileById2 } from '@/api/oss/oss' |
||||
|
|
||||
|
export default { |
||||
|
name: 'ErfAttachmentManager', |
||||
|
|
||||
|
props: { |
||||
|
// 申请单号 |
||||
|
applyNo: { |
||||
|
type: String, |
||||
|
required: true |
||||
|
}, |
||||
|
// 是否禁用编辑 |
||||
|
disabled: { |
||||
|
type: Boolean, |
||||
|
default: false |
||||
|
}, |
||||
|
// 表格高度 |
||||
|
height: { |
||||
|
type: [String, Number], |
||||
|
default: '30vh' |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
data() { |
||||
|
return { |
||||
|
dataList: [], |
||||
|
queryLoading: false, |
||||
|
uploadLoading: false, |
||||
|
selectionDataList: [], |
||||
|
ossVisible: false, |
||||
|
ossForm: { |
||||
|
orderRef1: 'ERF', // 模块标识 |
||||
|
orderRef2: '', // 申请单号 |
||||
|
orderRef6: 'EXP_APPLY', // 业务类型标识 |
||||
|
remark: '' |
||||
|
}, |
||||
|
fileList: [], |
||||
|
pasteImageCounter: 0 // 粘贴图片计数器,用于生成文件名 |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
mounted() { |
||||
|
this.handleQuery() |
||||
|
}, |
||||
|
|
||||
|
watch: { |
||||
|
applyNo(newVal) { |
||||
|
if (newVal) { |
||||
|
this.handleQuery() |
||||
|
} |
||||
|
}, |
||||
|
// 监听对话框关闭,移除粘贴事件监听器 |
||||
|
ossVisible(newVal) { |
||||
|
if (!newVal) { |
||||
|
document.removeEventListener('paste', this.handlePaste) |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
beforeDestroy() { |
||||
|
// 组件销毁时移除监听器 |
||||
|
document.removeEventListener('paste', this.handlePaste) |
||||
|
}, |
||||
|
|
||||
|
methods: { |
||||
|
/** |
||||
|
* 查询附件列表 |
||||
|
*/ |
||||
|
handleQuery() { |
||||
|
if (!this.applyNo) { |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
let params = { |
||||
|
orderRef1: 'ERF', |
||||
|
orderRef2: this.applyNo, |
||||
|
orderRef6: 'EXP_APPLY' |
||||
|
} |
||||
|
|
||||
|
this.queryLoading = true |
||||
|
queryOss(params).then(({data}) => { |
||||
|
this.queryLoading = false |
||||
|
if (data && data.code === 0) { |
||||
|
this.dataList = data.rows || [] |
||||
|
} else { |
||||
|
this.dataList = [] |
||||
|
this.$message.warning(data.msg || '查询失败') |
||||
|
} |
||||
|
}).catch(error => { |
||||
|
this.queryLoading = false |
||||
|
this.$message.error('查询异常') |
||||
|
}) |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 打开上传对话框 |
||||
|
*/ |
||||
|
handleUpload() { |
||||
|
this.$nextTick(() => { |
||||
|
if (this.$refs.upload) { |
||||
|
this.$refs.upload.clearFiles() |
||||
|
} |
||||
|
}) |
||||
|
this.fileList = [] |
||||
|
this.ossForm.orderRef2 = this.applyNo |
||||
|
this.ossForm.remark = '' |
||||
|
this.ossVisible = true |
||||
|
this.pasteImageCounter = 0 |
||||
|
|
||||
|
// 添加粘贴事件监听器 |
||||
|
this.$nextTick(() => { |
||||
|
document.addEventListener('paste', this.handlePaste) |
||||
|
}) |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 文件选择变化 |
||||
|
*/ |
||||
|
onRemoveFile(file, fileList) { |
||||
|
this.fileList = fileList |
||||
|
}, |
||||
|
|
||||
|
onChangeFile(file, fileList) { |
||||
|
this.fileList = fileList |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 处理粘贴事件(支持直接粘贴图片) |
||||
|
*/ |
||||
|
handlePaste(event) { |
||||
|
// 只在对话框打开时处理粘贴 |
||||
|
if (!this.ossVisible) { |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
const items = (event.clipboardData || event.originalEvent.clipboardData).items |
||||
|
|
||||
|
for (let i = 0; i < items.length; i++) { |
||||
|
const item = items[i] |
||||
|
|
||||
|
// 检查是否为图片类型 |
||||
|
if (item.type.indexOf('image') !== -1) { |
||||
|
event.preventDefault() // 阻止默认粘贴行为 |
||||
|
|
||||
|
const blob = item.getAsFile() |
||||
|
if (!blob) continue |
||||
|
|
||||
|
// 获取文件扩展名 |
||||
|
const extension = blob.type.split('/')[1] || 'png' |
||||
|
|
||||
|
// 尝试使用原始文件名,如果没有则使用简洁的默认名称 |
||||
|
let fileName = blob.name |
||||
|
if (!fileName || fileName === 'image.png' || fileName === 'blob') { |
||||
|
// 截图或无名称的情况,使用简洁的默认名称 |
||||
|
this.pasteImageCounter++ |
||||
|
fileName = `粘贴图片${this.pasteImageCounter}.${extension}` |
||||
|
} |
||||
|
|
||||
|
// 创建 File 对象(保留原始文件名) |
||||
|
const file = new File([blob], fileName, { type: blob.type }) |
||||
|
|
||||
|
// 创建一个符合 el-upload 格式的文件对象 |
||||
|
const uploadFile = { |
||||
|
name: fileName, |
||||
|
size: file.size, |
||||
|
type: file.type, |
||||
|
raw: file, |
||||
|
uid: Date.now() + this.pasteImageCounter, |
||||
|
status: 'ready' |
||||
|
} |
||||
|
|
||||
|
// 添加到文件列表 |
||||
|
this.fileList.push(uploadFile) |
||||
|
|
||||
|
// 手动触发 el-upload 的文件列表更新 |
||||
|
if (this.$refs.upload) { |
||||
|
this.$refs.upload.uploadFiles.push(uploadFile) |
||||
|
} |
||||
|
|
||||
|
this.$message.success(`已粘贴图片: ${fileName}`) |
||||
|
|
||||
|
break // 只处理第一张图片 |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 执行上传 |
||||
|
*/ |
||||
|
handleUploadFiles() { |
||||
|
if (this.fileList.length === 0) { |
||||
|
this.$message.error('请选择文件') |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
let formData = new FormData() |
||||
|
for (let i = 0; i < this.fileList.length; i++) { |
||||
|
formData.append('file', this.fileList[i].raw) |
||||
|
} |
||||
|
formData.append('orderRef1', 'ERF') |
||||
|
formData.append('orderRef2', this.ossForm.orderRef2) |
||||
|
formData.append('orderRef6', 'EXP_APPLY') |
||||
|
formData.append('createdBy', this.$store.state.user.name) |
||||
|
formData.append('orderRef5', this.ossForm.remark) |
||||
|
|
||||
|
this.uploadLoading = true |
||||
|
ossUpload(formData).then(({data}) => { |
||||
|
this.uploadLoading = false |
||||
|
if (data && data.code === 0) { |
||||
|
this.$message.success('上传成功') |
||||
|
this.handleQuery() |
||||
|
this.ossVisible = false |
||||
|
} else { |
||||
|
this.$message.warning(data.msg || '上传失败') |
||||
|
} |
||||
|
}).catch(error => { |
||||
|
this.uploadLoading = false |
||||
|
this.$message.error('上传异常') |
||||
|
}) |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 删除附件 |
||||
|
*/ |
||||
|
handleRemove(row) { |
||||
|
this.$confirm('确认删除该附件吗?', '提示', { |
||||
|
confirmButtonText: '确定', |
||||
|
cancelButtonText: '取消', |
||||
|
type: 'warning' |
||||
|
}).then(() => { |
||||
|
let ids = [row.id] |
||||
|
removeOss(ids).then(({data}) => { |
||||
|
if (data && data.code === 0) { |
||||
|
this.$message.success('删除成功') |
||||
|
this.handleQuery() |
||||
|
} else { |
||||
|
this.$message.warning(data.msg || '删除失败') |
||||
|
} |
||||
|
}).catch(error => { |
||||
|
this.$message.error('删除异常') |
||||
|
}) |
||||
|
}).catch(() => {}) |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 预览文件 |
||||
|
*/ |
||||
|
previewFile(row) { |
||||
|
let type = '' |
||||
|
let fileType = row.fileType.toLowerCase() |
||||
|
|
||||
|
// 图片类型 |
||||
|
let image = ['jpg', 'jpeg', 'png', 'gif', 'bmp'] |
||||
|
if (image.includes(fileType)) { |
||||
|
type = 'image/' + fileType |
||||
|
} |
||||
|
|
||||
|
// 视频类型 |
||||
|
let video = ['mp4', 'avi', 'mov', 'wmv', 'flv'] |
||||
|
if (video.includes(fileType)) { |
||||
|
type = 'video/' + fileType |
||||
|
} |
||||
|
|
||||
|
// 文本类型 |
||||
|
if (fileType === 'txt') { |
||||
|
type = 'text/plain;charset=utf-8' |
||||
|
} |
||||
|
|
||||
|
// Excel类型 |
||||
|
if (fileType === 'xlsx' || fileType === 'xls') { |
||||
|
type = 'excel' |
||||
|
} |
||||
|
|
||||
|
// Word类型 |
||||
|
if (fileType === 'docx') { |
||||
|
type = 'word' |
||||
|
} |
||||
|
|
||||
|
// PDF类型 |
||||
|
if (fileType === 'pdf') { |
||||
|
type = 'application/pdf;charset-UTF-8' |
||||
|
} |
||||
|
|
||||
|
// Office文件不支持预览 |
||||
|
if (fileType === 'doc' || fileType === 'ppt' || fileType === 'pptx') { |
||||
|
this.$message.warning('该文件格式暂不支持预览,请下载后查看') |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
// CAD文件不支持预览 |
||||
|
if (fileType === 'dwg' || fileType === 'dxf') { |
||||
|
this.$message.warning('CAD文件暂不支持预览,请下载后使用CAD软件查看') |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
if (type === '') { |
||||
|
this.$message.warning('该文件格式暂不支持预览') |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
let params = { |
||||
|
id: row.id, |
||||
|
fileType: type |
||||
|
} |
||||
|
|
||||
|
previewOssFileById2(params).then(({data}) => { |
||||
|
if (type === 'excel' || type === 'word') { |
||||
|
type = 'application/pdf;charset=UTF-8' |
||||
|
} |
||||
|
const blob = new Blob([data], { type: type }) |
||||
|
const fileURL = URL.createObjectURL(blob) |
||||
|
|
||||
|
if (type === 'xls' || type === 'docx' || type === 'xlsx') { |
||||
|
const { href } = this.$router.resolve({ |
||||
|
name: 'pre', |
||||
|
query: { |
||||
|
src: fileURL, |
||||
|
type: 'pdf' |
||||
|
} |
||||
|
}) |
||||
|
window.open(href, '_blank') |
||||
|
} else { |
||||
|
// 在新标签页中打开文件预览 |
||||
|
window.open(fileURL, '_blank') |
||||
|
} |
||||
|
}) |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 选择变化 |
||||
|
*/ |
||||
|
handleSelectionChange(val) { |
||||
|
this.selectionDataList = val |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 下载选中的附件 |
||||
|
*/ |
||||
|
handleDownload() { |
||||
|
if (this.selectionDataList.length === 0) { |
||||
|
this.$message.warning('请选择要下载的附件') |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
let selectList = this.selectionDataList.map(item => ({ ...item })) |
||||
|
for (let i = 0; i < selectList.length; i++) { |
||||
|
let params = { |
||||
|
id: selectList[i].id |
||||
|
} |
||||
|
previewOssFileById(params).then((response) => { |
||||
|
const blob = new Blob([response.data], { type: response.headers['content-type'] }) |
||||
|
const link = document.createElement('a') |
||||
|
link.href = URL.createObjectURL(blob) |
||||
|
link.setAttribute('download', selectList[i].fileName) |
||||
|
link.target = '_blank' |
||||
|
link.click() |
||||
|
URL.revokeObjectURL(link.href) |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
this.$refs.table.clearSelection() |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style scoped> |
||||
|
.erf-attachment-manager { |
||||
|
|
||||
|
} |
||||
|
/* 表格样式优化 */ |
||||
|
.el-table >>> .el-table__header th { |
||||
|
background-color: #F5F7FA; |
||||
|
color: #606266; |
||||
|
font-weight: bold; |
||||
|
} |
||||
|
|
||||
|
.el-table >>> .el-table__row td { |
||||
|
padding: 8px 0; |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,354 @@ |
|||||
|
<template> |
||||
|
<div class="exp-apply-form"> |
||||
|
<el-form |
||||
|
:model="saveHeaderData" |
||||
|
:rules="formRules" |
||||
|
ref="applyForm" |
||||
|
label-position="top" |
||||
|
style="margin-left: 5px; margin-top: -5px;"> |
||||
|
|
||||
|
<el-row :gutter="20"> |
||||
|
<el-col :span="6"> |
||||
|
<el-form-item label="事业部" prop="buNo"> |
||||
|
<el-select |
||||
|
v-model="saveHeaderData.buNo" |
||||
|
:disabled="readonly || isEdit" |
||||
|
placeholder="请选择事业部" |
||||
|
style="width: 100%"> |
||||
|
<el-option |
||||
|
v-for="i in buList" |
||||
|
:key="i.buNo" |
||||
|
:label="i.buDesc" |
||||
|
:value="i.buNo"> |
||||
|
</el-option> |
||||
|
</el-select> |
||||
|
</el-form-item> |
||||
|
</el-col> |
||||
|
|
||||
|
<el-col :span="6"> |
||||
|
<el-form-item label="试验类型" prop="experimentType"> |
||||
|
<el-select |
||||
|
v-model="saveHeaderData.experimentType" |
||||
|
:disabled="readonly" |
||||
|
placeholder="请选择试验类型" |
||||
|
style="width: 100%"> |
||||
|
<el-option label="High Risk" value="High Risk"></el-option> |
||||
|
<el-option label="Low Risk" value="Low Risk"></el-option> |
||||
|
</el-select> |
||||
|
</el-form-item> |
||||
|
</el-col> |
||||
|
|
||||
|
<el-col :span="6"> |
||||
|
<el-form-item label="项目编号" prop="projectNo"> |
||||
|
<el-input |
||||
|
v-model="saveHeaderData.projectNo" |
||||
|
:readonly="readonly" |
||||
|
placeholder="请输入项目编号"> |
||||
|
</el-input> |
||||
|
</el-form-item> |
||||
|
</el-col> |
||||
|
|
||||
|
<el-col :span="6"> |
||||
|
<el-form-item label="期望完成日期" prop="expectedFinishDate"> |
||||
|
<el-date-picker |
||||
|
v-model="saveHeaderData.expectedFinishDate" |
||||
|
:disabled="readonly" |
||||
|
type="date" |
||||
|
format="yyyy-MM-dd" |
||||
|
value-format="yyyy-MM-dd" |
||||
|
placeholder="选择日期" |
||||
|
style="width: 100%"> |
||||
|
</el-date-picker> |
||||
|
</el-form-item> |
||||
|
</el-col> |
||||
|
</el-row> |
||||
|
|
||||
|
<el-row :gutter="20"> |
||||
|
<el-col :span="24"> |
||||
|
<el-form-item label="试验名称" prop="title"> |
||||
|
<el-input |
||||
|
v-model="saveHeaderData.title" |
||||
|
:readonly="readonly" |
||||
|
placeholder="请输入试验名称"> |
||||
|
</el-input> |
||||
|
</el-form-item> |
||||
|
</el-col> |
||||
|
</el-row> |
||||
|
|
||||
|
<el-row :gutter="20"> |
||||
|
<el-col :span="24"> |
||||
|
<el-form-item label="试验目的" prop="purpose"> |
||||
|
<el-input |
||||
|
v-model="saveHeaderData.purpose" |
||||
|
:readonly="readonly" |
||||
|
placeholder="请输入试验目的"> |
||||
|
</el-input> |
||||
|
</el-form-item> |
||||
|
</el-col> |
||||
|
</el-row> |
||||
|
|
||||
|
<el-row :gutter="20"> |
||||
|
<el-col :span="24"> |
||||
|
<el-form-item label="验证方法及判断标准" prop="justification"> |
||||
|
<el-input |
||||
|
v-model="saveHeaderData.justification" |
||||
|
:readonly="readonly" |
||||
|
placeholder="请输入验证方法及判断标准"> |
||||
|
</el-input> |
||||
|
</el-form-item> |
||||
|
</el-col> |
||||
|
</el-row> |
||||
|
|
||||
|
<el-row :gutter="20"> |
||||
|
<el-col :span="12"> |
||||
|
<el-form-item label="试验产品型号"> |
||||
|
<el-input |
||||
|
v-model="saveHeaderData.productType" |
||||
|
:readonly="readonly" |
||||
|
placeholder="请输入产品型号"> |
||||
|
</el-input> |
||||
|
</el-form-item> |
||||
|
</el-col> |
||||
|
|
||||
|
<el-col :span="12"> |
||||
|
<el-form-item label="申请数量"> |
||||
|
<el-input |
||||
|
v-model="saveHeaderData.quantityReq" |
||||
|
:readonly="readonly" |
||||
|
placeholder="请输入申请数量描述"> |
||||
|
</el-input> |
||||
|
</el-form-item> |
||||
|
</el-col> |
||||
|
</el-row> |
||||
|
|
||||
|
<el-row :gutter="20"> |
||||
|
<el-col :span="12"> |
||||
|
<el-form-item label="试验负责人"> |
||||
|
<el-input |
||||
|
v-model="saveHeaderData.projectLeader" |
||||
|
:readonly="readonly" |
||||
|
placeholder="请输入试验负责人"> |
||||
|
</el-input> |
||||
|
</el-form-item> |
||||
|
</el-col> |
||||
|
|
||||
|
<el-col :span="12"> |
||||
|
<el-form-item label="联系方式"> |
||||
|
<el-input |
||||
|
v-model="saveHeaderData.contactMethod" |
||||
|
:readonly="readonly" |
||||
|
placeholder="请输入联系方式"> |
||||
|
</el-input> |
||||
|
</el-form-item> |
||||
|
</el-col> |
||||
|
</el-row> |
||||
|
|
||||
|
<el-row :gutter="20"> |
||||
|
<el-col :span="24"> |
||||
|
<el-form-item label="工艺、取样、测试要求"> |
||||
|
<el-input |
||||
|
v-model="saveHeaderData.processRequirement" |
||||
|
:readonly="readonly" |
||||
|
type="textarea" |
||||
|
:rows="3" |
||||
|
placeholder="请输入工艺、取样、测试要求"> |
||||
|
</el-input> |
||||
|
</el-form-item> |
||||
|
</el-col> |
||||
|
</el-row> |
||||
|
|
||||
|
<!-- 只读模式下显示的附加信息 --> |
||||
|
<template v-if="readonly && saveHeaderData.applyNo"> |
||||
|
<el-divider content-position="left">申请单信息</el-divider> |
||||
|
|
||||
|
<el-row :gutter="20"> |
||||
|
<el-col :span="8"> |
||||
|
<el-form-item label="申请单号"> |
||||
|
<span>{{ saveHeaderData.applyNo }}</span> |
||||
|
</el-form-item> |
||||
|
</el-col> |
||||
|
|
||||
|
<el-col :span="8"> |
||||
|
<el-form-item label="状态"> |
||||
|
<el-tag :type="getStatusType(saveHeaderData.status)"> |
||||
|
{{ getStatusText(saveHeaderData.status) }} |
||||
|
</el-tag> |
||||
|
</el-form-item> |
||||
|
</el-col> |
||||
|
|
||||
|
<el-col :span="8"> |
||||
|
<el-form-item label="创建人"> |
||||
|
<span>{{ saveHeaderData.creatorName }}</span> |
||||
|
</el-form-item> |
||||
|
</el-col> |
||||
|
</el-row> |
||||
|
|
||||
|
<el-row :gutter="20"> |
||||
|
<el-col :span="12"> |
||||
|
<el-form-item label="创建时间"> |
||||
|
<span>{{ saveHeaderData.createTime }}</span> |
||||
|
</el-form-item> |
||||
|
</el-col> |
||||
|
|
||||
|
<el-col :span="12"> |
||||
|
<el-form-item label="更新时间"> |
||||
|
<span>{{ saveHeaderData.updateTime }}</span> |
||||
|
</el-form-item> |
||||
|
</el-col> |
||||
|
</el-row> |
||||
|
</template> |
||||
|
</el-form> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import { saveExpApply } from '@/api/erf/erf' |
||||
|
import { getBuList } from '@/api/factory/site' |
||||
|
|
||||
|
export default { |
||||
|
name: 'ExpApplyForm', |
||||
|
|
||||
|
components: {}, |
||||
|
|
||||
|
props: { |
||||
|
applyData: { |
||||
|
type: Object, |
||||
|
default: () => ({}) |
||||
|
}, |
||||
|
readonly: { |
||||
|
type: Boolean, |
||||
|
default: false |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
data() { |
||||
|
return { |
||||
|
buList: [], |
||||
|
saveHeaderData: { |
||||
|
applyNo: '', |
||||
|
buNo: '', |
||||
|
experimentType: '', |
||||
|
projectNo: '', |
||||
|
title: '', |
||||
|
purpose: '', |
||||
|
justification: '', |
||||
|
productType: '', |
||||
|
quantityReq: '', |
||||
|
expectedFinishDate: '', |
||||
|
projectLeader: '', |
||||
|
contactMethod: '', |
||||
|
processRequirement: '', |
||||
|
creatorUserId: this.$store.state.user.id, |
||||
|
creatorName: this.$store.state.user.name |
||||
|
}, |
||||
|
|
||||
|
formRules: { |
||||
|
buNo: [ |
||||
|
{ required: true, message: '请选择事业部', trigger: 'change' } |
||||
|
], |
||||
|
experimentType: [ |
||||
|
{ required: true, message: '请选择试验类型', trigger: 'change' } |
||||
|
], |
||||
|
title: [ |
||||
|
{ required: true, message: '请输入试验名称', trigger: 'blur' } |
||||
|
], |
||||
|
expectedFinishDate: [ |
||||
|
{ required: true, message: '请选择期望完成日期', trigger: 'change' } |
||||
|
] |
||||
|
}, |
||||
|
|
||||
|
isEdit: false |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
mounted() { |
||||
|
this.loadBuList() |
||||
|
if (this.applyData && this.applyData.applyNo) { |
||||
|
this.saveHeaderData = { ...this.applyData } |
||||
|
this.isEdit = true |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
methods: { |
||||
|
/** |
||||
|
* 加载事业部列表 |
||||
|
*/ |
||||
|
loadBuList() { |
||||
|
const tempData = { site: this.$store.state.user.site } |
||||
|
getBuList(tempData).then(({data}) => { |
||||
|
if (data.code === 0) { |
||||
|
this.buList = data.row1 |
||||
|
if (data.row1.length === 1) { |
||||
|
this.saveHeaderData.buNo = data.row1[0].buNo |
||||
|
} |
||||
|
} |
||||
|
}) |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 获取表单数据(用于验证) |
||||
|
*/ |
||||
|
getFormData() { |
||||
|
let valid = false |
||||
|
this.$refs.applyForm.validate(v => { |
||||
|
valid = v |
||||
|
}) |
||||
|
return valid ? this.saveHeaderData : null |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 保存表单 |
||||
|
*/ |
||||
|
save() { |
||||
|
return new Promise((resolve, reject) => { |
||||
|
this.$refs.applyForm.validate(valid => { |
||||
|
if (!valid) { |
||||
|
reject('表单验证失败') |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
saveExpApply(this.saveHeaderData).then(({data}) => { |
||||
|
if (data && data.code === 0) { |
||||
|
resolve(data) |
||||
|
} else { |
||||
|
this.$message.error(data.msg || '保存失败') |
||||
|
reject(data.msg) |
||||
|
} |
||||
|
}).catch(error => { |
||||
|
reject(error) |
||||
|
}) |
||||
|
}) |
||||
|
}) |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 获取状态类型 |
||||
|
*/ |
||||
|
getStatusType(status) { |
||||
|
const types = { |
||||
|
'草稿': 'info', |
||||
|
'已提交': 'warning', |
||||
|
'已批准': 'success', |
||||
|
'生产中': '', |
||||
|
'已完成': 'success', |
||||
|
'已取消': 'danger', |
||||
|
'已驳回': 'danger' |
||||
|
} |
||||
|
return types[status] || 'info' |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 获取状态文本(直接返回中文状态) |
||||
|
*/ |
||||
|
getStatusText(status) { |
||||
|
return status || '' |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style scoped> |
||||
|
.exp-apply-form { |
||||
|
padding: 10px; |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,344 @@ |
|||||
|
<template> |
||||
|
<div class="exp-project-detail"> |
||||
|
<!-- 顶部操作按钮 --> |
||||
|
<div class="toolbar"> |
||||
|
<el-button |
||||
|
v-if="!editing && (status==='草稿' || status==='已驳回')" |
||||
|
@click="handleEdit" |
||||
|
type="primary" |
||||
|
size="small"> |
||||
|
编辑 |
||||
|
</el-button> |
||||
|
<el-button |
||||
|
v-if="editing" |
||||
|
@click="handleSave" |
||||
|
type="primary" |
||||
|
size="small" |
||||
|
:loading="saving"> |
||||
|
保存 |
||||
|
</el-button> |
||||
|
<el-button |
||||
|
v-if="editing" |
||||
|
@click="handleCancel" |
||||
|
size="small"> |
||||
|
取消 |
||||
|
</el-button> |
||||
|
</div> |
||||
|
|
||||
|
<!-- 项目详情表单 - label在上,input在下 --> |
||||
|
<el-form |
||||
|
:model="formData" |
||||
|
ref="detailForm" |
||||
|
label-position="top" |
||||
|
v-loading="loading"> |
||||
|
|
||||
|
<el-row :gutter="20"> |
||||
|
<!-- 左侧列 --> |
||||
|
<el-col :span="10"> |
||||
|
<el-form-item label="试验名称(Name of the experiment)"> |
||||
|
<el-input |
||||
|
v-model="formData.title" |
||||
|
:readonly="!editing" |
||||
|
placeholder="请输入试验名称"> |
||||
|
</el-input> |
||||
|
</el-form-item> |
||||
|
|
||||
|
<el-form-item label="试验目的(Purpose of the experiment)"> |
||||
|
<el-input |
||||
|
v-model="formData.purpose" |
||||
|
:readonly="!editing" |
||||
|
placeholder="请输入试验目的"> |
||||
|
</el-input> |
||||
|
</el-form-item> |
||||
|
|
||||
|
<el-form-item label="验证方法及判断标准(Experiment Justification)"> |
||||
|
<el-input |
||||
|
v-model="formData.justification" |
||||
|
:readonly="!editing" |
||||
|
placeholder="请输入验证方法及判断标准"> |
||||
|
</el-input> |
||||
|
</el-form-item> |
||||
|
|
||||
|
<el-form-item label="试验产品型号(Products'type)"> |
||||
|
<el-input |
||||
|
v-model="formData.productType" |
||||
|
:readonly="!editing" |
||||
|
placeholder="请输入试验产品型号"> |
||||
|
</el-input> |
||||
|
</el-form-item> |
||||
|
|
||||
|
<el-form-item label="期望完成日期(Desired finish date)"> |
||||
|
<el-date-picker |
||||
|
v-model="formData.expectedFinishDate" |
||||
|
:disabled="!editing" |
||||
|
type="date" |
||||
|
format="yyyy/MM/dd" |
||||
|
value-format="yyyy-MM-dd" |
||||
|
placeholder="选择日期" |
||||
|
style="width: 100%"> |
||||
|
</el-date-picker> |
||||
|
</el-form-item> |
||||
|
</el-col> |
||||
|
|
||||
|
<!-- 右侧大文本框 --> |
||||
|
<el-col :span="14"> |
||||
|
<el-form-item label="试验申请数量(Quantity requirement)"> |
||||
|
<el-input |
||||
|
v-model="formData.quantityReq" |
||||
|
type="textarea" |
||||
|
:readonly="!editing" |
||||
|
:rows="2" |
||||
|
placeholder="请输入试验申请数量"> |
||||
|
</el-input> |
||||
|
</el-form-item> |
||||
|
<el-form-item style="margin-top: 35px" label="工艺、取样、测试要求(PartIII Process,Sampling,and Test Requirement)"> |
||||
|
<el-input |
||||
|
v-model="formData.processRequirement" |
||||
|
type="textarea" |
||||
|
:readonly="!editing" |
||||
|
:rows="9" |
||||
|
placeholder="请输入工艺、取样、测试要求"> |
||||
|
</el-input> |
||||
|
</el-form-item> |
||||
|
</el-col> |
||||
|
</el-row> |
||||
|
|
||||
|
<!-- 底部:试验负责人信息 --> |
||||
|
<el-row :gutter="20"> |
||||
|
<el-col :span="10"> |
||||
|
<el-form-item label="试验负责人(Project leader)"> |
||||
|
<el-input |
||||
|
v-model="formData.projectLeader" |
||||
|
:readonly="!editing" |
||||
|
placeholder="请输入试验负责人"> |
||||
|
</el-input> |
||||
|
</el-form-item> |
||||
|
</el-col> |
||||
|
|
||||
|
<el-col :span="14"> |
||||
|
<el-form-item label="联系方式(Contact method)"> |
||||
|
<el-input |
||||
|
v-model="formData.contactMethod" |
||||
|
:readonly="!editing" |
||||
|
placeholder="请输入联系方式"> |
||||
|
</el-input> |
||||
|
</el-form-item> |
||||
|
</el-col> |
||||
|
</el-row> |
||||
|
</el-form> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import { getExpApplyDetail, saveExpApply } from '@/api/erf/erf' |
||||
|
|
||||
|
export default { |
||||
|
name: 'ExpProjectDetail', |
||||
|
|
||||
|
props: { |
||||
|
applyNo: { |
||||
|
type: String, |
||||
|
required: true |
||||
|
}, |
||||
|
status: { |
||||
|
type: String, |
||||
|
required: true |
||||
|
}, |
||||
|
height: { |
||||
|
type: String, |
||||
|
default: '35vh' |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
data() { |
||||
|
return { |
||||
|
formData: { |
||||
|
applyNo: '', |
||||
|
buNo: '', |
||||
|
experimentType: '', |
||||
|
projectNo: '', |
||||
|
title: '', |
||||
|
purpose: '', |
||||
|
justification: '', |
||||
|
productType: '', |
||||
|
quantityReq: '', |
||||
|
expectedFinishDate: '', |
||||
|
projectLeader: '', |
||||
|
contactMethod: '', |
||||
|
processRequirement: '', |
||||
|
status: '', |
||||
|
creatorUserId: '', |
||||
|
creatorName: '', |
||||
|
createTime: '', |
||||
|
updateTime: '', |
||||
|
plannerUserId: '', |
||||
|
plannerName: '', |
||||
|
workOrderNo: '', |
||||
|
scheduledDate: '' |
||||
|
}, |
||||
|
originalData: {}, // 保存原始数据,用于取消时恢复 |
||||
|
loading: false, |
||||
|
editing: false, |
||||
|
saving: false |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
watch: { |
||||
|
applyNo: { |
||||
|
immediate: true, |
||||
|
handler(val) { |
||||
|
if (val) { |
||||
|
this.editing = false |
||||
|
this.loadDetail() |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
methods: { |
||||
|
/** |
||||
|
* 加载项目详情 |
||||
|
*/ |
||||
|
loadDetail() { |
||||
|
this.loading = true |
||||
|
getExpApplyDetail({ applyNo: this.applyNo }).then(({data}) => { |
||||
|
this.loading = false |
||||
|
if (data && data.code === 0) { |
||||
|
this.formData = { ...this.formData, ...data.data } |
||||
|
this.originalData = JSON.parse(JSON.stringify(this.formData)) |
||||
|
} else { |
||||
|
this.$message.error(data.msg || '加载详情失败') |
||||
|
} |
||||
|
}).catch(error => { |
||||
|
this.loading = false |
||||
|
this.$message.error('加载详情异常') |
||||
|
}) |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 进入编辑模式 |
||||
|
*/ |
||||
|
handleEdit() { |
||||
|
this.editing = true |
||||
|
this.originalData = JSON.parse(JSON.stringify(this.formData)) |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 保存修改 |
||||
|
*/ |
||||
|
handleSave() { |
||||
|
// 数据验证 |
||||
|
if (!this.formData.title) { |
||||
|
this.$message.warning('请输入试验名称') |
||||
|
return |
||||
|
} |
||||
|
if (!this.formData.expectedFinishDate) { |
||||
|
this.$message.warning('请选择期望完成日期') |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
this.saving = true |
||||
|
|
||||
|
// 准备保存数据 |
||||
|
const saveData = { |
||||
|
applyNo: this.formData.applyNo, |
||||
|
buNo: this.formData.buNo, |
||||
|
experimentType: this.formData.experimentType, |
||||
|
projectNo: this.formData.projectNo, |
||||
|
title: this.formData.title, |
||||
|
purpose: this.formData.purpose, |
||||
|
justification: this.formData.justification, |
||||
|
productType: this.formData.productType, |
||||
|
quantityReq: this.formData.quantityReq, |
||||
|
expectedFinishDate: this.formData.expectedFinishDate, |
||||
|
projectLeader: this.formData.projectLeader, |
||||
|
contactMethod: this.formData.contactMethod, |
||||
|
processRequirement: this.formData.processRequirement |
||||
|
} |
||||
|
|
||||
|
saveExpApply(saveData).then(({data}) => { |
||||
|
this.saving = false |
||||
|
if (data && data.code === 0) { |
||||
|
this.$message.success('保存成功') |
||||
|
this.editing = false |
||||
|
this.loadDetail() |
||||
|
// 通知父组件刷新列表 |
||||
|
this.$emit('refresh') |
||||
|
} else { |
||||
|
this.$message.error(data.msg || '保存失败') |
||||
|
} |
||||
|
}).catch(error => { |
||||
|
this.saving = false |
||||
|
this.$message.error('保存异常') |
||||
|
}) |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 取消编辑 |
||||
|
*/ |
||||
|
handleCancel() { |
||||
|
this.$confirm('取消编辑将放弃所有修改,确定取消?', '操作提示', { |
||||
|
confirmButtonText: '确定', |
||||
|
cancelButtonText: '取消', |
||||
|
type: 'warning' |
||||
|
}).then(() => { |
||||
|
this.formData = JSON.parse(JSON.stringify(this.originalData)) |
||||
|
this.editing = false |
||||
|
}).catch(() => { |
||||
|
// 用户点击了取消 |
||||
|
}) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style scoped> |
||||
|
.exp-project-detail { |
||||
|
height: 100%; |
||||
|
overflow-y: auto; |
||||
|
overflow-x: hidden; |
||||
|
background-color: #fff; |
||||
|
} |
||||
|
|
||||
|
.toolbar { |
||||
|
margin-bottom: 5px; |
||||
|
padding-bottom: 5px; |
||||
|
border-bottom: 1px solid #EBEEF5; |
||||
|
text-align: left; |
||||
|
} |
||||
|
|
||||
|
.el-form-item { |
||||
|
margin-bottom: 15px; |
||||
|
} |
||||
|
|
||||
|
/* 只读状态的input样式 */ |
||||
|
.el-input >>> input[readonly] { |
||||
|
background-color: #F5F7FA; |
||||
|
border-color: #DCDFE6; |
||||
|
color: #606266; |
||||
|
cursor: default; |
||||
|
} |
||||
|
|
||||
|
.el-input >>> textarea[readonly] { |
||||
|
background-color: #F5F7FA; |
||||
|
border-color: #DCDFE6; |
||||
|
color: #606266; |
||||
|
cursor: default; |
||||
|
} |
||||
|
|
||||
|
/* 禁用状态的日期选择器样式 */ |
||||
|
.el-date-editor.is-disabled >>> .el-input__inner { |
||||
|
background-color: #F5F7FA; |
||||
|
border-color: #DCDFE6; |
||||
|
color: #606266; |
||||
|
cursor: default; |
||||
|
} |
||||
|
|
||||
|
/* 调整label样式 */ |
||||
|
.el-form-item >>> .el-form-item__label { |
||||
|
font-size: 13px; |
||||
|
color: #606266; |
||||
|
font-weight: normal; |
||||
|
padding-bottom: 5px; |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,569 @@ |
|||||
|
<template> |
||||
|
<div class="exp-tri-confirm"> |
||||
|
<!-- 操作按钮 --> |
||||
|
<div class="toolbar"> |
||||
|
<el-button @click="openAddDialog" type="primary" size="small">新增</el-button> |
||||
|
</div> |
||||
|
|
||||
|
<!-- 三方确认表格 --> |
||||
|
<el-table |
||||
|
:data="processList" |
||||
|
v-loading="loading" |
||||
|
border |
||||
|
:height="height" |
||||
|
style="width: 100%;"> |
||||
|
|
||||
|
<el-table-column |
||||
|
label="工序顺序" |
||||
|
width="100" |
||||
|
align="center" |
||||
|
header-align="center"> |
||||
|
<template slot-scope="scope"> |
||||
|
<el-tag :type="getSeqTagType(scope.row)" size="small"> |
||||
|
第{{ scope.row.processSeq }}道 |
||||
|
</el-tag> |
||||
|
</template> |
||||
|
</el-table-column> |
||||
|
|
||||
|
<el-table-column |
||||
|
prop="processStep" |
||||
|
label="工序名称" |
||||
|
width="120" |
||||
|
align="center" |
||||
|
header-align="center"> |
||||
|
</el-table-column> |
||||
|
|
||||
|
<el-table-column |
||||
|
prop="prodApproverName" |
||||
|
label="车间负责人" |
||||
|
width="150" |
||||
|
align="center" |
||||
|
header-align="center"> |
||||
|
<template slot-scope="scope"> |
||||
|
<span v-if="scope.row.prodApproverName" style="color: #67C23A;"> |
||||
|
✓ {{ scope.row.prodApproverName }} |
||||
|
</span> |
||||
|
<span v-else-if="canConfirmProcess(scope.row)" style="color: #E6A23C;"> |
||||
|
待确认 |
||||
|
</span> |
||||
|
<span v-else style="color: #909399;">-</span> |
||||
|
</template> |
||||
|
</el-table-column> |
||||
|
|
||||
|
<el-table-column |
||||
|
prop="qaApproverName" |
||||
|
label="质量负责人" |
||||
|
width="150" |
||||
|
align="center" |
||||
|
header-align="center"> |
||||
|
<template slot-scope="scope"> |
||||
|
<span v-if="scope.row.qaApproverName" style="color: #67C23A;"> |
||||
|
✓ {{ scope.row.qaApproverName }} |
||||
|
</span> |
||||
|
<span v-else-if="canConfirmProcess(scope.row)" style="color: #E6A23C;"> |
||||
|
待确认 |
||||
|
</span> |
||||
|
<span v-else style="color: #909399;">-</span> |
||||
|
</template> |
||||
|
</el-table-column> |
||||
|
|
||||
|
<el-table-column |
||||
|
prop="techApproverName" |
||||
|
label="技术负责人" |
||||
|
width="150" |
||||
|
align="center" |
||||
|
header-align="center"> |
||||
|
<template slot-scope="scope"> |
||||
|
<span v-if="scope.row.techApproverName" style="color: #67C23A;"> |
||||
|
✓ {{ scope.row.techApproverName }} |
||||
|
</span> |
||||
|
<span v-else-if="canConfirmProcess(scope.row)" style="color: #E6A23C;"> |
||||
|
待确认 |
||||
|
</span> |
||||
|
<span v-else style="color: #909399;">-</span> |
||||
|
</template> |
||||
|
</el-table-column> |
||||
|
|
||||
|
<el-table-column |
||||
|
prop="confirmStatus" |
||||
|
label="确认状态" |
||||
|
width="120" |
||||
|
align="center" |
||||
|
header-align="center"> |
||||
|
<template slot-scope="scope"> |
||||
|
<el-tag v-if="scope.row.confirmStatus === 'CONFIRMED'" type="success" size="small"> |
||||
|
已完成 |
||||
|
</el-tag> |
||||
|
<el-tag v-else-if="canConfirmProcess(scope.row)" type="warning" size="small"> |
||||
|
可确认 |
||||
|
</el-tag> |
||||
|
<el-tag v-else type="info" size="small"> |
||||
|
等待中 |
||||
|
</el-tag> |
||||
|
</template> |
||||
|
</el-table-column> |
||||
|
|
||||
|
<el-table-column |
||||
|
prop="remark" |
||||
|
label="备注" |
||||
|
min-width="150" |
||||
|
align="left" |
||||
|
header-align="center" |
||||
|
show-overflow-tooltip> |
||||
|
<template slot-scope="scope"> |
||||
|
<span>{{ scope.row.remark || '' }}</span> |
||||
|
</template> |
||||
|
</el-table-column> |
||||
|
|
||||
|
<el-table-column |
||||
|
label="操作" |
||||
|
width="120" |
||||
|
align="center" |
||||
|
header-align="center" |
||||
|
fixed="right"> |
||||
|
<template slot-scope="scope"> |
||||
|
<a |
||||
|
type="text" |
||||
|
size="small" |
||||
|
@click="openEditDialog(scope.row)"> |
||||
|
修改 |
||||
|
</a> |
||||
|
<a |
||||
|
type="text" |
||||
|
size="small" |
||||
|
style="color: #F56C6C" |
||||
|
@click="deleteProcessRow(scope.row)"> |
||||
|
删除 |
||||
|
</a> |
||||
|
</template> |
||||
|
</el-table-column> |
||||
|
</el-table> |
||||
|
|
||||
|
<!-- 新增/修改工序弹框 --> |
||||
|
<el-dialog |
||||
|
:title="processDialogTitle" |
||||
|
:visible.sync="processDialogVisible" |
||||
|
width="400px" |
||||
|
:close-on-click-modal="false"> |
||||
|
|
||||
|
<el-form |
||||
|
label-position="top" |
||||
|
:model="processForm" |
||||
|
ref="processForm" |
||||
|
label-width="100px"> |
||||
|
|
||||
|
<el-form-item label="工序" required> |
||||
|
<el-input |
||||
|
v-model="processForm.processStep" |
||||
|
:readonly="isEditMode" |
||||
|
placeholder="请输入工序名称"> |
||||
|
</el-input> |
||||
|
</el-form-item> |
||||
|
|
||||
|
<el-form-item label="车间负责人"> |
||||
|
<el-input |
||||
|
v-model="processForm.prodApproverName" |
||||
|
placeholder="请输入车间负责人"> |
||||
|
</el-input> |
||||
|
</el-form-item> |
||||
|
|
||||
|
<el-form-item label="质量负责人"> |
||||
|
<el-input |
||||
|
v-model="processForm.qaApproverName" |
||||
|
placeholder="请输入质量负责人"> |
||||
|
</el-input> |
||||
|
</el-form-item> |
||||
|
|
||||
|
<el-form-item label="技术负责人"> |
||||
|
<el-input |
||||
|
v-model="processForm.techApproverName" |
||||
|
placeholder="请输入技术负责人"> |
||||
|
</el-input> |
||||
|
</el-form-item> |
||||
|
|
||||
|
<el-form-item label="备注"> |
||||
|
<el-input |
||||
|
v-model="processForm.remark" |
||||
|
type="textarea" |
||||
|
:rows="2" |
||||
|
placeholder="请输入备注"> |
||||
|
</el-input> |
||||
|
</el-form-item> |
||||
|
</el-form> |
||||
|
|
||||
|
<div slot="footer" style="margin-top: 40px"> |
||||
|
<el-button |
||||
|
type="primary" |
||||
|
@click="saveProcess" |
||||
|
:loading="processSaving"> |
||||
|
确认 |
||||
|
</el-button> |
||||
|
<el-button @click="processDialogVisible = false">关闭</el-button> |
||||
|
</div> |
||||
|
</el-dialog> |
||||
|
|
||||
|
<!-- 确认对话框 --> |
||||
|
<el-dialog |
||||
|
:title="confirmDialogTitle" |
||||
|
:visible.sync="confirmDialogVisible" |
||||
|
width="450px" |
||||
|
:close-on-click-modal="false"> |
||||
|
|
||||
|
<el-form label-position="top" label-width="120px"> |
||||
|
<el-row :gutter="20"> |
||||
|
<el-col :span="12"> |
||||
|
<el-form-item label="确认人"> |
||||
|
<span>{{ $store.state.user.name }}</span> |
||||
|
</el-form-item> |
||||
|
</el-col> |
||||
|
<el-col :span="12"> |
||||
|
<el-form-item label="确认时间"> |
||||
|
<span>{{ currentTime }}</span> |
||||
|
</el-form-item> |
||||
|
</el-col> |
||||
|
</el-row> |
||||
|
|
||||
|
<el-form-item label="样品是否OK" required> |
||||
|
<el-radio-group v-model="sampleOk"> |
||||
|
<el-radio :label="true"> |
||||
|
<span style="color: #67C23A; font-weight: bold;">✓ OK</span> |
||||
|
</el-radio> |
||||
|
<el-radio :label="false"> |
||||
|
<span style="color: #F56C6C; font-weight: bold;">✗ NG</span> |
||||
|
</el-radio> |
||||
|
</el-radio-group> |
||||
|
<div style="color: #909399; font-size: 12px; margin-top: 5px;"> |
||||
|
请根据实际检查结果选择样品状态 |
||||
|
</div> |
||||
|
</el-form-item> |
||||
|
|
||||
|
<el-form-item label="备注"> |
||||
|
<el-input |
||||
|
v-model="confirmRemark" |
||||
|
type="textarea" |
||||
|
:rows="3" |
||||
|
placeholder="请输入确认备注(选填)"> |
||||
|
</el-input> |
||||
|
</el-form-item> |
||||
|
</el-form> |
||||
|
|
||||
|
<div slot="footer"> |
||||
|
<el-button @click="confirmDialogVisible = false">取消</el-button> |
||||
|
<el-button |
||||
|
type="primary" |
||||
|
@click="doConfirm" |
||||
|
:loading="confirmLoading" |
||||
|
:disabled="sampleOk === null"> |
||||
|
确认 |
||||
|
</el-button> |
||||
|
</div> |
||||
|
</el-dialog> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import { getTriConfirmList, saveTriConfirmProcess, deleteTriConfirmProcess, confirmTriApproval } from '@/api/erf/erf' |
||||
|
|
||||
|
export default { |
||||
|
name: 'ExpTriConfirm', |
||||
|
|
||||
|
props: { |
||||
|
applyNo: { |
||||
|
type: String, |
||||
|
required: true |
||||
|
}, |
||||
|
height: { |
||||
|
type: String, |
||||
|
default: '35vh' |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
data() { |
||||
|
return { |
||||
|
processList: [], |
||||
|
loading: false, |
||||
|
|
||||
|
// 新增/修改工序弹框 |
||||
|
processDialogVisible: false, |
||||
|
processDialogTitle: '', |
||||
|
isEditMode: false, |
||||
|
processSaving: false, |
||||
|
processForm: { |
||||
|
processStep: '', |
||||
|
prodApproverName: '', |
||||
|
qaApproverName: '', |
||||
|
techApproverName: '', |
||||
|
remark: '' |
||||
|
}, |
||||
|
|
||||
|
// 确认对话框 |
||||
|
confirmDialogVisible: false, |
||||
|
confirmDialogTitle: '', |
||||
|
confirmRemark: '', |
||||
|
sampleOk: null, // 样品是否OK |
||||
|
confirmLoading: false, |
||||
|
currentProcess: null, |
||||
|
currentRoleType: '', |
||||
|
currentTime: '' |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
watch: { |
||||
|
applyNo: { |
||||
|
immediate: true, |
||||
|
handler(val) { |
||||
|
if (val) { |
||||
|
this.loadProcessList() |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
methods: { |
||||
|
/** |
||||
|
* 加载工序列表 |
||||
|
*/ |
||||
|
loadProcessList() { |
||||
|
this.loading = true |
||||
|
getTriConfirmList({ applyNo: this.applyNo }).then(({data}) => { |
||||
|
this.loading = false |
||||
|
if (data && data.code === 0) { |
||||
|
// 按工序顺序排序 |
||||
|
this.processList = (data.list || []).sort((a, b) => { |
||||
|
return (a.processSeq || 0) - (b.processSeq || 0) |
||||
|
}) |
||||
|
} else { |
||||
|
this.processList = [] |
||||
|
this.$message.error(data.msg || '加载三方确认列表失败') |
||||
|
} |
||||
|
}).catch(error => { |
||||
|
this.loading = false |
||||
|
this.$message.error('加载三方确认列表异常') |
||||
|
}) |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 获取工序顺序标签颜色 |
||||
|
*/ |
||||
|
getSeqTagType(row) { |
||||
|
if (row.confirmStatus === 'CONFIRMED') { |
||||
|
return 'success' // 已完成:绿色 |
||||
|
} else if (this.canConfirmProcess(row)) { |
||||
|
return 'warning' // 可确认:橙色 |
||||
|
} else { |
||||
|
return 'info' // 等待中:灰色 |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 判断当前工序是否可以确认 |
||||
|
* 规则:第一道工序直接可确认,其他工序需要上一道完成 |
||||
|
*/ |
||||
|
canConfirmProcess(row) { |
||||
|
if (row.confirmStatus === 'CONFIRMED') { |
||||
|
return false // 已完成的不能再确认 |
||||
|
} |
||||
|
|
||||
|
if (row.processSeq === 1) { |
||||
|
return true // 第一道工序可以直接确认 |
||||
|
} |
||||
|
|
||||
|
// 查找上一道工序 |
||||
|
const previousProcess = this.processList.find(p => p.processSeq === row.processSeq - 1) |
||||
|
|
||||
|
// 上一道工序必须已完成 |
||||
|
return previousProcess && previousProcess.confirmStatus === 'CONFIRMED' |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 打开新增弹框 |
||||
|
*/ |
||||
|
openAddDialog() { |
||||
|
this.processDialogTitle = '新增工序确认流程' |
||||
|
this.isEditMode = false |
||||
|
this.processForm = { |
||||
|
processStep: '', |
||||
|
prodApproverName: '', |
||||
|
qaApproverName: '', |
||||
|
techApproverName: '', |
||||
|
remark: '' |
||||
|
} |
||||
|
this.processDialogVisible = true |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 打开修改弹框 |
||||
|
*/ |
||||
|
openEditDialog(row) { |
||||
|
this.processDialogTitle = '修改工序确认流程' |
||||
|
this.isEditMode = true |
||||
|
this.processForm = { |
||||
|
processStep: row.processStep, |
||||
|
prodApproverName: row.prodApproverName || '', |
||||
|
qaApproverName: row.qaApproverName || '', |
||||
|
techApproverName: row.techApproverName || '', |
||||
|
remark: row.remark || '' |
||||
|
} |
||||
|
this.processDialogVisible = true |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 保存工序 |
||||
|
*/ |
||||
|
saveProcess() { |
||||
|
// 验证必填项 |
||||
|
if (!this.processForm.processStep || !this.processForm.processStep.trim()) { |
||||
|
this.$message.warning('请输入工序名称') |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
this.processSaving = true |
||||
|
|
||||
|
saveTriConfirmProcess({ |
||||
|
applyNo: this.applyNo, |
||||
|
processStep: this.processForm.processStep.trim(), |
||||
|
prodApproverName: this.processForm.prodApproverName || '', |
||||
|
qaApproverName: this.processForm.qaApproverName || '', |
||||
|
techApproverName: this.processForm.techApproverName || '', |
||||
|
remark: this.processForm.remark || '' |
||||
|
}).then(({data}) => { |
||||
|
this.processSaving = false |
||||
|
if (data && data.code === 0) { |
||||
|
this.$message.success('保存成功') |
||||
|
this.processDialogVisible = false |
||||
|
this.loadProcessList() |
||||
|
} else { |
||||
|
this.$message.error(data.msg || '保存失败') |
||||
|
} |
||||
|
}).catch(error => { |
||||
|
this.processSaving = false |
||||
|
this.$message.error('保存异常') |
||||
|
}) |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 删除工序行 |
||||
|
*/ |
||||
|
deleteProcessRow(row) { |
||||
|
this.$confirm('确定删除该工序?', '操作提示', { |
||||
|
confirmButtonText: '确定', |
||||
|
cancelButtonText: '取消', |
||||
|
type: 'warning' |
||||
|
}).then(() => { |
||||
|
deleteTriConfirmProcess({ |
||||
|
applyNo: this.applyNo, |
||||
|
processStep: row.processStep |
||||
|
}).then(({data}) => { |
||||
|
if (data && data.code === 0) { |
||||
|
this.$message.success('删除成功') |
||||
|
this.loadProcessList() |
||||
|
} else { |
||||
|
this.$message.error(data.msg || '删除失败') |
||||
|
} |
||||
|
}).catch(error => { |
||||
|
this.$message.error('删除异常') |
||||
|
}) |
||||
|
}) |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 打开确认对话框 |
||||
|
*/ |
||||
|
openConfirmDialog(row, roleType) { |
||||
|
// 检查是否可以确认 |
||||
|
if (!this.canConfirmProcess(row)) { |
||||
|
this.$message.warning('请等待上一道工序完成三方确认') |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
this.currentProcess = row |
||||
|
this.currentRoleType = roleType |
||||
|
this.confirmRemark = '' |
||||
|
this.sampleOk = null // 重置样品状态 |
||||
|
this.currentTime = this.formatDateTime(new Date()) |
||||
|
|
||||
|
const roleNames = { |
||||
|
'PROD': '车间负责人', |
||||
|
'QA': '质量负责人', |
||||
|
'TECH': '技术负责人' |
||||
|
} |
||||
|
this.confirmDialogTitle = `${roleNames[roleType]}确认 - 第${row.processSeq}道:${row.processStep}` |
||||
|
this.confirmDialogVisible = true |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 执行确认 |
||||
|
*/ |
||||
|
doConfirm() { |
||||
|
// 验证必填项 |
||||
|
if (this.sampleOk === null) { |
||||
|
this.$message.warning('请选择样品是否OK') |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
this.confirmLoading = true |
||||
|
|
||||
|
confirmTriApproval({ |
||||
|
applyNo: this.applyNo, |
||||
|
processStep: this.currentProcess.processStep, |
||||
|
roleType: this.currentRoleType, |
||||
|
approverUserId: this.$store.state.user.id, |
||||
|
approverName: this.$store.state.user.name, |
||||
|
sampleOk: this.sampleOk, // ✅ 传递样品状态 |
||||
|
comment: this.confirmRemark |
||||
|
}).then(({data}) => { |
||||
|
this.confirmLoading = false |
||||
|
if (data && data.code === 0) { |
||||
|
this.$message.success('确认成功') |
||||
|
this.confirmDialogVisible = false |
||||
|
this.loadProcessList() |
||||
|
} else { |
||||
|
this.$message.error(data.msg || '确认失败') |
||||
|
} |
||||
|
}).catch(error => { |
||||
|
this.confirmLoading = false |
||||
|
this.$message.error('确认异常') |
||||
|
}) |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 格式化日期时间 |
||||
|
*/ |
||||
|
formatDateTime(date) { |
||||
|
const year = date.getFullYear() |
||||
|
const month = String(date.getMonth() + 1).padStart(2, '0') |
||||
|
const day = String(date.getDate()).padStart(2, '0') |
||||
|
const hours = String(date.getHours()).padStart(2, '0') |
||||
|
const minutes = String(date.getMinutes()).padStart(2, '0') |
||||
|
const seconds = String(date.getSeconds()).padStart(2, '0') |
||||
|
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}` |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style scoped> |
||||
|
.exp-tri-confirm { |
||||
|
background-color: #fff; |
||||
|
} |
||||
|
|
||||
|
.toolbar { |
||||
|
margin-bottom: 10px; |
||||
|
text-align: left; |
||||
|
} |
||||
|
|
||||
|
.el-button + .el-button { |
||||
|
margin-left: 5px; |
||||
|
} |
||||
|
|
||||
|
/* 表格样式优化 */ |
||||
|
.el-table >>> .el-table__header th { |
||||
|
background-color: #F5F7FA; |
||||
|
color: #606266; |
||||
|
font-weight: bold; |
||||
|
} |
||||
|
|
||||
|
.el-table >>> .el-table__row td { |
||||
|
padding: 8px 0; |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,490 @@ |
|||||
|
<template> |
||||
|
<div class="mod-config"> |
||||
|
<!-- 查询条件表单 --> |
||||
|
<el-form :inline="true" label-position="top"> |
||||
|
<el-form-item label="申请单号"> |
||||
|
<el-input v-model="queryHeaderData.applyNo" placeholder="支持模糊查询" clearable style="width: 150px"></el-input> |
||||
|
</el-form-item> |
||||
|
|
||||
|
<el-form-item label="事业部"> |
||||
|
<el-select v-model="queryHeaderData.buNo" placeholder="请选择" clearable style="width: 120px"> |
||||
|
<el-option label="全部" value=""></el-option> |
||||
|
<el-option |
||||
|
v-for="i in buList" |
||||
|
:key="i.buNo" |
||||
|
:label="i.buDesc" |
||||
|
:value="i.buNo"> |
||||
|
</el-option> |
||||
|
</el-select> |
||||
|
</el-form-item> |
||||
|
|
||||
|
<el-form-item label="试验类型"> |
||||
|
<el-select v-model="queryHeaderData.experimentType" placeholder="请选择" clearable style="width: 120px"> |
||||
|
<el-option label="全部" value=""></el-option> |
||||
|
<el-option label="High Risk" value="High Risk"></el-option> |
||||
|
<el-option label="Low Risk" value="Low Risk"></el-option> |
||||
|
</el-select> |
||||
|
</el-form-item> |
||||
|
|
||||
|
<el-form-item label="试验名称"> |
||||
|
<el-input v-model="queryHeaderData.title" placeholder="支持模糊查询" clearable style="width: 150px"></el-input> |
||||
|
</el-form-item> |
||||
|
|
||||
|
<el-form-item label="项目编号"> |
||||
|
<el-input v-model="queryHeaderData.projectNo" placeholder="支持模糊查询" clearable style="width: 150px"></el-input> |
||||
|
</el-form-item> |
||||
|
|
||||
|
<el-form-item label="产品型号"> |
||||
|
<el-input v-model="queryHeaderData.productType" placeholder="支持模糊查询" clearable style="width: 150px"></el-input> |
||||
|
</el-form-item> |
||||
|
|
||||
|
<el-form-item label="申请人"> |
||||
|
<el-input v-model="queryHeaderData.creatorName" placeholder="支持模糊查询" clearable style="width: 120px"></el-input> |
||||
|
</el-form-item> |
||||
|
|
||||
|
<el-form-item label="申请开始日期"> |
||||
|
<el-date-picker |
||||
|
v-model="queryHeaderData.createStartDate" |
||||
|
type="date" |
||||
|
placeholder="选择日期" |
||||
|
value-format="yyyy-MM-dd" |
||||
|
style="width: 150px"> |
||||
|
</el-date-picker> |
||||
|
</el-form-item> |
||||
|
|
||||
|
<el-form-item label="申请结束日期"> |
||||
|
<el-date-picker |
||||
|
v-model="queryHeaderData.createEndDate" |
||||
|
type="date" |
||||
|
placeholder="选择日期" |
||||
|
value-format="yyyy-MM-dd" |
||||
|
style="width: 150px"> |
||||
|
</el-date-picker> |
||||
|
</el-form-item> |
||||
|
|
||||
|
<el-form-item label=" "> |
||||
|
<el-button @click="getDataList('Y')" type="primary">查询</el-button> |
||||
|
<el-button @click="resetQuery()" type="default">重置</el-button> |
||||
|
</el-form-item> |
||||
|
</el-form> |
||||
|
|
||||
|
<!-- 待办申请单表格 --> |
||||
|
<el-table |
||||
|
:data="dataList" |
||||
|
v-loading="dataListLoading" |
||||
|
border |
||||
|
stripe |
||||
|
style="width: 100%;" |
||||
|
:height="height"> |
||||
|
|
||||
|
<el-table-column |
||||
|
label="操作" |
||||
|
width="120" |
||||
|
align="center" |
||||
|
header-align="center" |
||||
|
fixed="left"> |
||||
|
<template slot-scope="scope"> |
||||
|
<a type="text" size="small" @click="openApprovalDialog(scope.row)">审批</a> |
||||
|
</template> |
||||
|
</el-table-column> |
||||
|
|
||||
|
<el-table-column |
||||
|
prop="applyNo" |
||||
|
label="申请单号" |
||||
|
width="140" |
||||
|
align="center" |
||||
|
header-align="center" |
||||
|
show-overflow-tooltip> |
||||
|
</el-table-column> |
||||
|
|
||||
|
<el-table-column |
||||
|
prop="buDesc" |
||||
|
label="事业部" |
||||
|
width="80" |
||||
|
align="center" |
||||
|
header-align="center"> |
||||
|
</el-table-column> |
||||
|
|
||||
|
<el-table-column |
||||
|
prop="experimentType" |
||||
|
label="试验类型" |
||||
|
width="100" |
||||
|
align="center" |
||||
|
header-align="center"> |
||||
|
</el-table-column> |
||||
|
|
||||
|
<el-table-column |
||||
|
prop="title" |
||||
|
label="试验名称" |
||||
|
min-width="200" |
||||
|
align="left" |
||||
|
header-align="center" |
||||
|
show-overflow-tooltip> |
||||
|
</el-table-column> |
||||
|
|
||||
|
<el-table-column |
||||
|
prop="projectNo" |
||||
|
label="项目编号" |
||||
|
width="120" |
||||
|
align="center" |
||||
|
header-align="center" |
||||
|
show-overflow-tooltip> |
||||
|
</el-table-column> |
||||
|
|
||||
|
<el-table-column |
||||
|
prop="productType" |
||||
|
label="产品型号" |
||||
|
min-width="200" |
||||
|
align="center" |
||||
|
header-align="center" |
||||
|
show-overflow-tooltip> |
||||
|
</el-table-column> |
||||
|
|
||||
|
<el-table-column |
||||
|
prop="creatorName" |
||||
|
label="申请人" |
||||
|
width="100" |
||||
|
align="center" |
||||
|
header-align="center"> |
||||
|
</el-table-column> |
||||
|
|
||||
|
<el-table-column |
||||
|
prop="createTime" |
||||
|
label="申请时间" |
||||
|
width="160" |
||||
|
align="center" |
||||
|
header-align="center"> |
||||
|
</el-table-column> |
||||
|
|
||||
|
<el-table-column |
||||
|
prop="currentStep" |
||||
|
label="当前步骤" |
||||
|
width="140" |
||||
|
align="center" |
||||
|
header-align="center"> |
||||
|
</el-table-column> |
||||
|
</el-table> |
||||
|
|
||||
|
<!-- 分页组件 --> |
||||
|
<el-pagination |
||||
|
@size-change="sizeChangeHandle" |
||||
|
@current-change="currentChangeHandle" |
||||
|
:current-page="pageIndex" |
||||
|
:page-sizes="[20, 50, 100]" |
||||
|
:page-size="pageSize" |
||||
|
:total="totalPage" |
||||
|
layout="total, sizes, prev, pager, next, jumper" |
||||
|
style="margin-top: 20px; text-align: right;"> |
||||
|
</el-pagination> |
||||
|
|
||||
|
<!-- 审批弹窗 --> |
||||
|
<el-dialog |
||||
|
title="审批申请单" |
||||
|
:visible.sync="approvalDialogVisible" |
||||
|
width="1100px" |
||||
|
:close-on-click-modal="false" |
||||
|
v-drag> |
||||
|
|
||||
|
<!-- Tabs切换 --> |
||||
|
<el-tabs v-model="activeTab" type="border-card" style="font-size: 12px; min-height: 200px"> |
||||
|
<!-- 申请单基本信息 --> |
||||
|
<el-tab-pane label="申请单信息" name="basic"> |
||||
|
<el-descriptions :column="2" border size="small"> |
||||
|
<el-descriptions-item label="申请单号">{{ currentApply.applyNo }}</el-descriptions-item> |
||||
|
<el-descriptions-item label="事业部">{{ currentApply.buNo }}</el-descriptions-item> |
||||
|
<el-descriptions-item label="试验类型"> |
||||
|
<el-tag :type="currentApply.experimentType === 'High Risk' ? 'danger' : 'success'"> |
||||
|
{{ currentApply.experimentType }} |
||||
|
</el-tag> |
||||
|
</el-descriptions-item> |
||||
|
<el-descriptions-item label="项目编号">{{ currentApply.projectNo }}</el-descriptions-item> |
||||
|
<el-descriptions-item label="试验名称" :span="2">{{ currentApply.title }}</el-descriptions-item> |
||||
|
<el-descriptions-item label="试验目的" :span="2">{{ currentApply.purpose }}</el-descriptions-item> |
||||
|
<el-descriptions-item label="验证方法" :span="2">{{ currentApply.justification }}</el-descriptions-item> |
||||
|
<el-descriptions-item label="产品型号">{{ currentApply.productType }}</el-descriptions-item> |
||||
|
<el-descriptions-item label="申请数量">{{ currentApply.quantityReq }}</el-descriptions-item> |
||||
|
<el-descriptions-item label="期望完成日期">{{ currentApply.expectedFinishDate }}</el-descriptions-item> |
||||
|
<el-descriptions-item label="申请人">{{ currentApply.creatorName }}</el-descriptions-item> |
||||
|
<el-descriptions-item label="申请时间" :span="2">{{ currentApply.createTime }}</el-descriptions-item> |
||||
|
</el-descriptions> |
||||
|
</el-tab-pane> |
||||
|
|
||||
|
<!-- 审批历史 --> |
||||
|
<el-tab-pane label="审批历史" name="history"> |
||||
|
<approval-history |
||||
|
v-if="activeTab === 'history' && currentApply.applyNo" |
||||
|
:apply-no="currentApply.applyNo"> |
||||
|
</approval-history> |
||||
|
</el-tab-pane> |
||||
|
</el-tabs> |
||||
|
|
||||
|
<!-- 审批操作 --> |
||||
|
<el-divider content-position="left">审批操作</el-divider> |
||||
|
<el-form :model="approvalData" label-position="top" style="margin-left: 5px; margin-top: -5px;"> |
||||
|
<el-row :gutter="20"> |
||||
|
<el-col :span="24"> |
||||
|
<el-form-item label="审批意见" required> |
||||
|
<el-input |
||||
|
v-model="approvalData.comment" |
||||
|
type="textarea" |
||||
|
:rows="2" |
||||
|
placeholder="请输入审批意见"> |
||||
|
</el-input> |
||||
|
</el-form-item> |
||||
|
</el-col> |
||||
|
</el-row> |
||||
|
</el-form> |
||||
|
|
||||
|
<el-footer style="height: 40px; margin-top: 40px; text-align: center"> |
||||
|
<el-button type="success" @click="approveApply" :loading="approvalLoading"> |
||||
|
{{ approvalLoading ? '审批中...' : '批准' }} |
||||
|
</el-button> |
||||
|
<el-button type="danger" @click="rejectApply" :loading="approvalLoading"> |
||||
|
{{ approvalLoading ? '驳回中...' : '驳回' }} |
||||
|
</el-button> |
||||
|
<el-button type="primary" @click="approvalDialogVisible = false" :disabled="approvalLoading">关闭</el-button> |
||||
|
</el-footer> |
||||
|
</el-dialog> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import { getPendingApplyList, approveExpApply } from '@/api/erf/erf' |
||||
|
import { getBuList } from '@/api/factory/site' |
||||
|
import ApprovalHistory from './components/approvalHistory.vue' |
||||
|
|
||||
|
export default { |
||||
|
name: 'ExpApplyApproval', |
||||
|
|
||||
|
components: { |
||||
|
ApprovalHistory |
||||
|
}, |
||||
|
|
||||
|
data() { |
||||
|
return { |
||||
|
buList: [], |
||||
|
activeTab: 'basic', |
||||
|
// 查询条件 |
||||
|
queryHeaderData: { |
||||
|
applyNo: '', |
||||
|
buNo: '', |
||||
|
experimentType: '', |
||||
|
title: '', |
||||
|
projectNo: '', |
||||
|
productType: '', |
||||
|
creatorName: '', |
||||
|
createStartDate: '', |
||||
|
createEndDate: '', |
||||
|
currentUserId: this.$store.state.user.id, |
||||
|
pendingStatus: '已下达', // 审批节点只查询已下达状态 |
||||
|
page: 1, |
||||
|
limit: 20 |
||||
|
}, |
||||
|
|
||||
|
// 数据列表 |
||||
|
dataList: [], |
||||
|
|
||||
|
// 分页参数 |
||||
|
pageIndex: 1, |
||||
|
pageSize: 20, |
||||
|
totalPage: 0, |
||||
|
dataListLoading: false, |
||||
|
|
||||
|
// 审批弹窗 |
||||
|
approvalDialogVisible: false, |
||||
|
currentApply: {}, |
||||
|
approvalData: { |
||||
|
applyNo: '', |
||||
|
nodeCode: '', |
||||
|
action: '', |
||||
|
comment: '', |
||||
|
operatorUserId: this.$store.state.user.id, |
||||
|
operatorName: this.$store.state.user.name |
||||
|
}, |
||||
|
approvalLoading: false, |
||||
|
|
||||
|
// 表格高度 |
||||
|
height: window.innerHeight - 260 |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
activated() { |
||||
|
this.loadBuList() |
||||
|
this.getDataList() |
||||
|
}, |
||||
|
|
||||
|
methods: { |
||||
|
/** |
||||
|
* 加载事业部列表 |
||||
|
*/ |
||||
|
loadBuList() { |
||||
|
const tempData = { site: this.$store.state.user.site } |
||||
|
getBuList(tempData).then(({data}) => { |
||||
|
if (data.code === 0) { |
||||
|
this.buList = data.row1 |
||||
|
} |
||||
|
}) |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 获取待办列表 |
||||
|
*/ |
||||
|
getDataList(flag) { |
||||
|
if (flag === 'Y') { |
||||
|
this.pageIndex = 1 |
||||
|
} |
||||
|
|
||||
|
this.queryHeaderData.page = this.pageIndex |
||||
|
this.queryHeaderData.limit = this.pageSize |
||||
|
this.queryHeaderData.currentUserId = this.$store.state.user.id |
||||
|
|
||||
|
this.dataListLoading = true |
||||
|
|
||||
|
getPendingApplyList(this.queryHeaderData).then(({data}) => { |
||||
|
this.dataListLoading = false |
||||
|
if (data && data.code === 0) { |
||||
|
this.dataList = data.page.list || [] |
||||
|
this.totalPage = data.page.totalCount || 0 |
||||
|
} else { |
||||
|
this.dataList = [] |
||||
|
this.totalPage = 0 |
||||
|
this.$message.error(data.msg || '查询失败') |
||||
|
} |
||||
|
}).catch(error => { |
||||
|
this.dataListLoading = false |
||||
|
this.$message.error('查询异常') |
||||
|
}) |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 重置查询条件 |
||||
|
*/ |
||||
|
resetQuery() { |
||||
|
this.queryHeaderData.applyNo = '' |
||||
|
this.queryHeaderData.buNo = '' |
||||
|
this.queryHeaderData.experimentType = '' |
||||
|
this.queryHeaderData.title = '' |
||||
|
this.queryHeaderData.projectNo = '' |
||||
|
this.queryHeaderData.productType = '' |
||||
|
this.queryHeaderData.creatorName = '' |
||||
|
this.queryHeaderData.createStartDate = '' |
||||
|
this.queryHeaderData.createEndDate = '' |
||||
|
this.getDataList('Y') |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 打开审批对话框 |
||||
|
*/ |
||||
|
openApprovalDialog(row) { |
||||
|
this.currentApply = { ...row } |
||||
|
|
||||
|
// 查询当前流程实例,获取正确的节点编码 |
||||
|
this.getCurrentNodeCode(row.applyNo) |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 获取当前节点编码 |
||||
|
*/ |
||||
|
getCurrentNodeCode(applyNo) { |
||||
|
// 调用API获取流程实例的当前节点 |
||||
|
this.$http({ |
||||
|
url: this.$http.adornUrl('/erf/expApply/getCurrentNodeCode'), |
||||
|
method: 'post', |
||||
|
data: this.$http.adornData({ applyNo: applyNo }) |
||||
|
}).then(({data}) => { |
||||
|
if (data && data.code === 0) { |
||||
|
this.approvalData = { |
||||
|
applyNo: applyNo, |
||||
|
nodeCode: data.nodeCode, // 使用流程实例的当前节点编码 |
||||
|
action: '', |
||||
|
comment: '', |
||||
|
operatorUserId: this.$store.state.user.id, |
||||
|
operatorName: this.$store.state.user.name |
||||
|
} |
||||
|
this.approvalDialogVisible = true |
||||
|
} else { |
||||
|
this.$message.error('获取节点信息失败') |
||||
|
} |
||||
|
}) |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 批准操作 |
||||
|
*/ |
||||
|
approveApply() { |
||||
|
if (!this.approvalData.comment) { |
||||
|
this.$message.warning('请输入审批意见') |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
this.approvalData.action = '批准' |
||||
|
this.doApproval() |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 驳回操作 |
||||
|
*/ |
||||
|
rejectApply() { |
||||
|
if (!this.approvalData.comment) { |
||||
|
this.$message.warning('请输入审批意见') |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
this.approvalData.action = '驳回' |
||||
|
this.doApproval() |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 执行审批 |
||||
|
*/ |
||||
|
doApproval() { |
||||
|
this.approvalLoading = true |
||||
|
|
||||
|
approveExpApply(this.approvalData).then(({data}) => { |
||||
|
this.approvalLoading = false |
||||
|
if (data && data.code === 0) { |
||||
|
this.$message.success('审批成功') |
||||
|
this.approvalDialogVisible = false |
||||
|
this.getDataList() |
||||
|
} else { |
||||
|
this.$message.error(data.msg || '审批失败') |
||||
|
} |
||||
|
}).catch(error => { |
||||
|
this.approvalLoading = false |
||||
|
this.$message.error('审批异常') |
||||
|
}) |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 分页大小改变 |
||||
|
*/ |
||||
|
sizeChangeHandle(val) { |
||||
|
this.pageSize = val |
||||
|
this.pageIndex = 1 |
||||
|
this.getDataList() |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 当前页改变 |
||||
|
*/ |
||||
|
currentChangeHandle(val) { |
||||
|
this.pageIndex = val |
||||
|
this.getDataList() |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style scoped> |
||||
|
.mod-config { |
||||
|
|
||||
|
} |
||||
|
|
||||
|
.el-form { |
||||
|
margin-bottom: 20px; |
||||
|
} |
||||
|
|
||||
|
.dialog-footer { |
||||
|
text-align: right; |
||||
|
} |
||||
|
</style> |
||||
1311
src/views/modules/erf/expApplyList.vue
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,372 @@ |
|||||
|
<template> |
||||
|
<div class="mod-config"> |
||||
|
<!-- 查询条件 --> |
||||
|
<el-form :inline="true" label-position="top"> |
||||
|
<el-form-item label="申请单号"> |
||||
|
<el-input v-model="searchData.applyNo" placeholder="请输入申请单号" clearable style="width: 150px"></el-input> |
||||
|
</el-form-item> |
||||
|
|
||||
|
<el-form-item label="事业部"> |
||||
|
<el-select v-model="searchData.buNo" clearable style="width: 120px"> |
||||
|
<el-option label="全部" value=""></el-option> |
||||
|
<el-option |
||||
|
v-for="i in buList" |
||||
|
:key="i.buNo" |
||||
|
:label="i.buDesc" |
||||
|
:value="i.buNo"> |
||||
|
</el-option> |
||||
|
</el-select> |
||||
|
</el-form-item> |
||||
|
|
||||
|
<el-form-item label="试验类型"> |
||||
|
<el-select v-model="searchData.experimentType" placeholder="请选择" clearable style="width: 120px"> |
||||
|
<el-option label="全部" value=""></el-option> |
||||
|
<el-option label="High Risk" value="High Risk"></el-option> |
||||
|
<el-option label="Low Risk" value="Low Risk"></el-option> |
||||
|
</el-select> |
||||
|
</el-form-item> |
||||
|
|
||||
|
<el-form-item label="试验名称"> |
||||
|
<el-input v-model="searchData.title" placeholder="支持模糊查询" clearable style="width: 150px"></el-input> |
||||
|
</el-form-item> |
||||
|
|
||||
|
<el-form-item label="项目编号"> |
||||
|
<el-input v-model="searchData.projectNo" placeholder="支持模糊查询" clearable style="width: 150px"></el-input> |
||||
|
</el-form-item> |
||||
|
|
||||
|
<el-form-item label="产品型号"> |
||||
|
<el-input v-model="searchData.productType" placeholder="支持模糊查询" clearable style="width: 150px"></el-input> |
||||
|
</el-form-item> |
||||
|
|
||||
|
<el-form-item label="申请人"> |
||||
|
<el-input v-model="searchData.creatorName" placeholder="支持模糊查询" clearable style="width: 120px"></el-input> |
||||
|
</el-form-item> |
||||
|
|
||||
|
<!-- <el-form-item label="申请开始日期"> |
||||
|
<el-date-picker |
||||
|
v-model="searchData.createStartDate" |
||||
|
type="date" |
||||
|
placeholder="选择日期" |
||||
|
value-format="yyyy-MM-dd" |
||||
|
style="width: 150px"> |
||||
|
</el-date-picker> |
||||
|
</el-form-item> |
||||
|
|
||||
|
<el-form-item label="申请结束日期"> |
||||
|
<el-date-picker |
||||
|
v-model="searchData.createEndDate" |
||||
|
type="date" |
||||
|
placeholder="选择日期" |
||||
|
value-format="yyyy-MM-dd" |
||||
|
style="width: 150px"> |
||||
|
</el-date-picker> |
||||
|
</el-form-item>--> |
||||
|
|
||||
|
<el-form-item label="期望完成开始日期"> |
||||
|
<el-date-picker |
||||
|
v-model="searchData.expectedFinishStartDate" |
||||
|
type="date" |
||||
|
placeholder="选择日期" |
||||
|
value-format="yyyy-MM-dd" |
||||
|
style="width: 150px"> |
||||
|
</el-date-picker> |
||||
|
</el-form-item> |
||||
|
|
||||
|
<el-form-item label="期望完成结束日期"> |
||||
|
<el-date-picker |
||||
|
v-model="searchData.expectedFinishEndDate" |
||||
|
type="date" |
||||
|
placeholder="选择日期" |
||||
|
value-format="yyyy-MM-dd" |
||||
|
style="width: 150px"> |
||||
|
</el-date-picker> |
||||
|
</el-form-item> |
||||
|
|
||||
|
<el-form-item label=" "> |
||||
|
<el-button @click="getDataList('Y')" type="primary">查询</el-button> |
||||
|
</el-form-item> |
||||
|
</el-form> |
||||
|
|
||||
|
<!-- 待排产申请单列表 --> |
||||
|
<el-table |
||||
|
:data="dataList" |
||||
|
v-loading="dataListLoading" |
||||
|
border |
||||
|
stripe |
||||
|
style="width: 100%;" |
||||
|
:height="height"> |
||||
|
|
||||
|
<el-table-column |
||||
|
label="操作" |
||||
|
width="100" |
||||
|
align="center" |
||||
|
fixed="left"> |
||||
|
<template slot-scope="scope"> |
||||
|
<a type="text" size="small" @click="openScheduleDialog(scope.row)">排产</a> |
||||
|
</template> |
||||
|
</el-table-column> |
||||
|
|
||||
|
<el-table-column prop="applyNo" label="申请单号" width="140" align="center"></el-table-column> |
||||
|
<el-table-column prop="buNo" label="事业部" width="80" align="center"></el-table-column> |
||||
|
<el-table-column prop="experimentType" label="试验类型" min-width="100" align="center"></el-table-column> |
||||
|
<el-table-column prop="title" label="试验名称" min-width="200" align="left" show-overflow-tooltip></el-table-column> |
||||
|
<el-table-column |
||||
|
prop="projectNo" |
||||
|
label="项目编号" |
||||
|
min-width="120" |
||||
|
align="center" |
||||
|
header-align="center" |
||||
|
show-overflow-tooltip> |
||||
|
</el-table-column> |
||||
|
|
||||
|
<el-table-column |
||||
|
prop="productType" |
||||
|
label="产品型号" |
||||
|
min-width="200" |
||||
|
align="center" |
||||
|
header-align="center" |
||||
|
show-overflow-tooltip> |
||||
|
</el-table-column> |
||||
|
<el-table-column prop="expectedFinishDate" label="期望完成日期" width="120" align="center"></el-table-column> |
||||
|
<el-table-column |
||||
|
prop="creatorName" |
||||
|
label="申请人" |
||||
|
width="100" |
||||
|
align="center" |
||||
|
header-align="center"> |
||||
|
</el-table-column> |
||||
|
|
||||
|
<el-table-column |
||||
|
prop="createTime" |
||||
|
label="申请时间" |
||||
|
width="160" |
||||
|
align="center" |
||||
|
header-align="center"> |
||||
|
</el-table-column> |
||||
|
</el-table> |
||||
|
|
||||
|
<!-- 分页 --> |
||||
|
<el-pagination |
||||
|
@size-change="sizeChangeHandle" |
||||
|
@current-change="currentChangeHandle" |
||||
|
:current-page="pageIndex" |
||||
|
:page-sizes="[20, 50, 100]" |
||||
|
:page-size="pageSize" |
||||
|
:total="totalPage" |
||||
|
layout="total, sizes, prev, pager, next, jumper" |
||||
|
style="margin-top: 20px; text-align: right;"> |
||||
|
</el-pagination> |
||||
|
|
||||
|
<!-- 排产弹窗 --> |
||||
|
<el-dialog |
||||
|
title="计划员排产" |
||||
|
:visible.sync="scheduleDialogVisible" |
||||
|
width="600px" |
||||
|
:close-on-click-modal="false" |
||||
|
v-drag> |
||||
|
|
||||
|
<el-form :model="scheduleData" label-position="top" style="margin-left: 5px; margin-top: -5px;"> |
||||
|
<el-row :gutter="20"> |
||||
|
<el-col :span="8"> |
||||
|
<el-form-item label="申请单号"> |
||||
|
<span>{{ scheduleData.applyNo }}</span> |
||||
|
</el-form-item> |
||||
|
</el-col> |
||||
|
|
||||
|
<el-col :span="8"> |
||||
|
<el-form-item label="试验类型"> |
||||
|
<el-tag :type="currentApply.experimentType === 'High Risk' ? 'danger' : 'success'"> |
||||
|
{{ currentApply.experimentType }} |
||||
|
</el-tag> |
||||
|
</el-form-item> |
||||
|
</el-col> |
||||
|
|
||||
|
<el-col :span="8"> |
||||
|
<el-form-item label="申请人"> |
||||
|
<span>{{ currentApply.creatorName }}</span> |
||||
|
</el-form-item> |
||||
|
</el-col> |
||||
|
</el-row> |
||||
|
|
||||
|
<el-row :gutter="20"> |
||||
|
<el-col :span="24"> |
||||
|
<el-form-item label="试验名称"> |
||||
|
<span>{{ currentApply.title }}</span> |
||||
|
</el-form-item> |
||||
|
</el-col> |
||||
|
</el-row> |
||||
|
|
||||
|
<el-row :gutter="20"> |
||||
|
<el-col :span="12"> |
||||
|
<el-form-item label="实验工单号" required> |
||||
|
<el-input |
||||
|
v-model="scheduleData.workOrderNo" |
||||
|
placeholder="请输入实验工单号"> |
||||
|
</el-input> |
||||
|
</el-form-item> |
||||
|
</el-col> |
||||
|
|
||||
|
<el-col :span="12"> |
||||
|
<el-form-item label="排产日期" required> |
||||
|
<el-date-picker |
||||
|
v-model="scheduleData.scheduledDate" |
||||
|
type="date" |
||||
|
format="yyyy-MM-dd" |
||||
|
value-format="yyyy-MM-dd" |
||||
|
placeholder="选择排产日期" |
||||
|
style="width: 100%"> |
||||
|
</el-date-picker> |
||||
|
</el-form-item> |
||||
|
</el-col> |
||||
|
</el-row> |
||||
|
</el-form> |
||||
|
|
||||
|
<el-footer style="height: 40px; margin-top: 10px; text-align: center"> |
||||
|
<el-button type="primary" @click="doSchedule" :loading="scheduleLoading"> |
||||
|
{{ scheduleLoading ? '排产中...' : '确定排产' }} |
||||
|
</el-button> |
||||
|
<el-button type="primary" @click="scheduleDialogVisible = false" :disabled="scheduleLoading">关闭</el-button> |
||||
|
</el-footer> |
||||
|
</el-dialog> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import { getPendingApplyList, plannerSchedule } from '@/api/erf/erf' |
||||
|
import { getBuList } from '@/api/factory/site' |
||||
|
|
||||
|
export default { |
||||
|
name: 'PlannerSchedule', |
||||
|
|
||||
|
data() { |
||||
|
return { |
||||
|
buList: [], |
||||
|
searchData: { |
||||
|
applyNo: '', |
||||
|
buNo: '', |
||||
|
currentUserId: this.$store.state.user.id, |
||||
|
pendingStatus: '已批准', // 计划员排产只查询已批准状态 |
||||
|
page: 1, |
||||
|
limit: 20 |
||||
|
}, |
||||
|
|
||||
|
dataList: [], |
||||
|
pageIndex: 1, |
||||
|
pageSize: 20, |
||||
|
totalPage: 0, |
||||
|
dataListLoading: false, |
||||
|
|
||||
|
scheduleDialogVisible: false, |
||||
|
currentApply: {}, |
||||
|
scheduleData: { |
||||
|
applyNo: '', |
||||
|
workOrderNo: '', |
||||
|
scheduledDate: '' |
||||
|
}, |
||||
|
scheduleLoading: false, |
||||
|
|
||||
|
height: window.innerHeight - 260 |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
activated() { |
||||
|
this.loadBuList() |
||||
|
this.getDataList() |
||||
|
}, |
||||
|
|
||||
|
methods: { |
||||
|
/** |
||||
|
* 加载事业部列表 |
||||
|
*/ |
||||
|
loadBuList() { |
||||
|
const tempData = { site: this.$store.state.user.site } |
||||
|
getBuList(tempData).then(({data}) => { |
||||
|
if (data.code === 0) { |
||||
|
this.buList = data.row1 |
||||
|
} |
||||
|
}) |
||||
|
}, |
||||
|
|
||||
|
getDataList(flag) { |
||||
|
if (flag === 'Y') { |
||||
|
this.pageIndex = 1 |
||||
|
} |
||||
|
|
||||
|
this.searchData.page = this.pageIndex |
||||
|
this.searchData.limit = this.pageSize |
||||
|
|
||||
|
this.dataListLoading = true |
||||
|
|
||||
|
getPendingApplyList(this.searchData).then(({data}) => { |
||||
|
this.dataListLoading = false |
||||
|
if (data && data.code === 0) { |
||||
|
this.dataList = data.page.list || [] |
||||
|
this.totalPage = data.page.totalCount || 0 |
||||
|
} else { |
||||
|
this.dataList = [] |
||||
|
this.totalPage = 0 |
||||
|
} |
||||
|
}).catch(error => { |
||||
|
this.dataListLoading = false |
||||
|
}) |
||||
|
}, |
||||
|
|
||||
|
openScheduleDialog(row) { |
||||
|
this.currentApply = { ...row } |
||||
|
this.scheduleData = { |
||||
|
applyNo: row.applyNo, |
||||
|
workOrderNo: '', |
||||
|
scheduledDate: '', |
||||
|
processSteps: '', |
||||
|
comment: '' |
||||
|
} |
||||
|
this.scheduleDialogVisible = true |
||||
|
}, |
||||
|
|
||||
|
doSchedule() { |
||||
|
if (!this.scheduleData.workOrderNo) { |
||||
|
this.$message.warning('请输入实验工单号') |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
if (!this.scheduleData.scheduledDate) { |
||||
|
this.$message.warning('请选择排产日期') |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
|
||||
|
this.scheduleLoading = true |
||||
|
|
||||
|
plannerSchedule(this.scheduleData).then(({data}) => { |
||||
|
this.scheduleLoading = false |
||||
|
if (data && data.code === 0) { |
||||
|
this.$message.success('排产成功') |
||||
|
this.scheduleDialogVisible = false |
||||
|
this.getDataList() |
||||
|
} else { |
||||
|
this.$message.error(data.msg || '排产失败') |
||||
|
} |
||||
|
}).catch(error => { |
||||
|
this.scheduleLoading = false |
||||
|
}) |
||||
|
}, |
||||
|
|
||||
|
sizeChangeHandle(val) { |
||||
|
this.pageSize = val |
||||
|
this.pageIndex = 1 |
||||
|
this.getDataList() |
||||
|
}, |
||||
|
|
||||
|
currentChangeHandle(val) { |
||||
|
this.pageIndex = val |
||||
|
this.getDataList() |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style scoped> |
||||
|
.mod-config { |
||||
|
|
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,324 @@ |
|||||
|
<template> |
||||
|
<div class="mod-config"> |
||||
|
<!-- 查询条件 --> |
||||
|
<el-form :inline="true" label-position="top"> |
||||
|
<el-form-item label="申请单号"> |
||||
|
<el-input v-model="searchData.applyNo" placeholder="请输入申请单号" style="width: 200px"></el-input> |
||||
|
</el-form-item> |
||||
|
|
||||
|
<el-form-item label=" "> |
||||
|
<el-button @click="getConfirmList" type="primary">查询</el-button> |
||||
|
</el-form-item> |
||||
|
</el-form> |
||||
|
|
||||
|
<!-- 三方确认表格 --> |
||||
|
<el-table |
||||
|
:data="dataList" |
||||
|
v-loading="dataListLoading" |
||||
|
border |
||||
|
style="width: 100%;" |
||||
|
:height="height"> |
||||
|
|
||||
|
<el-table-column |
||||
|
prop="applyNo" |
||||
|
label="申请单号" |
||||
|
min-width="140" |
||||
|
align="center" |
||||
|
header-align="center"> |
||||
|
</el-table-column> |
||||
|
|
||||
|
<el-table-column |
||||
|
prop="processStep" |
||||
|
label="工序名称" |
||||
|
min-width="120" |
||||
|
align="center" |
||||
|
header-align="center"> |
||||
|
</el-table-column> |
||||
|
|
||||
|
<el-table-column |
||||
|
label="生产确认" |
||||
|
min-width="180" |
||||
|
align="center" |
||||
|
header-align="center"> |
||||
|
<template slot-scope="scope"> |
||||
|
<div v-if="getConfirmStatus(scope.row, 'PROD')"> |
||||
|
<el-tag type="success" size="small">已确认</el-tag> |
||||
|
<div style="font-size: 12px; color: #999;"> |
||||
|
{{ getApproverName(scope.row, 'PROD') }} |
||||
|
</div> |
||||
|
</div> |
||||
|
<el-button |
||||
|
v-else |
||||
|
type="text" |
||||
|
size="small" |
||||
|
@click="openConfirmDialog(scope.row, 'PROD')"> |
||||
|
确认 |
||||
|
</el-button> |
||||
|
</template> |
||||
|
</el-table-column> |
||||
|
|
||||
|
<el-table-column |
||||
|
label="质量确认" |
||||
|
min-width="180" |
||||
|
align="center" |
||||
|
header-align="center"> |
||||
|
<template slot-scope="scope"> |
||||
|
<div v-if="getConfirmStatus(scope.row, 'QA')"> |
||||
|
<el-tag type="success" size="small">已确认</el-tag> |
||||
|
<div style="font-size: 12px; color: #999;"> |
||||
|
{{ getApproverName(scope.row, 'QA') }} |
||||
|
</div> |
||||
|
</div> |
||||
|
<el-button |
||||
|
v-else |
||||
|
type="text" |
||||
|
size="small" |
||||
|
@click="openConfirmDialog(scope.row, 'QA')"> |
||||
|
确认 |
||||
|
</el-button> |
||||
|
</template> |
||||
|
</el-table-column> |
||||
|
|
||||
|
<el-table-column |
||||
|
label="技术确认" |
||||
|
min-width="180" |
||||
|
align="center" |
||||
|
header-align="center"> |
||||
|
<template slot-scope="scope"> |
||||
|
<div v-if="getConfirmStatus(scope.row, 'TECH')"> |
||||
|
<el-tag type="success" size="small">已确认</el-tag> |
||||
|
<div style="font-size: 12px; color: #999;"> |
||||
|
{{ getApproverName(scope.row, 'TECH') }} |
||||
|
</div> |
||||
|
</div> |
||||
|
<el-button |
||||
|
v-else |
||||
|
type="text" |
||||
|
size="small" |
||||
|
@click="openConfirmDialog(scope.row, 'TECH')"> |
||||
|
确认 |
||||
|
</el-button> |
||||
|
</template> |
||||
|
</el-table-column> |
||||
|
|
||||
|
<el-table-column |
||||
|
label="整体状态" |
||||
|
min-width="120" |
||||
|
align="center" |
||||
|
header-align="center"> |
||||
|
<template slot-scope="scope"> |
||||
|
<el-tag |
||||
|
:type="isAllConfirmed(scope.row) ? 'success' : 'warning'" |
||||
|
size="small"> |
||||
|
{{ isAllConfirmed(scope.row) ? '已完成' : '待确认' }} |
||||
|
</el-tag> |
||||
|
</template> |
||||
|
</el-table-column> |
||||
|
</el-table> |
||||
|
|
||||
|
<!-- 确认弹窗 --> |
||||
|
<el-dialog |
||||
|
:title="'三方确认 - ' + getRoleName(confirmData.roleType)" |
||||
|
:visible.sync="confirmDialogVisible" |
||||
|
width="600px" |
||||
|
:close-on-click-modal="false" |
||||
|
v-drag> |
||||
|
|
||||
|
<el-form :model="confirmData" label-position="top" style="margin-left: 5px; margin-top: -5px;"> |
||||
|
<el-row :gutter="20"> |
||||
|
<el-col :span="12"> |
||||
|
<el-form-item label="申请单号"> |
||||
|
<span>{{ confirmData.applyNo }}</span> |
||||
|
</el-form-item> |
||||
|
</el-col> |
||||
|
|
||||
|
<el-col :span="12"> |
||||
|
<el-form-item label="工序"> |
||||
|
<span>{{ confirmData.processStep }}</span> |
||||
|
</el-form-item> |
||||
|
</el-col> |
||||
|
</el-row> |
||||
|
|
||||
|
<el-row :gutter="20"> |
||||
|
<el-col :span="12"> |
||||
|
<el-form-item label="确认角色"> |
||||
|
<span>{{ getRoleName(confirmData.roleType) }}</span> |
||||
|
</el-form-item> |
||||
|
</el-col> |
||||
|
|
||||
|
<el-col :span="12"> |
||||
|
<el-form-item label="样品是否OK" required> |
||||
|
<el-radio-group v-model="confirmData.sampleOk"> |
||||
|
<el-radio :label="true">OK</el-radio> |
||||
|
<el-radio :label="false">NG</el-radio> |
||||
|
</el-radio-group> |
||||
|
</el-form-item> |
||||
|
</el-col> |
||||
|
</el-row> |
||||
|
|
||||
|
<el-row :gutter="20"> |
||||
|
<el-col :span="24"> |
||||
|
<el-form-item label="确认意见"> |
||||
|
<el-input |
||||
|
v-model="confirmData.comment" |
||||
|
type="textarea" |
||||
|
:rows="4" |
||||
|
placeholder="请输入确认意见"> |
||||
|
</el-input> |
||||
|
</el-form-item> |
||||
|
</el-col> |
||||
|
</el-row> |
||||
|
</el-form> |
||||
|
|
||||
|
<el-footer style="height: 40px; margin-top: 10px; text-align: center"> |
||||
|
<el-button type="primary" @click="doConfirm" :loading="confirmLoading"> |
||||
|
{{ confirmLoading ? '确认中...' : '确认' }} |
||||
|
</el-button> |
||||
|
<el-button type="primary" @click="confirmDialogVisible = false" :disabled="confirmLoading">关闭</el-button> |
||||
|
</el-footer> |
||||
|
</el-dialog> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import { getTriConfirmList, confirmTriApproval } from '@/api/erf/erf' |
||||
|
|
||||
|
export default { |
||||
|
name: 'TriConfirm', |
||||
|
|
||||
|
data() { |
||||
|
return { |
||||
|
searchData: { |
||||
|
applyNo: '' |
||||
|
}, |
||||
|
|
||||
|
dataList: [], |
||||
|
dataListLoading: false, |
||||
|
|
||||
|
confirmDialogVisible: false, |
||||
|
confirmData: { |
||||
|
applyNo: '', |
||||
|
processStep: '', |
||||
|
roleType: '', |
||||
|
sampleOk: true, |
||||
|
comment: '', |
||||
|
approverUserId: this.$store.state.user.id, |
||||
|
approverName: this.$store.state.user.name |
||||
|
}, |
||||
|
confirmLoading: false, |
||||
|
|
||||
|
height: window.innerHeight - 200 |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
methods: { |
||||
|
/** |
||||
|
* 获取确认列表 |
||||
|
*/ |
||||
|
getConfirmList() { |
||||
|
if (!this.searchData.applyNo) { |
||||
|
this.$message.warning('请输入申请单号') |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
this.dataListLoading = true |
||||
|
|
||||
|
getTriConfirmList({ applyNo: this.searchData.applyNo }).then(({data}) => { |
||||
|
this.dataListLoading = false |
||||
|
if (data && data.code === 0) { |
||||
|
this.dataList = data.list || [] |
||||
|
} else { |
||||
|
this.dataList = [] |
||||
|
this.$message.error(data.msg || '查询失败') |
||||
|
} |
||||
|
}).catch(error => { |
||||
|
this.dataListLoading = false |
||||
|
this.$message.error('查询异常') |
||||
|
}) |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 打开确认对话框 |
||||
|
*/ |
||||
|
openConfirmDialog(row, roleType) { |
||||
|
this.confirmData = { |
||||
|
applyNo: row.applyNo, |
||||
|
processStep: row.processStep, |
||||
|
roleType: roleType, |
||||
|
sampleOk: true, |
||||
|
comment: '', |
||||
|
approverUserId: this.$store.state.user.id, |
||||
|
approverName: this.$store.state.user.name |
||||
|
} |
||||
|
this.confirmDialogVisible = true |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 执行确认 |
||||
|
*/ |
||||
|
doConfirm() { |
||||
|
if (!this.confirmData.comment) { |
||||
|
this.$message.warning('请输入确认意见') |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
this.confirmLoading = true |
||||
|
|
||||
|
confirmTriApproval(this.confirmData).then(({data}) => { |
||||
|
this.confirmLoading = false |
||||
|
if (data && data.code === 0) { |
||||
|
this.$message.success('确认成功') |
||||
|
this.confirmDialogVisible = false |
||||
|
this.getConfirmList() |
||||
|
} else { |
||||
|
this.$message.error(data.msg || '确认失败') |
||||
|
} |
||||
|
}).catch(error => { |
||||
|
this.confirmLoading = false |
||||
|
this.$message.error('确认异常') |
||||
|
}) |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 获取确认状态 |
||||
|
*/ |
||||
|
getConfirmStatus(row, roleType) { |
||||
|
// TODO: 从row中获取对应角色的确认状态 |
||||
|
return row[roleType.toLowerCase() + 'Confirmed'] |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 获取确认人姓名 |
||||
|
*/ |
||||
|
getApproverName(row, roleType) { |
||||
|
return row[roleType.toLowerCase() + 'ApproverName'] || '' |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 判断是否全部确认 |
||||
|
*/ |
||||
|
isAllConfirmed(row) { |
||||
|
return this.getConfirmStatus(row, 'PROD') && |
||||
|
this.getConfirmStatus(row, 'QA') && |
||||
|
this.getConfirmStatus(row, 'TECH') |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 获取角色名称 |
||||
|
*/ |
||||
|
getRoleName(roleType) { |
||||
|
const names = { |
||||
|
'PROD': '生产负责人', |
||||
|
'QA': '质量负责人', |
||||
|
'TECH': '技术负责人' |
||||
|
} |
||||
|
return names[roleType] || roleType |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style scoped> |
||||
|
.mod-config { |
||||
|
} |
||||
|
</style> |
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue