refactor: extract image form to standalone page and implement tags view store

- Created ImageForm.vue as standalone page for add/edit image functionality
- Removed dialog-based image form from VmImages.vue
- Implemented tagsViewStore for global tab state management
- Added automatic tab closing on form cancel/back
- Fixed data persistence issue when switching between image edits
- Removed quick actions section from ImageForm
- Updated router configuration for new image form route
This commit is contained in:
2025-11-28 14:15:29 +08:00
parent 067e0539ba
commit f7c3be1d30
45 changed files with 8776 additions and 6881 deletions
+61 -13
View File
@@ -336,9 +336,24 @@ const fetchTicketList = async (append = false) => {
}))
if (append) {
ticketList.value = [...ticketList.value, ...mappedTickets]
// 翻页时:合并列表,使用work_id去重,然后按work_id从大到小排序
const existingIds = new Set(ticketList.value.map(t => t.id))
const newTickets = mappedTickets.filter(t => !existingIds.has(t.id))
const mergedTickets = [...ticketList.value, ...newTickets]
// 按work_id从大到小排序
ticketList.value = mergedTickets.sort((a, b) => {
const idA = typeof a.id === 'string' ? parseInt(a.id) || 0 : a.id
const idB = typeof b.id === 'string' ? parseInt(b.id) || 0 : b.id
return idB - idA
})
} else {
ticketList.value = mappedTickets
// 首次加载或切换类别:直接使用新数据,按work_id从大到小排序
ticketList.value = mappedTickets.sort((a, b) => {
const idA = typeof a.id === 'string' ? parseInt(a.id) || 0 : a.id
const idB = typeof b.id === 'string' ? parseInt(b.id) || 0 : b.id
return idB - idA
})
}
hasMore.value = ticketList.value.length < res.data.all_count
@@ -398,6 +413,18 @@ const fetchAllStats = async () => {
}
}
// 只刷新当前分类的统计数据(用于定时刷新,减少请求)
const fetchCurrentStatusStat = async () => {
try {
// 只获取当前选中分类的统计数据
await fetchStatusStat(activeStatus.value)
// 同时获取全部工单数量(因为顶部显示需要)
await fetchStatusStat('')
} catch (error) {
console.error('获取当前分类统计数据出错:', error)
}
}
// 加载更多工单
const loadMoreTickets = () => {
if (!hasMore.value || isLoading.value) return
@@ -436,16 +463,16 @@ const filteredTickets = computed(() => {
)
}
// 按最后回复时间排序(优先显示有新消息的
// 按work_id从大到小排序(优先显示待处理
return result.sort((a, b) => {
// 优先显示待处理
if (a.status === 'pending' && b.status !== 'pending') return -1
if (a.status !== 'pending' && b.status === 'pending') return 1
// 然后按最后回复时间
const timeA = new Date(a.lastReplyTime || a.createTime)
const timeB = new Date(b.lastReplyTime || b.createTime)
return timeB - timeA
// 然后按work_id从大到小排序
const idA = typeof a.id === 'string' ? parseInt(a.id) || 0 : a.id
const idB = typeof b.id === 'string' ? parseInt(b.id) || 0 : b.id
return idB - idA
})
})
@@ -685,7 +712,8 @@ const filterByStatus = (status) => {
activeStatus.value = status
currentPage.value = 1 // 切换状态后重置页码
hasMore.value = true // 重置加载更多标志
fetchTicketList() // 重新获取数据
ticketList.value = [] // 清空列表,不缓存
fetchTicketList(false) // 从头重新获取数据,不追加
}
// 搜索处理
@@ -775,8 +803,8 @@ const startAutoRefresh = () => {
// 静默刷新工单列表,保持当前页码
refreshTicketList()
// 刷新工单统计
fetchAllStats()
// 刷新当前分类的统计数据,减少请求数量
fetchCurrentStatusStat()
}, refreshInterval)
}
@@ -800,7 +828,8 @@ const refreshTicketList = async () => {
else if (activeStatus.value === 'completed') statusParam = '3'
}
const res = await getTickerList(pageSize.value, currentPage.value, statusParam)
// 刷新时只获取第一页数据,用于更新最新数据
const res = await getTickerList(pageSize.value, 1, statusParam)
if (res.code === 200) {
const tickets = res.data.data || []
@@ -817,8 +846,27 @@ const refreshTicketList = async () => {
content: item.name
}))
// 更新列表
ticketList.value = mappedTickets
// 合并到现有列表,去重并按work_id从大到小排序
// 使用Map来更新已存在的工单信息,添加新的工单
const ticketMap = new Map()
// 先添加现有列表
ticketList.value.forEach(ticket => {
ticketMap.set(ticket.id, ticket)
})
// 更新或添加新数据(新数据会覆盖旧数据)
mappedTickets.forEach(ticket => {
ticketMap.set(ticket.id, ticket)
})
// 转换为数组并按work_id从大到小排序
ticketList.value = Array.from(ticketMap.values()).sort((a, b) => {
const idA = typeof a.id === 'string' ? parseInt(a.id) || 0 : a.id
const idB = typeof b.id === 'string' ? parseInt(b.id) || 0 : b.id
return idB - idA
})
// 不重置页码,保持用户当前的浏览位置
// 更新加载更多标志
hasMore.value = ticketList.value.length < res.data.all_count
}
} catch (error) {