You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
besure_web/src/views/mes/taskManagement/index.vue

594 lines
19 KiB
Vue

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<template>
<ContentWrap>
<div class="task-management__header">
<div v-for="card in statusCards" :key="card.key" class="task-management__stat"
:class="{ 'is-active': currentStatusKey === card.key }" @click="changeStatus(card.key, card.taskType, card.enabled)">
<div class="task-management__stat-title">{{ card.label }}</div>
<div class="task-management__stat-value">{{ card.value }}</div>
</div>
</div>
<el-form
class="-mb-15px"
:model="queryParams"
ref="queryFormRef"
:inline="true"
min-label-width="68px"
>
<el-form-item :label="t('EquipmentManagement.TaskManagement.name')" prop="name">
<el-input
v-model="queryParams.name"
:placeholder="t('EquipmentManagement.TaskManagement.placeholderName')"
clearable
@keyup.enter="handleQuery"
class="!w-240px"
/>
</el-form-item>
<el-form-item :label="t('EquipmentManagement.TaskManagement.taskType')" prop="taskType">
<el-select
v-model="queryParams.taskType"
:placeholder="t('EquipmentManagement.TaskManagement.placeholderTaskType')"
clearable
class="!w-240px"
>
<el-option v-for="opt in taskTypeOptions" :key="opt.value" :label="opt.label" :value="opt.value" />
</el-select>
</el-form-item>
<el-form-item :label="t('EquipmentManagement.TaskManagement.projectForm')" prop="projectForm" v-show="showAllFilters">
<el-select
v-model="queryParams.projectForm"
multiple
filterable
clearable
:placeholder="t('EquipmentManagement.TaskManagement.placeholderProjectForm')"
class="!w-240px"
>
<el-option v-for="item in planOptions" :key="String(item.id)" :label="item.planName" :value="String(item.id)" />
</el-select>
</el-form-item>
<el-form-item :label="t('EquipmentManagement.TaskManagement.deviceList')" prop="deviceIds" v-show="showAllFilters">
<el-select
v-model="queryParams.deviceIds"
multiple
filterable
clearable
collapse-tags
collapse-tags-tooltip
:placeholder="t('EquipmentManagement.TaskManagement.placeholderDeviceList')"
class="!w-240px"
>
<el-option
v-for="item in deviceOptions"
:key="item.id"
:label="item.label"
:value="item.id"
/>
</el-select>
</el-form-item>
<el-form-item :label="t('EquipmentManagement.TaskManagement.operableUsers')" prop="operator" v-show="showAllFilters">
<el-select
v-model="queryParams.operator"
filterable
clearable
:placeholder="t('EquipmentManagement.TaskManagement.placeholderOperableUsers')"
class="!w-240px"
>
<el-option v-for="item in users" :key="String(item.id)" :label="item.nickname" :value="String(item.id)" />
</el-select>
</el-form-item>
<el-form-item :label="t('EquipmentManagement.TaskManagement.taskTime')" prop="taskTime" v-show="showAllFilters">
<el-date-picker
v-model="queryParams.taskTime"
value-format="YYYY-MM-DD HH:mm:ss"
type="datetime"
:placeholder="t('EquipmentManagement.TaskManagement.placeholderTaskTime')"
clearable
class="!w-240px"
/>
</el-form-item>
<el-form-item v-if="filterCount > 2">
<el-button type="text" class="text-primary" @click="toggleFilters">
<Icon :icon="showAllFilters ? 'ep:arrow-up' : 'ep:arrow-down'" class="mr-5px" />
{{ showAllFilters ? t('FactoryModeling.FactoryStructure.collapseText') : t('FactoryModeling.FactoryStructure.expandText') }}
</el-button>
</el-form-item>
<el-form-item>
<el-button @click="handleQuery">
<Icon icon="ep:search" class="mr-5px" />
{{ t('common.query') }}
</el-button>
<el-button @click="resetQuery">
<Icon icon="ep:refresh" class="mr-5px" />
{{ t('common.reset') }}
</el-button>
<el-button type="primary" plain @click="openForm('create')" v-hasPermi="['mes:task-management:create']">
<Icon icon="ep:plus" class="mr-5px" />
{{ t('action.add') }}
</el-button>
<el-button
type="success"
plain
@click="handleExport"
:loading="exportLoading"
v-hasPermi="['mes:task-management:export']"
>
<Icon icon="ep:download" class="mr-5px" />
{{ t('action.export') }}
</el-button>
</el-form-item>
</el-form>
</ContentWrap>
<ContentWrap>
<el-table
ref="tableRef"
v-loading="loading"
:data="list"
:stripe="true"
:show-overflow-tooltip="true"
:max-height="planTableMaxHeight"
@selection-change="handleSelectionChange"
>
<el-table-column type="selection" width="55" />
<el-table-column
type="index"
:label="t('EquipmentManagement.TaskManagement.index')"
align="center"
width="70"
/>
<el-table-column
:label="t('EquipmentManagement.TaskManagement.name')"
align="center"
prop="name"
min-width="200"
sortable />
<el-table-column
:label="t('EquipmentManagement.TaskManagement.deviceList')"
align="center"
prop="deviceList"
min-width="180"
>
<template #default="scope">
<span>{{ getDeviceNames(scope.row.deviceList) }}</span>
</template>
</el-table-column>
<el-table-column
:label="t('EquipmentManagement.TaskManagement.taskType')"
align="center"
prop="taskType"
width="90"
sortable>
<template #default="scope">
<el-tag v-if="scope.row.taskType === 1" type="primary">
{{ t('EquipmentManagement.TaskManagement.taskTypeInspect') }}
</el-tag>
<el-tag v-else-if="scope.row.taskType === 2" type="success">
{{ t('EquipmentManagement.TaskManagement.taskTypeMaintain') }}
</el-tag>
<span v-else>-</span>
</template>
</el-table-column>
<el-table-column
:label="t('EquipmentManagement.TaskManagement.projectForm')"
align="center"
prop="projectFormName"
min-width="300"
sortable />
<el-table-column
:label="t('EquipmentManagement.TaskManagement.startDate')"
align="center"
prop="startDate"
:formatter="dateFormatter2"
width="120"
sortable />
<el-table-column
:label="t('EquipmentManagement.TaskManagement.endDate')"
align="center"
prop="endDate"
:formatter="dateFormatter2"
width="120"
sortable />
<el-table-column
:label="t('EquipmentManagement.TaskManagement.cronExpression')"
align="center"
prop="cronExpression"
min-width="180"
/>
<el-table-column
:label="t('EquipmentManagement.TaskManagement.enabled')"
align="center"
prop="enabled"
width="110"
>
<template #default="scope">
<el-switch
:model-value="isTaskEnabled(scope.row)"
:before-change="() => handleBeforeEnabledChange(scope.row)"
@click.stop
@change="(val) => handleEnabledChange(scope.row, val)"
/>
</template>
</el-table-column>
<el-table-column
:label="t('EquipmentManagement.TaskManagement.creatorName')"
align="center"
prop="creatorName"
width="200"
sortable />
<el-table-column
:label="t('EquipmentManagement.TaskManagement.createTime')"
align="center"
prop="createTime"
:formatter="dateFormatter"
width="180"
sortable />
<el-table-column
:label="t('EquipmentManagement.TaskManagement.updateTime')"
align="center"
prop="updateTime"
:formatter="dateFormatter"
width="180"
sortable />
<el-table-column
:label="t('EquipmentManagement.TaskManagement.operate')"
fixed="right"
align="center"
min-width="230"
>
<template #default="scope">
<el-button
link
type="success"
:disabled="!isTaskEnabled(scope.row)"
@click="handleCreateTicket(scope.row)"
:loading="ticketLoadingId === scope.row.id"
>
{{ t('EquipmentManagement.TaskManagement.createWorkOrder') }}
</el-button>
<el-button
link
type="primary"
@click="openForm('update', scope.row)"
v-hasPermi="['mes:task-management:update']"
>
{{ t('action.update') }}
</el-button>
<el-button link type="danger" @click="handleDelete(scope.row.id)" v-hasPermi="['mes:task-management:delete']">
{{ t('action.delete') }}
</el-button>
</template>
</el-table-column>
</el-table>
<Pagination
:total="total"
v-model:page="queryParams.pageNo"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
</ContentWrap>
<TaskManagementForm ref="formRef" @success="getList" />
</template>
<script setup lang="ts">
import { ElMessageBox } from 'element-plus'
import { dateFormatter, dateFormatter2 } from '@/utils/formatTime'
import download from '@/utils/download'
import { TaskManagementApi, TaskManagementVO } from '@/api/mes/taskManagement'
import { DeviceLedgerApi, DeviceLedgerVO } from '@/api/mes/deviceledger'
import { PlanMaintenanceApi } from '@/api/mes/planmaintenance'
import { getSimpleUserList, UserVO } from '@/api/system/user'
import TaskManagementForm from './TaskManagementForm.vue'
defineOptions({ name: 'TaskManagement' })
const message = useMessage()
const { t } = useI18n()
const taskTypeOptions = [
{ label: t('EquipmentManagement.TaskManagement.taskTypeInspect'), value: 1 },
{ label: t('EquipmentManagement.TaskManagement.taskTypeMaintain'), value: 2 }
]
const loading = ref(true)
const list = ref<TaskManagementVO[]>([])
const total = ref(0)
const tableRef = ref()
const planTableMaxHeight = ref(520)
const planPaginationRef = ref<HTMLElement>()
const exportLoading = ref(false)
const selectedIds = ref<number[]>([])
const updatePlanTableMaxHeight = async () => {
await nextTick()
const tableTop = tableRef.value?.$el?.getBoundingClientRect().top ?? 0
const paginationHeight = planPaginationRef.value?.getBoundingClientRect().height ?? 72
const pageBottomGap = 56
const availableHeight = window.innerHeight - tableTop - paginationHeight - pageBottomGap
planTableMaxHeight.value = Math.min(window.innerHeight, Math.max(240, Math.floor(availableHeight)))
}
type PlanOption = {
id: number | string
planName: string
}
const planOptions = ref<PlanOption[]>([])
const deviceOptions = ref<{ id: string | number; label: string; raw?: DeviceLedgerVO }[]>([])
const users = ref<UserVO[]>([])
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
name: undefined as string | undefined,
taskType: undefined as number | undefined,
projectForm: [] as string[],
deviceIds: [] as string[],
operator: undefined as string | undefined,
taskTime: undefined as string | undefined,
enabled: undefined as boolean | undefined
})
const queryFormRef = ref()
const currentStatusKey = ref('all')
const counters = reactive({
allCount: 0,
inspectCount: 0,
maintainCount: 0,
enabledCount: 0,
disabledCount: 0
})
const statusCards = computed(() => [
{ key: 'all', label: t('EquipmentManagement.TaskManagement.all'), value: counters.allCount, taskType: undefined, enabled: undefined },
{ key: 'inspect', label: t('EquipmentManagement.TaskManagement.taskTypeInspect'), value: counters.inspectCount, taskType: 1, enabled: undefined },
{ key: 'maintain', label: t('EquipmentManagement.TaskManagement.taskTypeMaintain'), value: counters.maintainCount, taskType: 2, enabled: undefined },
{ key: 'enabled', label: t('EquipmentManagement.TaskManagement.enabled'), value: counters.enabledCount, taskType: undefined, enabled: true },
{ key: 'disabled', label: t('EquipmentManagement.TaskManagement.disabled'), value: counters.disabledCount, taskType: undefined, enabled: false }
])
const changeStatus = (key: string, taskType?: number, enabled?: boolean) => {
currentStatusKey.value = key
queryParams.taskType = taskType
queryParams.enabled = enabled
handleQuery()
}
const showAllFilters = ref(false)
const filterCount = 6
const toggleFilters = () => {
showAllFilters.value = !showAllFilters.value
}
const parseIdsValue = (value: any): string[] => {
if (!value) return []
if (Array.isArray(value)) return value.map((v) => String(v).trim()).filter(Boolean)
return String(value)
.split(',')
.map((v) => v.trim())
.filter(Boolean)
}
const toCommaSeparatedIds = (value: any): string | undefined => {
const ids = parseIdsValue(value)
return ids.length ? ids.join(',') : undefined
}
const ensurePlanOptionsLoaded = async () => {
const planRes = await PlanMaintenanceApi.getPlanMaintenancePage({ pageNo: 1, pageSize: 100 })
planOptions.value = (planRes?.list ?? []) as PlanOption[]
}
const ensureDeviceOptionsLoaded = async () => {
const deviceRes = await DeviceLedgerApi.getDeviceLedgerList()
const rows = (Array.isArray(deviceRes) ? deviceRes : deviceRes?.list ?? deviceRes?.data ?? []) as DeviceLedgerVO[]
deviceOptions.value = rows
.filter((item) => item?.id != null)
.map((item) => ({
id: item.id,
label: `${item.deviceCode ?? ''} ${item.deviceName ?? ''}`.trim() || String(item.id),
raw: item
}))
}
const ensureUserOptionsLoaded = async () => {
users.value = await getSimpleUserList()
}
const getDeviceNames = (value: any) => {
const ids = parseIdsValue(value)
if (!ids.length) return '-'
const names = ids
.map((id) => {
const option = deviceOptions.value.find((item) => String(item.id) === id)
return option?.raw?.deviceName ?? option?.label?.split(' ').slice(1).join(' ') ?? id
})
.filter(Boolean)
return names.length ? names.join('') : '-'
}
const buildQueryParams = () => {
return {
...queryParams,
projectForm: toCommaSeparatedIds(queryParams.projectForm)
}
}
const getList = async () => {
loading.value = true
try {
const data = await TaskManagementApi.getTaskManagementPage(buildQueryParams())
list.value = data.list
total.value = data.total
counters.allCount = Number(data?.allCount ?? data?.total ?? 0)
counters.inspectCount = Number(data?.inspectCount ?? 0)
counters.maintainCount = Number(data?.maintainCount ?? 0)
counters.enabledCount = Number(data?.enabledCount ?? 0)
counters.disabledCount = Number(data?.disabledCount ?? 0)
} finally {
loading.value = false
updatePlanTableMaxHeight()
}
}
const handleQuery = () => {
queryParams.pageNo = 1
getList()
}
const resetQuery = () => {
queryFormRef.value.resetFields()
currentStatusKey.value = 'all'
queryParams.taskType = undefined
queryParams.enabled = undefined
handleQuery()
}
const handleSelectionChange = (rows: TaskManagementVO[]) => {
selectedIds.value = rows
.map((r) => r.id)
.filter((id): id is number => typeof id === 'number')
}
const formRef = ref()
const openForm = (type: string, row?: TaskManagementVO) => {
formRef.value.open(type, row)
}
const isTaskEnabled = (row: TaskManagementVO) => {
const value = (row as any)?.enabled
if (typeof value === 'boolean') return value
const text = String(value ?? '').toLowerCase().trim()
return text === 'true' || text === '1' || text === 'yes'
}
const handleDelete = async (id?: number) => {
if (!id) return
try {
await message.delConfirm()
await TaskManagementApi.deleteTaskManagement(String(id))
message.success(t('common.delSuccess'))
await getList()
} catch { }
}
const ticketLoadingId = ref<number | undefined>(undefined)
const handleCreateTicket = async (row?: TaskManagementVO) => {
if (!row?.id) return
if (!isTaskEnabled(row)) {
message.warning(t('EquipmentManagement.TaskManagement.createTicketDisabledTip'))
return
}
try {
await message.confirm(
t('EquipmentManagement.TaskManagement.createTicketConfirm'),
t('common.reminder')
)
} catch {
return
}
ticketLoadingId.value = row.id
try {
await TaskManagementApi.createTaskManagementTicket(row.id)
message.success(t('EquipmentManagement.TaskManagement.createTicketSuccess'))
} catch {
message.error(t('EquipmentManagement.TaskManagement.createTicketFail'))
} finally {
ticketLoadingId.value = undefined
}
}
const handleBeforeEnabledChange = async (row: TaskManagementVO) => {
const isEnabled = isTaskEnabled(row)
if (isEnabled) return true
const cronExpression = (row as any)?.cronExpression
if (typeof cronExpression === 'string' && cronExpression.trim()) return true
await ElMessageBox.alert(t('EquipmentManagement.TaskManagement.selectCronBeforeEnable'), t('EquipmentManagement.TaskManagement.cronAlertTitle'), {
type: 'warning',
confirmButtonText: t('EquipmentManagement.TaskManagement.cronAlertConfirm'),
closeOnClickModal: false,
closeOnPressEscape: false
})
return false
}
const handleEnabledChange = async (row: TaskManagementVO, value: string | number | boolean) => {
if (!row.id) return
const enabled = value === true || value === 'true' || value === '1' || value === 1
const oldEnabled = row.enabled
row.enabled = enabled
try {
await TaskManagementApi.updateTaskManagementEnabled(String(row.id), enabled ? 'true' : 'false')
message.success(t('EquipmentManagement.TaskManagement.updateEnabledSuccess'))
} catch {
row.enabled = oldEnabled
message.error(t('EquipmentManagement.TaskManagement.updateEnabledFail'))
}
}
const handleExport = async () => {
try {
await message.exportConfirm()
exportLoading.value = true
const params: Record<string, any> = buildQueryParams()
if (selectedIds.value.length > 0) {
params.ids = selectedIds.value.join(',')
}
const data = await TaskManagementApi.exportTaskManagement(params)
download.excel(data, t('EquipmentManagement.TaskManagement.exportFilename'))
} catch {
} finally {
exportLoading.value = false
}
}
onMounted(async () => {
await Promise.all([ensurePlanOptionsLoaded(), ensureDeviceOptionsLoaded(), ensureUserOptionsLoaded(), getList()])
updatePlanTableMaxHeight()
window.addEventListener('resize', updatePlanTableMaxHeight)
})
onBeforeUnmount(() => {
window.removeEventListener('resize', updatePlanTableMaxHeight)
})
</script>
<style scoped lang="scss">
.task-management__header {
display: grid;
grid-template-columns: repeat(5, minmax(0, 1fr));
gap: 12px;
width: 100%;
margin-bottom: 16px;
}
.task-management__stat {
padding: 16px 18px;
border: 1px solid var(--el-border-color-light);
border-radius: 12px;
cursor: pointer;
transition: box-shadow 0.2s ease;
background: #fff;
&:hover {
box-shadow: 0 10px 24px rgba(64, 158, 255, 0.12);
}
&.is-active {
border-color: var(--el-color-primary);
box-shadow: 0 10px 24px rgba(64, 158, 255, 0.12);
}
}
.task-management__stat-title {
color: var(--el-text-color-regular);
font-size: 14px;
}
.task-management__stat-value {
margin-top: 8px;
color: var(--el-text-color-primary);
font-size: 28px;
font-weight: 700;
line-height: 1;
}
</style>