import { computed, ref, watch } from 'vue' import EnterpriseSelect from '../../components/shared/EnterpriseSelect.vue' import TableLoadingState from '../../components/shared/TableLoadingState.vue' import TableEmptyState from '../../components/shared/TableEmptyState.vue' import { normalizeRequestForUi } from '../../utils/requestViewModel.js' function extractRowDate(value) { const matched = String(value || '').match(/\d{4}-\d{2}-\d{2}/) return matched ? matched[0] : '' } export default { name: 'RequestsView', components: { EnterpriseSelect, TableLoadingState, TableEmptyState }, props: { filteredRequests: { type: Array, required: true }, hasData: { type: Boolean, default: false }, loading: { type: Boolean, default: false }, error: { type: String, default: '' } }, emits: ['ask', 'approve', 'reject', 'create-request', 'reload'], setup(props, { emit }) { const activeTab = ref('全部') const tabs = ['全部', '草稿', '待提交', '审批中', '待补充', '待付款', '已完成'] const filters = ['报销状态', '报销类型', '所属主体'] const listKeyword = ref('') const datePopover = ref(false) const rangeStart = ref('') const rangeEnd = ref('') const appliedStart = ref('') const appliedEnd = ref('') const dateRangeLabel = computed(() => { if (appliedStart.value && appliedEnd.value) { return `${appliedStart.value} ~ ${appliedEnd.value}` } return '选择时间段' }) function applyDateRange() { if (!rangeStart.value || !rangeEnd.value) { return } appliedStart.value = rangeStart.value appliedEnd.value = rangeEnd.value datePopover.value = false } const rows = computed(() => props.filteredRequests .map((item) => normalizeRequestForUi(item)) .filter(Boolean) ) const currentPage = ref(1) const pageSize = ref(10) const pageSizes = [10, 20, 50] const pageSizeOptions = pageSizes.map((size) => ({ label: `${size} 条/页`, value: size })) function changePageSize(size) { pageSize.value = size currentPage.value = 1 } const filteredRows = computed(() => { const keyword = listKeyword.value.trim().toLowerCase() return rows.value.filter((row) => { const matchesKeyword = !keyword || [ row.id, row.documentNo, row.typeLabel, row.reason, row.sceneTarget, row.relatedCustomer, row.riskSummary ] .filter(Boolean) .join('') .toLowerCase() .includes(keyword) const applyDate = extractRowDate(row.applyTime) const matchesDateRange = !appliedStart.value || !appliedEnd.value || (applyDate && applyDate >= appliedStart.value && applyDate <= appliedEnd.value) const matchesTab = activeTab.value === '全部' || (activeTab.value === '草稿' && row.approvalKey === 'draft') || (activeTab.value === '待提交' && row.approvalKey === 'supplement' && row.status === 'returned') || (activeTab.value === '审批中' && row.approvalKey === 'in_progress') || (activeTab.value === '待补充' && row.approvalKey === 'supplement' && row.status !== 'returned') || (activeTab.value === '待付款' && row.approvalKey === 'pending_payment') || (activeTab.value === '已完成' && row.approvalKey === 'completed') return matchesKeyword && matchesDateRange && matchesTab }) }) const totalCount = computed(() => filteredRows.value.length) const totalPages = computed(() => Math.max(1, Math.ceil(totalCount.value / pageSize.value))) const visibleRows = computed(() => { const start = (currentPage.value - 1) * pageSize.value return filteredRows.value.slice(start, start + pageSize.value) }) const showTable = computed(() => !props.loading && !props.error && visibleRows.value.length > 0) const showEmpty = computed(() => !props.loading && !props.error && visibleRows.value.length === 0) const hasListFilters = computed(() => { return Boolean( activeTab.value !== '全部' || listKeyword.value.trim() || appliedStart.value || appliedEnd.value ) }) const emptyState = computed(() => { if (!props.hasData) { return { eyebrow: '个人报销', title: '还没有任何报销单据', desc: '首张草稿或已提交的报销单会自动出现在这里,后续可以继续补充、提交和跟踪进度。', icon: 'mdi mdi-receipt-text-plus-outline', actionLabel: '', actionIcon: '', tone: 'theme', artLabel: 'CLAIM', tips: ['保存草稿后会自动回到这里', '支持草稿、待提交、审批中和已完成全流程管理'] } } return { eyebrow: hasListFilters.value ? '筛选结果为空' : '状态列表为空', title: hasListFilters.value ? '当前条件下没有匹配单据' : `“${activeTab.value}”里暂时没有单据`, desc: hasListFilters.value ? '可以清空关键词、时间段或状态筛选后再看看。' : '当前状态下还没有可展示的报销记录,可以先发起一笔报销或切换到其他状态。', icon: hasListFilters.value ? 'mdi mdi-magnify-scan' : 'mdi mdi-clipboard-text-clock-outline', actionLabel: hasListFilters.value ? '清空筛选' : '', actionIcon: hasListFilters.value ? 'mdi mdi-filter-remove-outline' : '', tone: hasListFilters.value ? 'sky' : 'slate', artLabel: hasListFilters.value ? 'FILTER' : 'QUEUE', tips: hasListFilters.value ? ['关键词、时间段和状态会叠加生效', '可尝试搜索单号、事由或报销类型'] : ['已完成单据会保留在列表中便于追踪', '草稿、待提交、审批中和待补充会按真实状态实时归类'] } }) function resetFilters() { activeTab.value = '全部' listKeyword.value = '' datePopover.value = false rangeStart.value = '' rangeEnd.value = '' appliedStart.value = '' appliedEnd.value = '' currentPage.value = 1 } function handleEmptyAction() { if (!props.hasData) { emit('create-request') return } resetFilters() } watch([activeTab, rows, listKeyword, appliedStart, appliedEnd], () => { currentPage.value = 1 }) return { emit, activeTab, tabs, filters, listKeyword, datePopover, rangeStart, rangeEnd, appliedStart, appliedEnd, dateRangeLabel, applyDateRange, rows, currentPage, pageSize, pageSizes, pageSizeOptions, changePageSize, filteredRows, totalCount, totalPages, visibleRows, showTable, showEmpty, emptyState, resetFilters, handleEmptyAction } } }