|
@@ -17,7 +17,7 @@
|
|
|
<p>作业状态: <span>{{ ticketStatusLabel }}</span></p>
|
|
<p>作业状态: <span>{{ ticketStatusLabel }}</span></p>
|
|
|
<p>开始时间: <span>{{JobForm.ticketStartTime}}</span> </p>
|
|
<p>开始时间: <span>{{JobForm.ticketStartTime}}</span> </p>
|
|
|
<p>结束时间: <span>{{JobForm.ticketEndTime}}</span> </p>
|
|
<p>结束时间: <span>{{JobForm.ticketEndTime}}</span> </p>
|
|
|
- <p>当前步骤: <span class="specialText">待更新</span> </p>
|
|
|
|
|
|
|
+ <p>当前步骤: <span class="specialText">{{ currentStepTitle }}</span> </p>
|
|
|
<p>最新日志: <span>待更新</span> </p>
|
|
<p>最新日志: <span>待更新</span> </p>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
@@ -30,11 +30,11 @@
|
|
|
>作业流程</span
|
|
>作业流程</span
|
|
|
>
|
|
>
|
|
|
</div>
|
|
</div>
|
|
|
- <div class="processDetail">
|
|
|
|
|
|
|
+ <div class="processDetail" ref="scrollContainer">
|
|
|
<!-- VueFlow 主画布 -->
|
|
<!-- VueFlow 主画布 -->
|
|
|
- <VueFlow style="width: 100%; height: 300px" :min-zoom="1" :max-zoom="1" :default-zoom="1">
|
|
|
|
|
|
|
+ <VueFlow style="width: 100%; height: 300px" :min-zoom="1" :max-zoom="1" :default-zoom="1" :nodes="nodes" :edges="edges">
|
|
|
<template #node-default="{ id, data }">
|
|
<template #node-default="{ id, data }">
|
|
|
- <div class="custom-node">
|
|
|
|
|
|
|
+ <div class="custom-node" :id="id">
|
|
|
<div class="node-content">
|
|
<div class="node-content">
|
|
|
<!-- 图标显示 -->
|
|
<!-- 图标显示 -->
|
|
|
<div style="font-size: 30px">
|
|
<div style="font-size: 30px">
|
|
@@ -75,7 +75,7 @@
|
|
|
</div>
|
|
</div>
|
|
|
</ContentWrap>
|
|
</ContentWrap>
|
|
|
<!--作业执行-->
|
|
<!--作业执行-->
|
|
|
- <ContentWrap>
|
|
|
|
|
|
|
+ <ContentWrap style="overflow: auto">
|
|
|
<div class="jobExecution">
|
|
<div class="jobExecution">
|
|
|
<el-tabs v-model="activeName" type="card" class="demo-tabs" @tab-click="handleClick">
|
|
<el-tabs v-model="activeName" type="card" class="demo-tabs" @tab-click="handleClick">
|
|
|
<!-- 作业人员-->
|
|
<!-- 作业人员-->
|
|
@@ -135,21 +135,22 @@
|
|
|
:key="group.id"
|
|
:key="group.id"
|
|
|
>
|
|
>
|
|
|
<div class="tab-header">
|
|
<div class="tab-header">
|
|
|
- <p class="tab-title">{{ group.groupName }}</p>
|
|
|
|
|
|
|
+ <p class="tab-title">{{ group.groupName }}(待锁定:{{group.
|
|
|
|
|
+ waitLock}} 已锁定:{{group.locked}} 已解锁:{{group.unlocked}})</p>
|
|
|
</div>
|
|
</div>
|
|
|
<!-- 表格数据 -->
|
|
<!-- 表格数据 -->
|
|
|
<div class="tableCon">
|
|
<div class="tableCon">
|
|
|
<el-radio-group v-model="tableLayouts[index]">
|
|
<el-radio-group v-model="tableLayouts[index]">
|
|
|
- <el-radio-button value="fixed">fixed</el-radio-button>
|
|
|
|
|
- <el-radio-button value="auto">auto</el-radio-button>
|
|
|
|
|
|
|
+ <el-radio-button value="fixed">常规</el-radio-button>
|
|
|
|
|
+ <el-radio-button value="auto">扩展</el-radio-button>
|
|
|
</el-radio-group>
|
|
</el-radio-group>
|
|
|
<el-table
|
|
<el-table
|
|
|
- :data="getPointsByGroupId(group.id)"
|
|
|
|
|
|
|
+ :data="group.ticketPointsRespVOList"
|
|
|
:table-layout="tableLayouts[index]"
|
|
:table-layout="tableLayouts[index]"
|
|
|
>
|
|
>
|
|
|
- <el-table-column prop="pointId" label="隔离点" />
|
|
|
|
|
- <el-table-column prop="ticketId" label="作用" />
|
|
|
|
|
- <el-table-column prop="ticketId" label="锁定人" />
|
|
|
|
|
|
|
+ <el-table-column prop="pointName" label="隔离点" />
|
|
|
|
|
+ <el-table-column prop="ability" label="作用" />
|
|
|
|
|
+ <el-table-column prop="lockUserName" label="锁定人" />
|
|
|
<el-table-column prop="pointStatus" label="锁定状态" >
|
|
<el-table-column prop="pointStatus" label="锁定状态" >
|
|
|
<template #default="scope">
|
|
<template #default="scope">
|
|
|
<dict-tag
|
|
<dict-tag
|
|
@@ -165,7 +166,7 @@
|
|
|
</div>
|
|
</div>
|
|
|
</el-tab-pane>
|
|
</el-tab-pane>
|
|
|
<!-- 人员共锁 -->
|
|
<!-- 人员共锁 -->
|
|
|
- <el-tab-pane name="third">
|
|
|
|
|
|
|
+ <el-tab-pane name="third" v-if="isShowclocker">
|
|
|
<template #label>
|
|
<template #label>
|
|
|
<div class="tab-label">
|
|
<div class="tab-label">
|
|
|
<img src="@/assets/images/icon_co-lock.png" alt="" class="titleimg" />
|
|
<img src="@/assets/images/icon_co-lock.png" alt="" class="titleimg" />
|
|
@@ -176,12 +177,12 @@
|
|
|
<!-- 待共锁-->
|
|
<!-- 待共锁-->
|
|
|
<div class="mumberbox">
|
|
<div class="mumberbox">
|
|
|
<div class="tab-header">
|
|
<div class="tab-header">
|
|
|
- <span class="tab-title">待共锁(5)</span>
|
|
|
|
|
|
|
+ <span class="tab-title">待共锁({{ waitLockUsers.length }})</span>
|
|
|
</div>
|
|
</div>
|
|
|
<div class="user">
|
|
<div class="user">
|
|
|
- <div class="userItem" >
|
|
|
|
|
|
|
+ <div class="userItem" v-for="user in waitLockUsers" :key="user.userId" >
|
|
|
<img src="@/assets/images/icon_co-lock.png" alt=""/>
|
|
<img src="@/assets/images/icon_co-lock.png" alt=""/>
|
|
|
- <p>哇哈哈</p>
|
|
|
|
|
|
|
+ <p>{{ user.userName }}</p>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
@@ -191,34 +192,28 @@
|
|
|
<!-- 已共锁-->
|
|
<!-- 已共锁-->
|
|
|
<div class="mumberbox">
|
|
<div class="mumberbox">
|
|
|
<div class="tab-header">
|
|
<div class="tab-header">
|
|
|
- <span class="tab-title">已共锁(5)</span>
|
|
|
|
|
|
|
+ <span class="tab-title">已共锁({{ lockedUsers.length }})</span>
|
|
|
</div>
|
|
</div>
|
|
|
<div class="user">
|
|
<div class="user">
|
|
|
- <div class="userItem" >
|
|
|
|
|
|
|
+ <div class="userItem" v-for="user in lockedUsers" :key="user.userId">
|
|
|
<img src="@/assets/images/icon_co-lock.png" alt=""/>
|
|
<img src="@/assets/images/icon_co-lock.png" alt=""/>
|
|
|
- <p>哇哈哈</p>
|
|
|
|
|
|
|
+ <p>{{ user.userName }}</p>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
<el-icon :size="50" :color="color" class="arrow">
|
|
<el-icon :size="50" :color="color" class="arrow">
|
|
|
<CaretRight />
|
|
<CaretRight />
|
|
|
</el-icon>
|
|
</el-icon>
|
|
|
- <!-- 未共锁-->
|
|
|
|
|
|
|
+ <!-- 已解除共锁-->
|
|
|
<div class="mumberbox">
|
|
<div class="mumberbox">
|
|
|
<div class="tab-header">
|
|
<div class="tab-header">
|
|
|
- <span class="tab-title">未共锁(0)</span>
|
|
|
|
|
|
|
+ <span class="tab-title">已解除共锁({{ unlockPendingUsers.length }})</span>
|
|
|
</div>
|
|
</div>
|
|
|
<div class="user">
|
|
<div class="user">
|
|
|
- <div class="userItem" >
|
|
|
|
|
- <img src="@/assets/images/icon_co-lock.png" alt=""/>
|
|
|
|
|
- <p>哇哈哈</p>
|
|
|
|
|
- </div>
|
|
|
|
|
- <div class="userItem" >
|
|
|
|
|
|
|
+ <div class="userItem" v-for="user in unlockPendingUsers" :key="user.userId">
|
|
|
<img src="@/assets/images/icon_co-lock.png" alt=""/>
|
|
<img src="@/assets/images/icon_co-lock.png" alt=""/>
|
|
|
- <p>哇哈哈</p>
|
|
|
|
|
|
|
+ <p>{{ user.userName }} </p>
|
|
|
</div>
|
|
</div>
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
@@ -231,16 +226,68 @@
|
|
|
<span class="tab-text">作业日志</span>
|
|
<span class="tab-text">作业日志</span>
|
|
|
</div>
|
|
</div>
|
|
|
</template>
|
|
</template>
|
|
|
- Task
|
|
|
|
|
|
|
+ <div class="joblogCon">
|
|
|
|
|
+ <!--顶部日志内容-->
|
|
|
|
|
+ <div class="joblogTop">
|
|
|
|
|
+ <p v-for="(item, index) in filteredJoblogList" :key="index">
|
|
|
|
|
+ <span v-html="renderLogContent(item.operationContent)"></span>
|
|
|
|
|
+ </p>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <!-- 底部过滤条件-->
|
|
|
|
|
+ <div class="bottomCheck">
|
|
|
|
|
+ <el-checkbox
|
|
|
|
|
+ :indeterminate="isIndeterminate"
|
|
|
|
|
+ :checked="checkAlljoblog"
|
|
|
|
|
+ @change="handleCheckAllChange"
|
|
|
|
|
+ style="margin:3px 25px;font-size: 16px"
|
|
|
|
|
+
|
|
|
|
|
+ >
|
|
|
|
|
+ 全部
|
|
|
|
|
+ </el-checkbox>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 多选项 -->
|
|
|
|
|
+ <el-checkbox-group
|
|
|
|
|
+ v-model="checkedJoblogs"
|
|
|
|
|
+ @change="handleCheckedJoblogsChange"
|
|
|
|
|
+ class="big-checkbox"
|
|
|
|
|
+ >
|
|
|
|
|
+ <el-checkbox
|
|
|
|
|
+ v-for="item in jobLogtypes"
|
|
|
|
|
+ :label="item.value"
|
|
|
|
|
+ :key="item.value"
|
|
|
|
|
+ class="big-checkbox"
|
|
|
|
|
+ >
|
|
|
|
|
+ {{ item.label }}
|
|
|
|
|
+ </el-checkbox>
|
|
|
|
|
+ </el-checkbox-group>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
</el-tab-pane>
|
|
</el-tab-pane>
|
|
|
</el-tabs>
|
|
</el-tabs>
|
|
|
</div>
|
|
</div>
|
|
|
</ContentWrap>
|
|
</ContentWrap>
|
|
|
|
|
+<!-- 作业日志弹框-->
|
|
|
|
|
+ <el-dialog
|
|
|
|
|
+ v-model="joblogDialogVisible"
|
|
|
|
|
+ title="Warning"
|
|
|
|
|
+ width="500"
|
|
|
|
|
+ align-center
|
|
|
|
|
+ >
|
|
|
|
|
+ <span>Open the dialog from the center from the screen</span>
|
|
|
|
|
+ <template #footer>
|
|
|
|
|
+ <div class="dialog-footer">
|
|
|
|
|
+ <el-button @click="joblogDialogVisible = false">Cancel</el-button>
|
|
|
|
|
+ <el-button type="primary" @click="joblogDialogVisible = false">
|
|
|
|
|
+ Confirm
|
|
|
|
|
+ </el-button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-dialog>
|
|
|
</template>
|
|
</template>
|
|
|
|
|
|
|
|
<script setup lang="ts">
|
|
<script setup lang="ts">
|
|
|
|
|
+import { ref, nextTick, onMounted, onBeforeUnmount } from 'vue'
|
|
|
import { Handle, useVueFlow, VueFlow } from '@vue-flow/core'
|
|
import { Handle, useVueFlow, VueFlow } from '@vue-flow/core'
|
|
|
-import { ref } from 'vue'
|
|
|
|
|
import * as JobPointGroup from '@/api/job/jobPointGroup'
|
|
import * as JobPointGroup from '@/api/job/jobPointGroup'
|
|
|
import * as PointApi from '@/api/dv/spm/index'
|
|
import * as PointApi from '@/api/dv/spm/index'
|
|
|
import * as JobApi from '@/api/job/index'
|
|
import * as JobApi from '@/api/job/index'
|
|
@@ -282,11 +329,34 @@ const activeName = ref('first')
|
|
|
const route = useRoute()
|
|
const route = useRoute()
|
|
|
const jobTypeOptions = ref([])
|
|
const jobTypeOptions = ref([])
|
|
|
const jobStatusOptions = ref([])
|
|
const jobStatusOptions = ref([])
|
|
|
|
|
+const currentStepTitle = ref('') // 当前步骤名称
|
|
|
|
|
+
|
|
|
// 表格数据
|
|
// 表格数据
|
|
|
const jobGroupList=ref([]) //获取作业分组信息
|
|
const jobGroupList=ref([]) //获取作业分组信息
|
|
|
const ticketPointsList = ref([]) // 隔离点数据
|
|
const ticketPointsList = ref([]) // 隔离点数据
|
|
|
const tableLayouts = ref([]) // 每组表格的布局
|
|
const tableLayouts = ref([]) // 每组表格的布局
|
|
|
|
|
|
|
|
|
|
+// 人员共锁
|
|
|
|
|
+const jobclockerList = ref([])
|
|
|
|
|
+const isShowclocker=ref(true) //是否展示人员共锁
|
|
|
|
|
+const waitLockUsers = ref([])
|
|
|
|
|
+const lockedUsers = ref([])
|
|
|
|
|
+const unlockPendingUsers = ref([])
|
|
|
|
|
+
|
|
|
|
|
+// 作业日志
|
|
|
|
|
+const joblogList = ref([])//原始作业日志数据列表
|
|
|
|
|
+const filteredJoblogList = ref([]) // 过滤后日志
|
|
|
|
|
+// 作业日志底部查询
|
|
|
|
|
+const isIndeterminate= ref(false)
|
|
|
|
|
+const checkedJoblogs = ref([])
|
|
|
|
|
+const jobLogtypes= ref([])
|
|
|
|
|
+const joblogsOptions= ref([]) // 只存所有 dictValue,用于全选逻辑,
|
|
|
|
|
+const checkAlljoblog =ref(true)
|
|
|
|
|
+const JoblogdialogVisible =ref(false)
|
|
|
|
|
+const dialogLog= ref('')
|
|
|
|
|
+const isJoblogVisible= ref(false)
|
|
|
|
|
+const joblogDialogVisible= ref(false)
|
|
|
|
|
+
|
|
|
|
|
|
|
|
// 初始化
|
|
// 初始化
|
|
|
onMounted(async () => {
|
|
onMounted(async () => {
|
|
@@ -295,9 +365,15 @@ onMounted(async () => {
|
|
|
jobStatusOptions.value = await getIntDictOptions(DICT_TYPE.TICKET_STATUS)
|
|
jobStatusOptions.value = await getIntDictOptions(DICT_TYPE.TICKET_STATUS)
|
|
|
await fetchAllGroupsAndPoints() //获取所有分组和点位 来渲染SOP的首页
|
|
await fetchAllGroupsAndPoints() //获取所有分组和点位 来渲染SOP的首页
|
|
|
await onModeContent()
|
|
await onModeContent()
|
|
|
|
|
+ window.addEventListener('resize', onResize)
|
|
|
|
|
+ await onResize()
|
|
|
|
|
+})
|
|
|
|
|
+onBeforeUnmount(() => {
|
|
|
|
|
+ window.removeEventListener('resize', onResize)
|
|
|
})
|
|
})
|
|
|
console.log(route.query.id, '是否传递成功')
|
|
console.log(route.query.id, '是否传递成功')
|
|
|
|
|
|
|
|
|
|
+// 基础信息
|
|
|
const ticketTypeLabel = computed(() => {
|
|
const ticketTypeLabel = computed(() => {
|
|
|
const match = jobTypeOptions.value.find(item => item.value == JobForm.ticketType)
|
|
const match = jobTypeOptions.value.find(item => item.value == JobForm.ticketType)
|
|
|
return match ? match.label : JobForm.ticketType
|
|
return match ? match.label : JobForm.ticketType
|
|
@@ -308,39 +384,60 @@ const ticketStatusLabel = computed(() => {
|
|
|
return match ? match.label : JobForm.ticketStatus
|
|
return match ? match.label : JobForm.ticketStatus
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
-// 获取详情
|
|
|
|
|
-// const getDetail = async () => {
|
|
|
|
|
-// // 获取sop信息
|
|
|
|
|
-// const JobData = await JobApi.selectJobTicketById(route.query.id)
|
|
|
|
|
-// // 将JobData的数据复制给JobForm
|
|
|
|
|
-// Object.assign(JobForm, JobData)
|
|
|
|
|
-// jobGroupList.value=JobForm.ticketGroupList
|
|
|
|
|
-// tableData.value = JobForm.ticketPointsList
|
|
|
|
|
-// console.log(JobData,'数据有哪些',tableData.value,'隔离点数据',jobGroupList.value,'分组信息')
|
|
|
|
|
-// }
|
|
|
|
|
-
|
|
|
|
|
// 初始化详情数据
|
|
// 初始化详情数据
|
|
|
const getDetail = async () => {
|
|
const getDetail = async () => {
|
|
|
const JobData = await JobApi.selectJobTicketById(route.query.id)
|
|
const JobData = await JobApi.selectJobTicketById(route.query.id)
|
|
|
// 将JobData的数据复制给JobForm
|
|
// 将JobData的数据复制给JobForm
|
|
|
Object.assign(JobForm, JobData)
|
|
Object.assign(JobForm, JobData)
|
|
|
-
|
|
|
|
|
|
|
+ // 点位锁定
|
|
|
jobGroupList.value = JobForm.ticketGroupList
|
|
jobGroupList.value = JobForm.ticketGroupList
|
|
|
- ticketPointsList.value = JobForm.ticketPointsList
|
|
|
|
|
|
|
+ ticketPointsList.value = JobForm.ticketGroupList.ticketPointsRespVOList
|
|
|
console.log(JobData,'数据有哪些',ticketPointsList.value,'隔离点数据',jobGroupList.value,'分组信息')
|
|
console.log(JobData,'数据有哪些',ticketPointsList.value,'隔离点数据',jobGroupList.value,'分组信息')
|
|
|
// 初始化每个表格的布局设置为 'fixed'
|
|
// 初始化每个表格的布局设置为 'fixed'
|
|
|
tableLayouts.value = jobGroupList.value.map(() => 'fixed')
|
|
tableLayouts.value = jobGroupList.value.map(() => 'fixed')
|
|
|
-}
|
|
|
|
|
|
|
|
|
|
-// 获取某个分组下的隔离点
|
|
|
|
|
-const getPointsByGroupId = (groupId) => {
|
|
|
|
|
- return ticketPointsList.value.filter((point) => point.groupId === groupId)
|
|
|
|
|
|
|
+// 人员共锁
|
|
|
|
|
+ jobclockerList.value = JobForm.ticketUserList
|
|
|
|
|
+ // 判断是否存在共锁人
|
|
|
|
|
+ isShowclocker.value = jobclockerList.value.some(item => item.userRole === 'jtcolocker')
|
|
|
|
|
+ updateUserGroups() // 分类数据
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
+// 作业流程绘制
|
|
|
|
|
+const scrollContainer = ref(null)
|
|
|
|
|
|
|
|
|
|
+const scrollToCurrentNode = () => {
|
|
|
|
|
+ nextTick(() => {
|
|
|
|
|
+ if (!scrollContainer.value) return
|
|
|
|
|
+
|
|
|
|
|
+ const firstZeroIndex = nodes.value.findIndex(n => n.data.stepStatus === 0)
|
|
|
|
|
+ if (firstZeroIndex === -1) return
|
|
|
|
|
+
|
|
|
|
|
+ const currentNodeId = nodes.value[firstZeroIndex].id
|
|
|
|
|
+ const currentNodeEl = document.getElementById(currentNodeId)
|
|
|
|
|
+ if (!currentNodeEl) return
|
|
|
|
|
+
|
|
|
|
|
+ // 计算滚动,让当前节点居中
|
|
|
|
|
+ const container = scrollContainer.value
|
|
|
|
|
+ const containerRect = container.getBoundingClientRect()
|
|
|
|
|
+ const nodeRect = currentNodeEl.getBoundingClientRect()
|
|
|
|
|
+
|
|
|
|
|
+ const offset = nodeRect.left - containerRect.left - (containerRect.width / 2) + (nodeRect.width / 2)
|
|
|
|
|
+
|
|
|
|
|
+ container.scrollBy({
|
|
|
|
|
+ left: offset,
|
|
|
|
|
+ behavior: 'smooth'
|
|
|
|
|
+ })
|
|
|
|
|
+ })
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const onResize = () => {
|
|
|
|
|
+ if (window.innerWidth <= 600) {
|
|
|
|
|
+ scrollToCurrentNode()
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
-// 作业流程绘制
|
|
|
|
|
const nodes = ref([]) //储存节点
|
|
const nodes = ref([]) //储存节点
|
|
|
const edges = ref([]) // 存储连接线
|
|
const edges = ref([]) // 存储连接线
|
|
|
const { addNodes, addEdges, setEdges, setNodes } = useVueFlow()
|
|
const { addNodes, addEdges, setEdges, setNodes } = useVueFlow()
|
|
@@ -363,27 +460,41 @@ const clearCanvasProperly = async () => {
|
|
|
}
|
|
}
|
|
|
// 初始化数据渲染节点 - 修正版本
|
|
// 初始化数据渲染节点 - 修正版本
|
|
|
const renderNodesFromData = (data) => {
|
|
const renderNodesFromData = (data) => {
|
|
|
- // 清空现有节点
|
|
|
|
|
nodes.value = []
|
|
nodes.value = []
|
|
|
|
|
|
|
|
|
|
+ const firstZeroIndex = data.findIndex(item => item.stepStatus === 0)
|
|
|
|
|
+ if (firstZeroIndex !== -1) {
|
|
|
|
|
+ currentStepTitle.value = data[firstZeroIndex].stepTitleShort || '无标题'
|
|
|
|
|
+ } else {
|
|
|
|
|
+ currentStepTitle.value = ''
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
data.forEach((item, index) => {
|
|
data.forEach((item, index) => {
|
|
|
|
|
+ let backgroundColor = '#fff'
|
|
|
|
|
+ let isCurrent = false
|
|
|
|
|
+ if (item.stepStatus === 1) {
|
|
|
|
|
+ backgroundColor = '#a0e99b'
|
|
|
|
|
+ } else if (item.stepStatus === 0 && index === firstZeroIndex) {
|
|
|
|
|
+ backgroundColor = '#ffe58f'
|
|
|
|
|
+ isCurrent = true
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
const nodeId = `node-${item.id || Date.now() + index}`
|
|
const nodeId = `node-${item.id || Date.now() + index}`
|
|
|
|
|
|
|
|
- const newNode = {
|
|
|
|
|
|
|
+ nodes.value.push({
|
|
|
id: nodeId,
|
|
id: nodeId,
|
|
|
- position: {
|
|
|
|
|
- x: 100 + index * 200,
|
|
|
|
|
- y: 100
|
|
|
|
|
- },
|
|
|
|
|
|
|
+ position: { x: 100 + index * 200, y: 100 },
|
|
|
width: 100,
|
|
width: 100,
|
|
|
height: 150,
|
|
height: 150,
|
|
|
data: {
|
|
data: {
|
|
|
|
|
+ ...item,
|
|
|
stepIcon: item.stepIcon,
|
|
stepIcon: item.stepIcon,
|
|
|
stepTitleShort: item.stepTitleShort,
|
|
stepTitleShort: item.stepTitleShort,
|
|
|
- stepIndex: item.stepIndex || index + 1, // 使用 stepIndex
|
|
|
|
|
- index: item.stepIndex || index + 1, // 保持兼容性
|
|
|
|
|
- // 保存完整的数据用于表单编辑
|
|
|
|
|
- stepData: item
|
|
|
|
|
|
|
+ stepIndex: item.stepIndex || index + 1,
|
|
|
|
|
+ index: item.stepIndex || index + 1,
|
|
|
|
|
+ stepData: item,
|
|
|
|
|
+ bgColor: backgroundColor,
|
|
|
|
|
+ isCurrent
|
|
|
},
|
|
},
|
|
|
style: {
|
|
style: {
|
|
|
width: '130px',
|
|
width: '130px',
|
|
@@ -396,16 +507,71 @@ const renderNodesFromData = (data) => {
|
|
|
alignItems: 'center',
|
|
alignItems: 'center',
|
|
|
justifyContent: 'space-between',
|
|
justifyContent: 'space-between',
|
|
|
padding: '10px',
|
|
padding: '10px',
|
|
|
- backgroundColor: '#fff'
|
|
|
|
|
|
|
+ backgroundColor
|
|
|
},
|
|
},
|
|
|
- draggable: true
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- addNodes(newNode)
|
|
|
|
|
- nodes.value.push(newNode)
|
|
|
|
|
|
|
+ draggable: false //设置是否可以拖动生成的图
|
|
|
|
|
+ })
|
|
|
})
|
|
})
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+// const renderNodesFromData = (data) => {
|
|
|
|
|
+// nodes.value = []
|
|
|
|
|
+//
|
|
|
|
|
+// const firstZeroIndex = data.findIndex(item => item.stepStatus === 0)
|
|
|
|
|
+//
|
|
|
|
|
+// // 设置当前步骤标题
|
|
|
|
|
+// if (firstZeroIndex !== -1) {
|
|
|
|
|
+// currentStepTitle.value = data[firstZeroIndex].stepTitleShort
|
|
|
|
|
+// } else {
|
|
|
|
|
+// currentStepTitle.value = '' // 或者显示“全部完成”之类的
|
|
|
|
|
+// }
|
|
|
|
|
+//
|
|
|
|
|
+// data.forEach((item, index) => {
|
|
|
|
|
+// const nodeId = `node-${item.id || Date.now() + index}`
|
|
|
|
|
+//
|
|
|
|
|
+// let backgroundColor = '#fff'
|
|
|
|
|
+// if (item.stepStatus === 1) {
|
|
|
|
|
+// backgroundColor = '#a0e99b'
|
|
|
|
|
+// } else if (item.stepStatus === 0 && index === firstZeroIndex) {
|
|
|
|
|
+// backgroundColor = '#ffe58f'
|
|
|
|
|
+// }
|
|
|
|
|
+//
|
|
|
|
|
+// const newNode = {
|
|
|
|
|
+// id: nodeId,
|
|
|
|
|
+// position: {
|
|
|
|
|
+// x: 100 + index * 200,
|
|
|
|
|
+// y: 100
|
|
|
|
|
+// },
|
|
|
|
|
+// width: 100,
|
|
|
|
|
+// height: 150,
|
|
|
|
|
+// data: {
|
|
|
|
|
+// stepIcon: item.stepIcon,
|
|
|
|
|
+// stepTitleShort: item.stepTitleShort,
|
|
|
|
|
+// stepIndex: item.stepIndex || index + 1,
|
|
|
|
|
+// index: item.stepIndex || index + 1,
|
|
|
|
|
+// stepData: item
|
|
|
|
|
+// },
|
|
|
|
|
+// style: {
|
|
|
|
|
+// width: '130px',
|
|
|
|
|
+// height: '180px',
|
|
|
|
|
+// borderRadius: '12px',
|
|
|
|
|
+// border: '1px solid #999',
|
|
|
|
|
+// textAlign: 'center',
|
|
|
|
|
+// display: 'flex',
|
|
|
|
|
+// flexDirection: 'column',
|
|
|
|
|
+// alignItems: 'center',
|
|
|
|
|
+// justifyContent: 'space-between',
|
|
|
|
|
+// padding: '10px',
|
|
|
|
|
+// backgroundColor
|
|
|
|
|
+// },
|
|
|
|
|
+// draggable: true
|
|
|
|
|
+// }
|
|
|
|
|
+//
|
|
|
|
|
+// addNodes(newNode)
|
|
|
|
|
+// nodes.value.push(newNode)
|
|
|
|
|
+// })
|
|
|
|
|
+// }
|
|
|
|
|
+
|
|
|
// 渲染连接线 - 新增函数
|
|
// 渲染连接线 - 新增函数
|
|
|
const renderEdgesFromData = (data) => {
|
|
const renderEdgesFromData = (data) => {
|
|
|
// 清空现有连接线
|
|
// 清空现有连接线
|
|
@@ -443,8 +609,30 @@ const renderEdgesFromData = (data) => {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-const handleClick = (tab: TabsPaneContext, event: Event) => {
|
|
|
|
|
|
|
+// 底部tabbar点击事件
|
|
|
|
|
+const handleClick = async (tab: TabsPaneContext, event: Event) => {
|
|
|
console.log(tab, event)
|
|
console.log(tab, event)
|
|
|
|
|
+ // console.log(activeName.value,'名称')
|
|
|
|
|
+ console.log(tab.paneName, '当前点击的 tab 名称')
|
|
|
|
|
+
|
|
|
|
|
+ if(tab.paneName === 'fourth') {
|
|
|
|
|
+ console.log('作业日志')
|
|
|
|
|
+ const dictRes= await getIntDictOptions(DICT_TYPE.JOB_LOG_TYPE)
|
|
|
|
|
+ console.log(dictRes,'接口调用是否成功')
|
|
|
|
|
+ const dicts = dictRes.map(item => ({
|
|
|
|
|
+ label: item.label,
|
|
|
|
|
+ value: String(item.value), // 确保是字符串类型
|
|
|
|
|
+ }));
|
|
|
|
|
+
|
|
|
|
|
+ jobLogtypes.value = dicts;
|
|
|
|
|
+ joblogsOptions.value = dicts.map(item => item.value);
|
|
|
|
|
+ checkedJoblogs.value = [...joblogsOptions.value]; // 初始化全选
|
|
|
|
|
+ console.log(checkAlljoblog.value,'选中状态');
|
|
|
|
|
+ checkAlljoblog.value = true;
|
|
|
|
|
+ console.log(checkAlljoblog.value,'是否选中');
|
|
|
|
|
+ isIndeterminate.value = false;
|
|
|
|
|
+ filterJoblogs();
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//ticketType改变函数
|
|
//ticketType改变函数
|
|
@@ -532,6 +720,11 @@ const resolvedGroupedPoints = computed(() => {
|
|
|
return result
|
|
return result
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
|
|
+
|
|
|
|
|
+// 获取某个分组下的隔离点
|
|
|
|
|
+// const getPointsByGroupId = (groupId) => {
|
|
|
|
|
+// return ticketPointsList.value.filter((point) => point.groupId === groupId)
|
|
|
|
|
+// }
|
|
|
// 从 JobForm.sopUserList 中提取锁定人并按 groupId 分组
|
|
// 从 JobForm.sopUserList 中提取锁定人并按 groupId 分组
|
|
|
const groupedLockers = computed(() => {
|
|
const groupedLockers = computed(() => {
|
|
|
const lockerUsers =
|
|
const lockerUsers =
|
|
@@ -554,6 +747,59 @@ const groupedLockers = computed(() => {
|
|
|
const coLockUsers = computed(() => {
|
|
const coLockUsers = computed(() => {
|
|
|
return JobForm.ticketUserList?.filter((u) => u.userRole === 'jtcolocker') || []
|
|
return JobForm.ticketUserList?.filter((u) => u.userRole === 'jtcolocker') || []
|
|
|
})
|
|
})
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+// 人员共锁
|
|
|
|
|
+const updateUserGroups = () => {
|
|
|
|
|
+ const allUsers = jobclockerList.value
|
|
|
|
|
+ console.log(allUsers,jobclockerList.value,'数据拿到了吗')
|
|
|
|
|
+
|
|
|
|
|
+ waitLockUsers.value = jobclockerList.value.filter(u => u.userRole == 'jtcolocker' && u.jobStatus == 0)
|
|
|
|
|
+ console.log(waitLockUsers.value,'待共锁')
|
|
|
|
|
+ lockedUsers.value = allUsers.filter(u => u.userRole === 'jtcolocker' && u.jobStatus == 1)
|
|
|
|
|
+ unlockPendingUsers.value = allUsers.filter(u => u.userRole === 'jtcolocker' && u.jobStatus == 2)
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+// 作用日志
|
|
|
|
|
+// 作业日志页面数据展示样式分割
|
|
|
|
|
+const renderLogContent=(content)=> {
|
|
|
|
|
+ if (!content) return ''
|
|
|
|
|
+ // 第一步:先处理 <url>[label] 为 <img> + label
|
|
|
|
|
+ content = content.replace(/<([^>]+)>\[([^\]]+)\]/g, (match, url, label) => {
|
|
|
|
|
+ return `<img src="${url}" style="height: 25px; vertical-align: middle;"> <span style="color:#007BFF;margin: 0 3px">${label}</span>`
|
|
|
|
|
+ })
|
|
|
|
|
+ // 第二步:处理剩下的 [label],转成蓝色字体
|
|
|
|
|
+ content = content.replace(/\[([^\]]+)\]/g, '<span style="color:#007BFF;margin: 0 5px">$1</span>')
|
|
|
|
|
+ return content
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 作业日志底部筛选功能
|
|
|
|
|
+const handleCheckedJoblogsChange=(val)=> {
|
|
|
|
|
+ const checkedCount = val.length;
|
|
|
|
|
+ checkAlljoblog.value = checkedCount === joblogsOptions.value.length;
|
|
|
|
|
+ isIndeterminate.value = checkedCount > 0 && checkedCount < joblogsOptions.value.length;
|
|
|
|
|
+
|
|
|
|
|
+ filterJoblogs();
|
|
|
|
|
+}
|
|
|
|
|
+const handleCheckAllChange=(val)=> {
|
|
|
|
|
+ checkedJoblogs.value = val ? [...joblogsOptions.value] : [];
|
|
|
|
|
+ isIndeterminate.value = false;
|
|
|
|
|
+ filterJoblogs();
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 实际日志筛选逻辑
|
|
|
|
|
+const filterJoblogs = () => {
|
|
|
|
|
+ if (checkedJoblogs.value.includes('ALL')) {
|
|
|
|
|
+ filteredJoblogList.value = joblogList.value;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ filteredJoblogList.value = joblogList.value.filter(log =>
|
|
|
|
|
+ checkedJoblogs.value.includes(String(log.operationType))
|
|
|
|
|
+ );
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
</script>
|
|
</script>
|
|
|
|
|
|
|
|
<style scoped lang="scss">
|
|
<style scoped lang="scss">
|
|
@@ -605,31 +851,10 @@ const coLockUsers = computed(() => {
|
|
|
.processDetail {
|
|
.processDetail {
|
|
|
width: 100%;
|
|
width: 100%;
|
|
|
height: 300px;
|
|
height: 300px;
|
|
|
- overflow-y: auto;
|
|
|
|
|
|
|
+ overflow-x: auto;
|
|
|
//background: green;
|
|
//background: green;
|
|
|
}
|
|
}
|
|
|
-.custom-node {
|
|
|
|
|
- position: relative;
|
|
|
|
|
- width: 125px;
|
|
|
|
|
- height: 180px;
|
|
|
|
|
- background-color: #fff;
|
|
|
|
|
- border-radius: 12px;
|
|
|
|
|
- display: flex;
|
|
|
|
|
- flex-direction: column;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- justify-content: space-between;
|
|
|
|
|
- padding: 10px;
|
|
|
|
|
- box-sizing: border-box;
|
|
|
|
|
-}
|
|
|
|
|
|
|
|
|
|
-.node-content {
|
|
|
|
|
- width: 100%;
|
|
|
|
|
- height: 100%;
|
|
|
|
|
- display: flex;
|
|
|
|
|
- flex-direction: column;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- justify-content: space-between;
|
|
|
|
|
-}
|
|
|
|
|
.jobExecution {
|
|
.jobExecution {
|
|
|
width: 100%;
|
|
width: 100%;
|
|
|
height: 600px;
|
|
height: 600px;
|
|
@@ -656,6 +881,7 @@ const coLockUsers = computed(() => {
|
|
|
min-height: 300px;
|
|
min-height: 300px;
|
|
|
display: flex;
|
|
display: flex;
|
|
|
flex-wrap: wrap;
|
|
flex-wrap: wrap;
|
|
|
|
|
+ //background: #000;
|
|
|
}
|
|
}
|
|
|
.left_box {
|
|
.left_box {
|
|
|
width: 500px;
|
|
width: 500px;
|
|
@@ -684,7 +910,7 @@ const coLockUsers = computed(() => {
|
|
|
position: relative;
|
|
position: relative;
|
|
|
width: 125px;
|
|
width: 125px;
|
|
|
height: 180px;
|
|
height: 180px;
|
|
|
- background-color: #fff;
|
|
|
|
|
|
|
+ //background-color: #fff;
|
|
|
border-radius: 12px;
|
|
border-radius: 12px;
|
|
|
display: flex;
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
flex-direction: column;
|
|
@@ -830,6 +1056,7 @@ const coLockUsers = computed(() => {
|
|
|
.group-container-user {
|
|
.group-container-user {
|
|
|
display: flex;
|
|
display: flex;
|
|
|
flex-direction: row;
|
|
flex-direction: row;
|
|
|
|
|
+ flex-wrap: wrap;
|
|
|
gap: 16px;
|
|
gap: 16px;
|
|
|
overflow-x: auto;
|
|
overflow-x: auto;
|
|
|
padding-bottom: 10px;
|
|
padding-bottom: 10px;
|
|
@@ -842,7 +1069,7 @@ const coLockUsers = computed(() => {
|
|
|
border-radius: 8px;
|
|
border-radius: 8px;
|
|
|
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.08);
|
|
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.08);
|
|
|
padding: 12px;
|
|
padding: 12px;
|
|
|
- flex-shrink: 0;
|
|
|
|
|
|
|
+ //flex-shrink: 0;
|
|
|
border: 1px solid #eee;
|
|
border: 1px solid #eee;
|
|
|
display: flex;
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
flex-direction: column;
|
|
@@ -910,7 +1137,7 @@ const coLockUsers = computed(() => {
|
|
|
margin: 0 15px;
|
|
margin: 0 15px;
|
|
|
.user{
|
|
.user{
|
|
|
width: 100%;
|
|
width: 100%;
|
|
|
- height: 500px;
|
|
|
|
|
|
|
+ max-height: 500px;
|
|
|
overflow-y: auto;
|
|
overflow-y: auto;
|
|
|
overflow-x: hidden;
|
|
overflow-x: hidden;
|
|
|
display: flex;
|
|
display: flex;
|
|
@@ -921,16 +1148,24 @@ const coLockUsers = computed(() => {
|
|
|
.userItem{
|
|
.userItem{
|
|
|
width: 20%;
|
|
width: 20%;
|
|
|
height: 80px;
|
|
height: 80px;
|
|
|
- padding: 2px 13px;
|
|
|
|
|
|
|
+ padding: 2px 3px;
|
|
|
|
|
+ margin: 10px;
|
|
|
|
|
+ text-align: center;
|
|
|
//background: #000;
|
|
//background: #000;
|
|
|
box-sizing: border-box;
|
|
box-sizing: border-box;
|
|
|
img{
|
|
img{
|
|
|
width: 50px;
|
|
width: 50px;
|
|
|
height:50px;
|
|
height:50px;
|
|
|
}
|
|
}
|
|
|
- p{
|
|
|
|
|
|
|
+ p {
|
|
|
font-size: 16px;
|
|
font-size: 16px;
|
|
|
|
|
+ display: -webkit-box;
|
|
|
|
|
+ -webkit-line-clamp: 1; /* 最多显示1行 */
|
|
|
|
|
+ -webkit-box-orient: vertical;
|
|
|
|
|
+ overflow: hidden;
|
|
|
|
|
+ text-overflow: ellipsis;
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
@@ -938,7 +1173,41 @@ const coLockUsers = computed(() => {
|
|
|
margin-top: 15%;
|
|
margin-top: 15%;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
+ //作业日志
|
|
|
|
|
+ .joblogCon{
|
|
|
|
|
+ width: 98%;
|
|
|
|
|
+ height: 75%;
|
|
|
|
|
+ margin: auto;
|
|
|
|
|
+ //background: greenyellow;
|
|
|
|
|
+ .joblogTop{
|
|
|
|
|
+ width: 100%;
|
|
|
|
|
+ height: 490px;
|
|
|
|
|
+ margin: auto;
|
|
|
|
|
+ overflow-y: auto;
|
|
|
|
|
+ //background: cadetblue;
|
|
|
|
|
+ p{
|
|
|
|
|
+ font-size: 16px;
|
|
|
|
|
+ line-height: 20px;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ .bottomCheck{
|
|
|
|
|
+ width:100%;
|
|
|
|
|
+ height: 50px;
|
|
|
|
|
+ line-height: 50px;
|
|
|
|
|
+ font-size: 23px;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ padding: 5px 0;
|
|
|
|
|
+ margin: auto;
|
|
|
|
|
+ //background: forestgreen;
|
|
|
|
|
+
|
|
|
|
|
+ .big-checkbox {
|
|
|
|
|
+ font-size: 23px; /* 字体大小 */
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
|
|
|
|
|
+ }
|
|
|
/* 小屏幕下隐藏文字,只显示图标 */
|
|
/* 小屏幕下隐藏文字,只显示图标 */
|
|
|
@media (max-width: 768px) {
|
|
@media (max-width: 768px) {
|
|
|
.tab-label {
|
|
.tab-label {
|
|
@@ -959,10 +1228,32 @@ const coLockUsers = computed(() => {
|
|
|
display: none;
|
|
display: none;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
+ .group-card-user {
|
|
|
|
|
+ width: 160px;
|
|
|
|
|
+ min-height: 130px;
|
|
|
|
|
+
|
|
|
|
|
+ }
|
|
|
|
|
+ //点位数据
|
|
|
|
|
+ .group-box{
|
|
|
|
|
+ margin: 10px 5px;
|
|
|
|
|
+ }
|
|
|
|
|
+ // 人员共锁
|
|
|
|
|
+ .biglockBox {
|
|
|
|
|
+ overflow-y: auto;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ //background: #bf2020;
|
|
|
|
|
+ .mumberbox {
|
|
|
|
|
+ width: 95%;
|
|
|
|
|
+ margin: 15px 0;
|
|
|
|
|
+ }
|
|
|
|
|
+ .arrow {
|
|
|
|
|
+ transform: rotate(90deg); // 向右 → 向下
|
|
|
|
|
+ margin-top: 10px;
|
|
|
|
|
+ margin-bottom: 10px;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
- .group-card-user {
|
|
|
|
|
- width: 160px;
|
|
|
|
|
- min-height: 130px;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+
|
|
|
}
|
|
}
|
|
|
</style>
|
|
</style>
|