- 新增 WorkbenchAiFilePreviewDialog 附件预览对话框及 useWorkbenchAiFilePreview,附件支持点击预览 - 新增 attachmentAssociationJobs/linkedReimbursementDraftJobs 前端服务与对应 composable,接入后台任务轮询与状态展示 - 新增 travelReimbursementDraftBranchModel 草稿分支模型,报销关联门控支持跳过/选择草稿 - PersonalWorkbenchAiMode 及各 composable(expense/document/steward/application-preview/attachment-association)重构适配,WorkbenchAiComposer/FileStrip 样式与交互完善 - DocumentsCenter/ReceiptFolder/TravelReimbursementCreate 等视图及 scripts 重构,风险/差旅规划/审批等工具适配 - 新增/更新前端测试:application-result-card、reimbursement-list-preview-fetch、guided-flow、composer-components 等
153 lines
3.9 KiB
JavaScript
153 lines
3.9 KiB
JavaScript
import { computed, reactive, ref } from 'vue'
|
|
|
|
import {
|
|
REIMBURSEMENT_LIST_PREVIEW_PARAMS,
|
|
extractExpenseClaimItems,
|
|
fetchExpenseClaims
|
|
} from '../services/reimbursements.js'
|
|
import { formatDate, toDate } from './requests/requestShared.js'
|
|
import { mapExpenseClaimToRequest } from './requests/requestClaimMapper.js'
|
|
|
|
export { mapExpenseClaimToRequest } from './requests/requestClaimMapper.js'
|
|
|
|
function getWeekStart(date) {
|
|
const nextDate = new Date(date)
|
|
const day = nextDate.getDay() || 7
|
|
nextDate.setHours(0, 0, 0, 0)
|
|
nextDate.setDate(nextDate.getDate() - day + 1)
|
|
return nextDate
|
|
}
|
|
|
|
function getRecentDaysStart(date, days) {
|
|
const nextDate = new Date(date)
|
|
nextDate.setHours(0, 0, 0, 0)
|
|
nextDate.setDate(nextDate.getDate() - Math.max(0, Number(days || 1) - 1))
|
|
return nextDate
|
|
}
|
|
|
|
function resolveRangeMatch(activeRange, item) {
|
|
if (activeRange === 'custom' || activeRange === '本月') {
|
|
if (activeRange !== '本月') {
|
|
return true
|
|
}
|
|
}
|
|
|
|
const targetDate = toDate(item?.submittedAt || item?.createdAt || item?.occurredAt)
|
|
if (!targetDate) {
|
|
return true
|
|
}
|
|
|
|
const now = new Date()
|
|
const targetDay = formatDate(targetDate)
|
|
|
|
if (activeRange === '今日') {
|
|
return targetDay === formatDate(now)
|
|
}
|
|
|
|
if (activeRange === '近10日') {
|
|
const recentStart = getRecentDaysStart(now, 10)
|
|
return targetDate >= recentStart && targetDate <= now
|
|
}
|
|
|
|
if (activeRange === '本周') {
|
|
const weekStart = getWeekStart(now)
|
|
const nextWeekStart = new Date(weekStart)
|
|
nextWeekStart.setDate(nextWeekStart.getDate() + 7)
|
|
return targetDate >= weekStart && targetDate < nextWeekStart
|
|
}
|
|
|
|
if (activeRange === '本月') {
|
|
return (
|
|
targetDate.getFullYear() === now.getFullYear()
|
|
&& targetDate.getMonth() === now.getMonth()
|
|
)
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
export function useRequests() {
|
|
const requests = ref([])
|
|
const loading = ref(false)
|
|
const loaded = ref(false)
|
|
const error = ref('')
|
|
const search = ref('')
|
|
const filters = reactive({ entity: '全部主体', category: '全部类型', risk: '全部状态' })
|
|
const ranges = ['今日', '近10日', '本周', '本月']
|
|
const activeRange = ref('近10日')
|
|
|
|
const filteredRequests = computed(() => {
|
|
const key = search.value.trim().toLowerCase()
|
|
|
|
return requests.value.filter((item) => {
|
|
const searchText = [
|
|
item.id,
|
|
item.person,
|
|
item.typeLabel,
|
|
item.title,
|
|
item.sceneTarget,
|
|
item.riskSummary
|
|
]
|
|
.filter(Boolean)
|
|
.join('')
|
|
.toLowerCase()
|
|
|
|
const matchesSearch = !key || searchText.includes(key)
|
|
const matchesRange = resolveRangeMatch(activeRange.value, item)
|
|
|
|
return matchesSearch && matchesRange
|
|
})
|
|
})
|
|
|
|
async function reload(options = {}) {
|
|
const silent = Boolean(options?.silent)
|
|
if (!silent) {
|
|
loading.value = true
|
|
error.value = ''
|
|
}
|
|
|
|
try {
|
|
const payload = await fetchExpenseClaims(REIMBURSEMENT_LIST_PREVIEW_PARAMS)
|
|
requests.value = extractExpenseClaimItems(payload).map((item) => mapExpenseClaimToRequest(item))
|
|
loaded.value = true
|
|
} catch (nextError) {
|
|
if (!silent) {
|
|
requests.value = []
|
|
}
|
|
error.value = nextError instanceof Error ? nextError.message : '个人报销列表加载失败。'
|
|
} finally {
|
|
if (!silent) {
|
|
loading.value = false
|
|
}
|
|
}
|
|
}
|
|
|
|
function approveRequest(request) {
|
|
return `${request.id} 未执行本地状态变更,列表当前只展示后端真实数据。`
|
|
}
|
|
|
|
function rejectRequest(request) {
|
|
return `${request.id} 未执行本地状态变更,列表当前只展示后端真实数据。`
|
|
}
|
|
|
|
function ensureLoaded() {
|
|
return loaded.value ? Promise.resolve() : reload()
|
|
}
|
|
|
|
return {
|
|
requests,
|
|
loading,
|
|
loaded,
|
|
error,
|
|
search,
|
|
filters,
|
|
ranges,
|
|
activeRange,
|
|
filteredRequests,
|
|
approveRequest,
|
|
rejectRequest,
|
|
ensureLoaded,
|
|
reload
|
|
}
|
|
}
|