小程序工单设备代码传入

This commit is contained in:
2025-12-31 12:06:21 +08:00
parent 1c207c2439
commit 1ef1b32f5f
5 changed files with 193 additions and 125 deletions

View File

@@ -1,5 +1,10 @@
// 全局EL分页组件样式
// ==================== 品牌色变量 ====================
$brand-color: #0055AA;
$brand-color-light: #EBF5FF;
$brand-color-hover: #004488;
.content-header {
display: flex;
align-items: center;
@@ -190,11 +195,32 @@
align-items: center;
gap: 10px;
}
// 分页样式
.table-pagination {
margin-top: 12px;
padding: 16px 20px;
display: flex;
justify-content: flex-end;
border-top: 1px solid #f1f5f9;
background: #fff;
}
// 全局分页样式(不需要 :deep因为这是全局样式
.el-pagination {
.el-pager {
li {
border-radius: 6px;
&.is-active {
background: $brand-color;
color: #fff;
}
}
}
.btn-prev,
.btn-next {
border-radius: 6px;
}
}
.file-name-cell {

View File

@@ -1,5 +1,5 @@
<template>
<AdminLayout title="工单日志" info="查看工单操作记录">
<AdminLayout title="工单日志" info="查看工单流程处理记录">
<template #action>
<el-button type="primary" @click="exportLogs">
<el-icon><Download /></el-icon>
@@ -11,46 +11,48 @@
<!-- 筛选区域 -->
<el-card class="filter-card">
<div class="ticket-filters">
<el-date-picker v-model="dateRange" type="daterange" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期" style="width: 280px;" />
<el-date-picker v-model="dateRange" type="daterange" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期" value-format="YYYY-MM-DD" style="width: 280px;" @change="handleSearch" />
<div class="filter-right">
<el-select v-model="operationFilter" placeholder="操作类型" clearable style="width: 140px;">
<el-option label="创建" value="create" />
<el-option label="更新" value="update" />
<el-option label="指派" value="assign" />
<el-option label="完成" value="complete" />
<el-option label="关闭" value="close" />
<el-select v-model="filter.action" placeholder="操作类型" clearable style="width: 140px;" @change="handleSearch">
<el-option v-for="item in actionOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
<el-select v-model="operatorFilter" placeholder="操作人" clearable style="width: 120px;">
<el-option label="王五" value="wangwu" />
<el-option label="赵六" value="zhaoliu" />
<el-option label="孙七" value="sunqi" />
</el-select>
<el-input v-model="searchKeyword" placeholder="搜索工单号/内容" style="width: 200px;" :prefix-icon="Search" clearable />
<el-input v-model="filter.workcaseId" placeholder="搜索工单ID" style="width: 200px;" :prefix-icon="Search" clearable @keyup.enter="handleSearch" @clear="handleSearch" />
<el-button type="primary" @click="handleSearch">搜索</el-button>
</div>
</div>
</el-card>
<!-- 日志列表 -->
<el-card>
<el-table :data="filteredLogs" style="width: 100%">
<el-table-column prop="logId" label="日志ID" width="120">
<el-table :data="processLogs" style="width: 100%" v-loading="loading">
<el-table-column prop="processId" label="流程ID" width="180">
<template #default="{ row }">
<span style="color: #409eff; font-weight: 500;">{{ row.logId }}</span>
<span style="color: #409eff; font-weight: 500;">{{ row.processId }}</span>
</template>
</el-table-column>
<el-table-column prop="ticketNo" label="工单" width="120" />
<el-table-column prop="operation" label="操作类型" width="100">
<el-table-column prop="workcaseId" label="工单ID" width="180" />
<el-table-column prop="action" label="操作类型" width="100">
<template #default="{ row }">
<el-tag :type="getOperationType(row.operation)" size="small">
{{ row.operationName }}
<el-tag :type="getActionTagType(row.action)" size="small">
{{ getActionLabel(row.action) }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="operator" label="操作" width="100" />
<el-table-column prop="content" label="操作内容" min-width="200" />
<el-table-column prop="operationTime" label="操作时间" width="160" />
<el-table-column prop="ipAddress" label="IP地址" width="130" />
<el-table-column prop="message" label="操作内容" min-width="200" show-overflow-tooltip />
<el-table-column prop="processor" label="处理人" width="120">
<template #default="{ row }">
{{ row.processor || '-' }}
</template>
</el-table-column>
<el-table-column prop="files" label="附件" width="80">
<template #default="{ row }">
<el-tag v-if="row.files?.length" size="small" type="info">{{ row.files.length }}</el-tag>
<span v-else>-</span>
</template>
</el-table-column>
<el-table-column prop="creator" label="操作人" width="120" />
<el-table-column prop="createTime" label="操作时间" width="170" />
<el-table-column label="操作" width="120" fixed="right">
<template #default="{ row }">
<el-button type="primary" link size="small" @click="viewDetail(row)">详情</el-button>
@@ -59,99 +61,154 @@
</el-table>
<div class="table-pagination">
<el-pagination v-model:current-page="currentPage" :page-size="10" :total="logs.length" layout="total, prev, pager, next" />
<el-pagination
v-model:current-page="pagination.current"
v-model:page-size="pagination.size"
:total="pagination.total"
:page-sizes="[10, 20, 50, 100]"
layout="total, sizes, prev, pager, next"
@size-change="handleSearch"
@current-change="handleSearch"
/>
</div>
</el-card>
</div>
<!-- 日志详情弹窗 -->
<el-dialog v-model="showDetailDialog" title="日志详情" width="600px">
<el-form v-if="selectedLog" label-width="100px">
<el-form-item label="日志ID">
<span>{{ selectedLog.logId }}</span>
</el-form-item>
<el-form-item label="工单号">
<span>{{ selectedLog.ticketNo }}</span>
</el-form-item>
<el-form-item label="操作类型">
<el-tag :type="getOperationType(selectedLog.operation)">{{ selectedLog.operationName }}</el-tag>
</el-form-item>
<el-form-item label="操作人">
<span>{{ selectedLog.operator }}</span>
</el-form-item>
<el-form-item label="操作内容">
<span>{{ selectedLog.content }}</span>
</el-form-item>
<el-form-item label="操作时间">
<span>{{ selectedLog.operationTime }}</span>
</el-form-item>
<el-form-item label="IP地址">
<span>{{ selectedLog.ipAddress }}</span>
</el-form-item>
</el-form>
<el-dialog v-model="showDetailDialog" title="流程详情" width="600px">
<el-descriptions v-if="selectedLog" :column="1" border>
<el-descriptions-item label="流程ID">{{ selectedLog.processId }}</el-descriptions-item>
<el-descriptions-item label="工单ID">{{ selectedLog.workcaseId }}</el-descriptions-item>
<el-descriptions-item label="操作类型">
<el-tag :type="getActionTagType(selectedLog.action)">{{ getActionLabel(selectedLog.action) }}</el-tag>
</el-descriptions-item>
<el-descriptions-item label="操作内容">{{ selectedLog.message || '-' }}</el-descriptions-item>
<el-descriptions-item label="处理人">{{ selectedLog.processor || '-' }}</el-descriptions-item>
<el-descriptions-item label="操作人">{{ selectedLog.creator }}</el-descriptions-item>
<el-descriptions-item label="操作时间">{{ selectedLog.createTime }}</el-descriptions-item>
<el-descriptions-item v-if="selectedLog.files?.length" label="附件">
<div class="file-list">
<el-tag v-for="(file, index) in selectedLog.files" :key="index" size="small" style="margin-right: 8px; margin-bottom: 4px;">
{{ file }}
</el-tag>
</div>
</el-descriptions-item>
</el-descriptions>
</el-dialog>
</AdminLayout>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue'
import { ref, reactive, onMounted } from 'vue'
import AdminLayout from '@/views/admin/AdminLayout.vue'
import { Download, Search } from 'lucide-vue-next'
import { ElMessage } from 'element-plus'
import { workcaseAPI } from '@/api/workcase'
import type { TbWorkcaseProcessDTO } from '@/types/workcase'
import type { PageRequest, PageParam } from 'shared/types'
const dateRange = ref<[Date, Date] | null>(null)
const operationFilter = ref('')
const operatorFilter = ref('')
const searchKeyword = ref('')
const currentPage = ref(1)
// 操作类型选项
const actionOptions = [
{ value: 'info', label: '记录' },
{ value: 'assign', label: '指派' },
{ value: 'redeploy', label: '转派' },
{ value: 'repeal', label: '撤销' },
{ value: 'finish', label: '完成' }
]
// 状态
const loading = ref(false)
const dateRange = ref<[string, string] | null>(null)
const showDetailDialog = ref(false)
const selectedLog = ref<any>(null)
const selectedLog = ref<TbWorkcaseProcessDTO | null>(null)
const processLogs = ref<TbWorkcaseProcessDTO[]>([])
const logs = ref([
{ logId: 'LOG001', ticketNo: 'TK001', operation: 'create', operationName: '创建', operator: '王五', content: '创建工单,客户反映设备显示屏不亮', operationTime: '2024-12-13 10:30', ipAddress: '192.168.1.100' },
{ logId: 'LOG002', ticketNo: 'TK001', operation: 'assign', operationName: '指派', operator: '赵六', content: '将工单指派给技术人员处理', operationTime: '2024-12-13 10:35', ipAddress: '192.168.1.101' },
{ logId: 'LOG003', ticketNo: 'TK002', operation: 'create', operationName: '创建', operator: '孙七', content: '创建工单,客户反映机械故障', operationTime: '2024-12-13 09:15', ipAddress: '192.168.1.102' },
{ logId: 'LOG004', ticketNo: 'TK002', operation: 'update', operationName: '更新', operator: '王五', content: '更新工单状态为处理中', operationTime: '2024-12-13 09:45', ipAddress: '192.168.1.100' },
{ logId: 'LOG005', ticketNo: 'TK003', operation: 'complete', operationName: '完成', operator: '赵六', content: '工单处理完成,客户已确认', operationTime: '2024-12-12 14:20', ipAddress: '192.168.1.101' }
])
const filteredLogs = computed(() => {
let result = logs.value
if (operationFilter.value) {
result = result.filter(l => l.operation === operationFilter.value)
}
if (operatorFilter.value) {
result = result.filter(l => l.operator === operatorFilter.value)
}
if (searchKeyword.value) {
const keyword = searchKeyword.value.toLowerCase()
result = result.filter(l =>
l.ticketNo.toLowerCase().includes(keyword) ||
l.content.toLowerCase().includes(keyword)
)
}
return result.slice((currentPage.value - 1) * 10, currentPage.value * 10)
// 筛选条件
const filter = reactive<TbWorkcaseProcessDTO>({
workcaseId: '',
action: undefined
})
const getOperationType = (operation: string) => {
// 分页
const pagination = reactive({
current: 1,
size: 10,
total: 0
})
// 获取操作类型标签
const getActionLabel = (action?: string) => {
const map: Record<string, string> = {
create: 'success',
update: 'info',
assign: 'warning',
complete: 'success',
close: 'danger'
info: '记录',
assign: '指派',
redeploy: '转派',
repeal: '撤销',
finish: '完成'
}
return map[operation] || 'info'
return map[action || ''] || action || '-'
}
const viewDetail = (row: any) => {
// 获取操作类型标签样式
const getActionTagType = (action?: string) => {
const map: Record<string, string> = {
info: 'info',
assign: 'warning',
redeploy: '',
repeal: 'danger',
finish: 'success'
}
return map[action || ''] || 'info'
}
// 查询数据
const handleSearch = async () => {
loading.value = true
try {
const filterData: TbWorkcaseProcessDTO = { ...filter }
// 日期范围
if (dateRange.value) {
filterData.startTime = dateRange.value[0]
filterData.endTime = dateRange.value[1]
}
const pageParam: PageParam = {
pageNumber: pagination.current,
pageSize: pagination.size
}
const pageRequest: PageRequest<TbWorkcaseProcessDTO> = {
filter: filterData,
pageParam
}
const res = await workcaseAPI.getWorkcaseProcessPage(pageRequest)
if (res.success) {
processLogs.value = res.dataList || res.pageDomain?.dataList || []
pagination.total = res.pageDomain?.pageParam?.totalElements || 0
}
} catch (error) {
console.error('查询工单流程失败:', error)
ElMessage.error('查询失败')
} finally {
loading.value = false
}
}
// 查看详情
const viewDetail = (row: TbWorkcaseProcessDTO) => {
selectedLog.value = row
showDetailDialog.value = true
}
// 导出日志
const exportLogs = () => {
ElMessage.success('日志导出功')
ElMessage.success('日志导出功能开发中')
}
onMounted(() => {
handleSearch()
})
</script>
<style lang="scss" scoped>
@@ -162,4 +219,9 @@ const exportLogs = () => {
flex-direction: column;
gap: 16px;
}
</style>
.file-list {
display: flex;
flex-wrap: wrap;
}
</style>

View File

@@ -247,32 +247,6 @@ $brand-color-hover: #004488;
}
}
// 分页样式
.table-pagination {
padding: 16px 20px;
display: flex;
justify-content: flex-end;
border-top: 1px solid #f1f5f9;
background: #fff;
}
:deep(.el-pagination) {
.el-pager {
li {
border-radius: 6px;
&.is-active {
background: $brand-color;
color: #fff;
}
}
}
.btn-prev,
.btn-next {
border-radius: 6px;
}
}
// 弹窗样式
:deep(.el-dialog) {