Move 所属项目/项目组 after selection and serial-number columns so multi-select and index stay leftmost across affected business lists.
695 lines
23 KiB
Vue
695 lines
23 KiB
Vue
<!-- 中试服务申请单 · 申请单 Tab -->
|
||
<template>
|
||
<div class="pilot-application-tab">
|
||
<div class="filter-block">
|
||
<el-form :model="searchForm" class="filter-form" @submit.prevent>
|
||
<div class="filter-grid">
|
||
<el-form-item v-if="!isClinical" label="项目组"
|
||
><ProjectSelect filter
|
||
team
|
||
v-model="teamFilter"
|
||
:biz-type="
|
||
props.bizType === 'basic_experiment'
|
||
? 'basic_experiment'
|
||
: 'pilot_service'
|
||
"
|
||
/></el-form-item>
|
||
<el-form-item v-if="!isClinical" label="项目"
|
||
><ProjectSelect filter
|
||
v-model="projectFilter"
|
||
:biz-type="
|
||
props.bizType === 'basic_experiment'
|
||
? 'basic_experiment'
|
||
: 'pilot_service'
|
||
"
|
||
/></el-form-item>
|
||
<el-form-item label="关键词">
|
||
<el-input v-model="searchForm.keyword" :placeholder="keywordPlaceholder" clearable />
|
||
</el-form-item>
|
||
<el-form-item v-if="bizConfig.showChannel" label="渠道">
|
||
<el-select
|
||
v-model="searchForm.channelId"
|
||
placeholder="全部"
|
||
clearable
|
||
filterable
|
||
:loading="channelLoading"
|
||
@focus="onChannelSelectFocus"
|
||
>
|
||
<el-option
|
||
v-for="ch in channelOptions"
|
||
:key="ch.id"
|
||
:label="ch.channelName || ch.name"
|
||
:value="ch.id"
|
||
/>
|
||
</el-select>
|
||
</el-form-item>
|
||
<el-form-item label="状态">
|
||
<el-select v-model="searchForm.applicationStatus" placeholder="全部" clearable>
|
||
<el-option label="待开始" value="dks" />
|
||
<el-option label="进行中" value="jxz" />
|
||
<el-option label="已完成" value="ywc" />
|
||
<el-option label="已作废" value="yzf" />
|
||
</el-select>
|
||
</el-form-item>
|
||
</div>
|
||
<div class="filter-actions-row">
|
||
<div class="filter-actions-end">
|
||
<el-button class="btn-ghost" @click="handleReset">
|
||
<el-icon><RefreshLeft /></el-icon>重置
|
||
</el-button>
|
||
<el-button class="btn-main" type="primary" @click="handleSearch">
|
||
<el-icon><Search /></el-icon>搜索
|
||
</el-button>
|
||
</div>
|
||
</div>
|
||
</el-form>
|
||
</div>
|
||
|
||
<div class="list-toolbar">
|
||
<div class="list-toolbar-row">
|
||
<div class="list-toolbar-selected">
|
||
已选 <span class="sel-num">{{ selectedRows.length }}</span> 项
|
||
</div>
|
||
<el-button class="btn-main" type="primary" @click="handleCreate">
|
||
<el-icon><Plus /></el-icon>
|
||
新增
|
||
</el-button>
|
||
<el-button
|
||
class="btn-ghost"
|
||
:disabled="!canBatchStart"
|
||
:loading="batchActionLoading === 'start'"
|
||
@click="handleBatchStart"
|
||
>
|
||
<el-icon><VideoPlay /></el-icon>
|
||
开始
|
||
</el-button>
|
||
<el-button
|
||
class="btn-ghost"
|
||
:disabled="!canBatchComplete"
|
||
:loading="batchActionLoading === 'complete'"
|
||
@click="handleBatchComplete"
|
||
>
|
||
<el-icon><CircleCheck /></el-icon>
|
||
完成
|
||
</el-button>
|
||
<el-button
|
||
class="btn-ghost"
|
||
type="danger"
|
||
:disabled="!canBatchVoid"
|
||
:loading="batchActionLoading === 'void'"
|
||
@click="handleBatchVoid"
|
||
>
|
||
<el-icon><CircleClose /></el-icon>
|
||
作废
|
||
</el-button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="list-body list-wrap">
|
||
<el-table
|
||
ref="tableRef"
|
||
class="list-table"
|
||
:data="tableData"
|
||
v-loading="loading"
|
||
stripe
|
||
row-key="id"
|
||
@selection-change="handleSelectionChange"
|
||
@row-dblclick="handleView"
|
||
>
|
||
<el-table-column type="selection" min-width="55" align="center" header-align="center" />
|
||
<el-table-column
|
||
type="index"
|
||
label="序号"
|
||
min-width="68"
|
||
align="center"
|
||
header-align="center"
|
||
:index="(index) => (pagination.page - 1) * pagination.pageSize + index + 1"
|
||
/>
|
||
<el-table-column v-if="!isClinical" prop="projectName" label="所属项目" min-width="160" />
|
||
<el-table-column v-if="!isClinical" prop="teamName" label="项目组" min-width="150" />
|
||
<el-table-column
|
||
prop="applicationNo"
|
||
label="申请单号"
|
||
min-width="180"
|
||
show-overflow-tooltip
|
||
>
|
||
<template #default="{ row }">
|
||
<el-button link type="primary" @click="handleView(row)">
|
||
{{ row.applicationNo || '-' }}
|
||
</el-button>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column
|
||
v-if="isBasicExperiment"
|
||
prop="projectTitle"
|
||
label="课题名称"
|
||
min-width="160"
|
||
show-overflow-tooltip
|
||
/>
|
||
<el-table-column
|
||
v-if="isBasicExperiment"
|
||
prop="customerName"
|
||
label="客户名称"
|
||
min-width="140"
|
||
show-overflow-tooltip
|
||
/>
|
||
<el-table-column
|
||
v-if="bizConfig.showChannel"
|
||
prop="channelName"
|
||
label="渠道名称"
|
||
min-width="150"
|
||
show-overflow-tooltip
|
||
/>
|
||
<el-table-column
|
||
v-if="!isClinical && !isBasicExperiment"
|
||
label="服务订单名称"
|
||
min-width="180"
|
||
show-overflow-tooltip
|
||
>
|
||
<template #default="{ row }">
|
||
<span v-if="row.serviceOrderNo">{{ row.serviceOrderNo }}</span>
|
||
<span v-else>-</span>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column
|
||
v-if="bizConfig.showPatient"
|
||
prop="patientName"
|
||
label="患者姓名"
|
||
min-width="100"
|
||
show-overflow-tooltip
|
||
>
|
||
<template #default="{ row }">{{ row.patientName || '-' }}</template>
|
||
</el-table-column>
|
||
<el-table-column
|
||
prop="infusionScheduleCount"
|
||
:label="`${bizConfig.scheduleLabel}数`"
|
||
min-width="100"
|
||
align="center"
|
||
>
|
||
<template #default="{ row }">{{ row.infusionScheduleCount ?? 0 }}</template>
|
||
</el-table-column>
|
||
<el-table-column prop="applicationStatus" label="状态" min-width="100" align="center">
|
||
<template #default="{ row }">
|
||
<el-tag :type="applicationStatusTagType(row.applicationStatus)" size="small">
|
||
{{ applicationStatusLabel(row.applicationStatus) }}
|
||
</el-tag>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column prop="createTime" label="创建时间" min-width="160" align="center" >
|
||
<template #default="{ row }">{{ formatDisplayOrDash(row.createTime) }}</template>
|
||
</el-table-column>
|
||
<el-table-column
|
||
label="操作"
|
||
min-width="260"
|
||
fixed="right"
|
||
align="center"
|
||
header-align="center"
|
||
>
|
||
<template #default="{ row }">
|
||
<div style="display: flex; justify-content: center; gap: 8px">
|
||
<template v-if="hasPrimaryStatusActions(row)">
|
||
<el-button
|
||
v-if="row.applicationStatus === 'dks'"
|
||
class="btn-op-item primary"
|
||
type="primary"
|
||
@click="handleStart(row)"
|
||
>
|
||
<el-icon><VideoPlay /></el-icon>开始
|
||
</el-button>
|
||
<el-button
|
||
v-if="row.applicationStatus === 'jxz'"
|
||
class="btn-op-item primary"
|
||
type="primary"
|
||
@click="handleComplete(row)"
|
||
>
|
||
<el-icon><CircleCheck /></el-icon>完成
|
||
</el-button>
|
||
<el-button
|
||
v-if="canVoidApplication(row.applicationStatus)"
|
||
class="btn-op-item op-warn"
|
||
type="danger"
|
||
@click="handleVoid(row)"
|
||
>
|
||
<el-icon><CircleClose /></el-icon>作废
|
||
</el-button>
|
||
</template>
|
||
<el-button v-else class="btn-op-item primary" @click="handleView(row)">
|
||
<el-icon><View /></el-icon>查看
|
||
</el-button>
|
||
<el-dropdown
|
||
v-if="hasMoreActions(row)"
|
||
trigger="hover"
|
||
@command="(cmd) => handleDropdownCommand(cmd, row)"
|
||
>
|
||
<el-button class="btn-op-item">
|
||
<el-icon><More /></el-icon>
|
||
</el-button>
|
||
<template #dropdown>
|
||
<el-dropdown-menu>
|
||
<el-dropdown-item v-if="hasPrimaryStatusActions(row)" command="view">
|
||
<el-icon><View /></el-icon>查看
|
||
</el-dropdown-item>
|
||
<el-dropdown-item
|
||
v-if="canEditApplication(row.applicationStatus)"
|
||
command="edit"
|
||
>
|
||
<el-icon><Edit /></el-icon>编辑
|
||
</el-dropdown-item>
|
||
<el-dropdown-item
|
||
v-if="row.applicationStatus === 'dks'"
|
||
command="delete"
|
||
divided
|
||
>
|
||
<el-icon><Delete /></el-icon>删除
|
||
</el-dropdown-item>
|
||
</el-dropdown-menu>
|
||
</template>
|
||
</el-dropdown>
|
||
</div>
|
||
</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
|
||
<div class="pagination list-pager-bar">
|
||
<el-pagination
|
||
:current-page="pagination.page"
|
||
:page-size="pagination.pageSize"
|
||
:total="pagination.total"
|
||
:page-sizes="[10, 20, 50, 100]"
|
||
layout="total, sizes, prev, pager, next, jumper"
|
||
@update:current-page="(val) => (pagination.page = val)"
|
||
@update:page-size="(val) => (pagination.pageSize = val)"
|
||
@size-change="loadData"
|
||
@current-change="loadData"
|
||
/>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 新增/编辑弹窗 -->
|
||
<ApplicationFormDialog
|
||
v-model="formDialogVisible"
|
||
:edit-id="formDialogEditId"
|
||
:biz-type="bizType"
|
||
@saved="onFormSaved"
|
||
/>
|
||
|
||
<!-- 查看详情弹窗 -->
|
||
<ApplicationViewDialog v-model="viewDialogVisible" :application-id="viewApplicationId" />
|
||
</div>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import ProjectSelect from '@/components/business/ProjectSelect.vue'
|
||
const projectFilter = ref('')
|
||
const teamFilter = ref('')
|
||
import { formatDisplayOrDash } from '@/utils/datetime-display'
|
||
import { ref, reactive, computed, onMounted } from 'vue'
|
||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||
import {
|
||
Plus,
|
||
Search,
|
||
RefreshLeft,
|
||
View,
|
||
Edit,
|
||
Delete,
|
||
More,
|
||
VideoPlay,
|
||
CircleCheck,
|
||
CircleClose
|
||
} from '@element-plus/icons-vue'
|
||
import ApplicationFormDialog from './ApplicationFormDialog.vue'
|
||
import ApplicationViewDialog from './ApplicationViewDialog.vue'
|
||
import {
|
||
fetchPilotApplicationList,
|
||
deletePilotApplication,
|
||
startPilotApplication,
|
||
completePilotApplication,
|
||
voidPilotApplication,
|
||
fetchChannelList
|
||
} from '@/api/admin'
|
||
import { applicationStatusLabel, applicationStatusTagType } from '@/utils/status-tag-registry'
|
||
import { fetchAllPagedRecords } from '@/utils/fetch-paged-list'
|
||
import { getPilotBizConfig, resolvePilotBizType } from '@/config/pilot-biz-type'
|
||
|
||
defineOptions({ name: 'PilotApplicationTab' })
|
||
|
||
const props = defineProps<{ bizType?: string }>()
|
||
const bizConfig = computed(() => getPilotBizConfig(props.bizType))
|
||
const isClinical = computed(() => props.bizType === 'clinical_application')
|
||
const isBasicExperiment = computed(() => props.bizType === 'basic_experiment')
|
||
const keywordPlaceholder = computed(() =>
|
||
isBasicExperiment.value ? '申请单号/课题名称/客户名称' : '申请单号/订单名称/患者姓名'
|
||
)
|
||
// ── 搜索 ──────────────────────────────────────────────────────────────
|
||
const searchForm = reactive({
|
||
keyword: '',
|
||
channelId: '',
|
||
applicationStatus: ''
|
||
})
|
||
|
||
// ── 搜索区域渠道下拉 ──────────────────────────────────────────────────
|
||
const channelOptions = ref<any[]>([])
|
||
const channelLoading = ref(false)
|
||
const channelLoaded = ref(false)
|
||
|
||
const onChannelSelectFocus = () => {
|
||
if (!channelLoaded.value) {
|
||
loadChannelOptions()
|
||
}
|
||
}
|
||
|
||
const loadChannelOptions = async () => {
|
||
channelLoading.value = true
|
||
try {
|
||
channelOptions.value = await fetchAllPagedRecords((page, pageSize) =>
|
||
fetchChannelList({ page, pageSize })
|
||
)
|
||
channelLoaded.value = true
|
||
} catch {
|
||
channelOptions.value = []
|
||
} finally {
|
||
channelLoading.value = false
|
||
}
|
||
}
|
||
|
||
// ── 表格 ──────────────────────────────────────────────────────────────
|
||
const tableRef = ref()
|
||
const tableData = ref<any[]>([])
|
||
const selectedRows = ref<any[]>([])
|
||
const loading = ref(false)
|
||
const batchActionLoading = ref<'start' | 'complete' | 'void' | ''>('')
|
||
const pagination = reactive({ page: 1, pageSize: 10, total: 0 })
|
||
|
||
const handleSelectionChange = (rows: any[]) => {
|
||
selectedRows.value = rows
|
||
}
|
||
|
||
const clearTableSelection = () => {
|
||
tableRef.value?.clearSelection?.()
|
||
selectedRows.value = []
|
||
}
|
||
|
||
const batchStartTargets = computed(() =>
|
||
selectedRows.value.filter((row) => row.applicationStatus === 'dks')
|
||
)
|
||
const batchCompleteTargets = computed(() =>
|
||
selectedRows.value.filter((row) => row.applicationStatus === 'jxz')
|
||
)
|
||
const batchVoidTargets = computed(() =>
|
||
selectedRows.value.filter((row) => ['dks', 'jxz'].includes(row.applicationStatus))
|
||
)
|
||
|
||
const canBatchStart = computed(() => batchStartTargets.value.length > 0)
|
||
const canBatchComplete = computed(() => batchCompleteTargets.value.length > 0)
|
||
const canBatchVoid = computed(() => batchVoidTargets.value.length > 0)
|
||
|
||
const loadData = async () => {
|
||
loading.value = true
|
||
try {
|
||
const params: Record<string, any> = {
|
||
page: pagination.page,
|
||
pageSize: pagination.pageSize
|
||
}
|
||
if (searchForm.keyword) params.keyword = searchForm.keyword
|
||
if (searchForm.channelId) params.channelId = searchForm.channelId
|
||
if (searchForm.applicationStatus) params.applicationStatus = searchForm.applicationStatus
|
||
params.projectId = projectFilter.value || undefined
|
||
params.teamId = teamFilter.value || undefined
|
||
params.bizType = resolvePilotBizType(props.bizType)
|
||
|
||
const res: any = await fetchPilotApplicationList(params)
|
||
tableData.value = res?.list ?? res?.records ?? []
|
||
pagination.total = res?.pagination?.total ?? res?.total ?? 0
|
||
} catch (e) {
|
||
console.error('加载申请单列表失败:', e)
|
||
} finally {
|
||
loading.value = false
|
||
}
|
||
}
|
||
|
||
const handleSearch = () => {
|
||
pagination.page = 1
|
||
loadData()
|
||
}
|
||
|
||
const handleReset = () => {
|
||
projectFilter.value = ''
|
||
teamFilter.value = ''
|
||
searchForm.keyword = ''
|
||
searchForm.channelId = ''
|
||
searchForm.applicationStatus = ''
|
||
pagination.page = 1
|
||
loadData()
|
||
}
|
||
|
||
// ── 查看详情弹窗 ──────────────────────────────────────────────────────
|
||
const viewDialogVisible = ref(false)
|
||
const viewApplicationId = ref('')
|
||
|
||
const handleView = (row: any) => {
|
||
viewApplicationId.value = row.id
|
||
viewDialogVisible.value = true
|
||
}
|
||
|
||
// ── 新增/编辑弹窗 ──────────────────────────────────────────────────────
|
||
const formDialogVisible = ref(false)
|
||
const formDialogEditId = ref<string | null>(null)
|
||
|
||
const handleCreate = () => {
|
||
formDialogEditId.value = null
|
||
formDialogVisible.value = true
|
||
}
|
||
|
||
/** 待开始、进行中可编辑 */
|
||
const canEditApplication = (status: string) => ['dks', 'jxz'].includes(status)
|
||
|
||
/** 待开始、进行中可作废 */
|
||
const canVoidApplication = (status: string) => ['dks', 'jxz'].includes(status)
|
||
|
||
/** 是否存在开始/完成/作废等主操作 */
|
||
const hasPrimaryStatusActions = (row: any) =>
|
||
row.applicationStatus === 'dks' || row.applicationStatus === 'jxz'
|
||
|
||
/** 更多菜单是否展示(有主操作时,查看/编辑/删除收入更多) */
|
||
const hasMoreActions = (row: any) => hasPrimaryStatusActions(row)
|
||
|
||
const handleDropdownCommand = (command: string, row: any) => {
|
||
switch (command) {
|
||
case 'view':
|
||
handleView(row)
|
||
break
|
||
case 'edit':
|
||
handleEdit(row)
|
||
break
|
||
case 'delete':
|
||
handleDelete(row)
|
||
break
|
||
}
|
||
}
|
||
|
||
const handleEdit = (row: any) => {
|
||
formDialogEditId.value = row.id
|
||
formDialogVisible.value = true
|
||
}
|
||
|
||
const onFormSaved = () => {
|
||
loadData()
|
||
}
|
||
|
||
// ── 删除 ──────────────────────────────────────────────────────────────
|
||
const handleDelete = async (row: any) => {
|
||
try {
|
||
await ElMessageBox.confirm('确定删除该申请单吗?', '提示', {
|
||
confirmButtonText: '确定',
|
||
cancelButtonText: '取消',
|
||
type: 'warning'
|
||
})
|
||
await deletePilotApplication({ id: row.id })
|
||
ElMessage.success('删除成功')
|
||
loadData()
|
||
} catch (e: any) {
|
||
if (e !== 'cancel') {
|
||
ElMessage.error(e?.message || '删除失败')
|
||
}
|
||
}
|
||
}
|
||
|
||
// ── 状态操作 ──────────────────────────────────────────────────────────
|
||
type BatchStatusAction = 'start' | 'complete' | 'void'
|
||
|
||
const runBatchStatusAction = async (options: {
|
||
action: BatchStatusAction
|
||
targets: any[]
|
||
actionLabel: string
|
||
confirmMessage: string
|
||
confirmType?: 'info' | 'warning'
|
||
apiFn: (payload: { id: string }) => Promise<unknown>
|
||
}) => {
|
||
const { action, targets, actionLabel, confirmMessage, confirmType = 'info', apiFn } = options
|
||
if (!selectedRows.value.length) {
|
||
ElMessage.warning('请先选择申请单')
|
||
return
|
||
}
|
||
if (!targets.length) {
|
||
ElMessage.warning(`所选申请单中没有可${actionLabel}的记录`)
|
||
return
|
||
}
|
||
|
||
const skipped = selectedRows.value.length - targets.length
|
||
let message = confirmMessage
|
||
if (skipped > 0) {
|
||
message += `\n共选中 ${selectedRows.value.length} 条,其中 ${targets.length} 条将执行${actionLabel},${skipped} 条因状态不符将跳过。`
|
||
} else {
|
||
message += `\n共 ${targets.length} 条申请单。`
|
||
}
|
||
|
||
try {
|
||
await ElMessageBox.confirm(message, '提示', {
|
||
confirmButtonText: '确定',
|
||
cancelButtonText: '取消',
|
||
type: confirmType
|
||
})
|
||
} catch {
|
||
return
|
||
}
|
||
|
||
batchActionLoading.value = action
|
||
let successCount = 0
|
||
let failCount = 0
|
||
let firstError = ''
|
||
|
||
for (const row of targets) {
|
||
try {
|
||
await apiFn({ id: row.id })
|
||
successCount += 1
|
||
} catch (e: any) {
|
||
failCount += 1
|
||
if (!firstError) {
|
||
firstError = e?.message || `${actionLabel}失败`
|
||
}
|
||
}
|
||
}
|
||
|
||
batchActionLoading.value = ''
|
||
clearTableSelection()
|
||
loadData()
|
||
|
||
if (failCount === 0) {
|
||
ElMessage.success(`已成功${actionLabel} ${successCount} 条申请单`)
|
||
} else if (successCount === 0) {
|
||
ElMessage.error(firstError || `${actionLabel}失败`)
|
||
} else {
|
||
ElMessage.warning(
|
||
`${actionLabel}完成:成功 ${successCount} 条,失败 ${failCount} 条。${firstError}`
|
||
)
|
||
}
|
||
}
|
||
|
||
const handleBatchStart = () =>
|
||
runBatchStatusAction({
|
||
action: 'start',
|
||
targets: batchStartTargets.value,
|
||
actionLabel: '开始',
|
||
confirmMessage: '确定将所选待开始申请单变更为进行中吗?',
|
||
apiFn: startPilotApplication
|
||
})
|
||
|
||
const handleBatchComplete = () =>
|
||
runBatchStatusAction({
|
||
action: 'complete',
|
||
targets: batchCompleteTargets.value,
|
||
actionLabel: '完成',
|
||
confirmMessage: '确定将所选进行中申请单变更为已完成吗?',
|
||
apiFn: completePilotApplication
|
||
})
|
||
|
||
const handleBatchVoid = () =>
|
||
runBatchStatusAction({
|
||
action: 'void',
|
||
targets: batchVoidTargets.value,
|
||
actionLabel: '作废',
|
||
confirmMessage: '确定作废所选申请单吗?作废后不可恢复。',
|
||
confirmType: 'warning',
|
||
apiFn: voidPilotApplication
|
||
})
|
||
|
||
const handleStart = async (row: any) => {
|
||
try {
|
||
await ElMessageBox.confirm('确定开始该申请单吗?', '提示', {
|
||
confirmButtonText: '确定',
|
||
cancelButtonText: '取消',
|
||
type: 'info'
|
||
})
|
||
await startPilotApplication({ id: row.id })
|
||
ElMessage.success('操作成功')
|
||
loadData()
|
||
} catch (e: any) {
|
||
if (e !== 'cancel') {
|
||
ElMessage.error(e?.message || '操作失败')
|
||
}
|
||
}
|
||
}
|
||
|
||
const handleComplete = async (row: any) => {
|
||
try {
|
||
await ElMessageBox.confirm('确定完成该申请单吗?', '提示', {
|
||
confirmButtonText: '确定',
|
||
cancelButtonText: '取消',
|
||
type: 'info'
|
||
})
|
||
await completePilotApplication({ id: row.id })
|
||
ElMessage.success('操作成功')
|
||
loadData()
|
||
} catch (e: any) {
|
||
if (e !== 'cancel') {
|
||
ElMessage.error(e?.message || '操作失败')
|
||
}
|
||
}
|
||
}
|
||
|
||
const handleVoid = async (row: any) => {
|
||
try {
|
||
await ElMessageBox.confirm('确定作废该申请单吗?作废后不可恢复。', '提示', {
|
||
confirmButtonText: '确定作废',
|
||
cancelButtonText: '取消',
|
||
type: 'warning'
|
||
})
|
||
await voidPilotApplication({ id: row.id })
|
||
ElMessage.success('操作成功')
|
||
loadData()
|
||
} catch (e: any) {
|
||
if (e !== 'cancel') {
|
||
ElMessage.error(e?.message || '操作失败')
|
||
}
|
||
}
|
||
}
|
||
|
||
// 检索条件变更时自动搜索(防抖 300ms)
|
||
watchDebounced(
|
||
searchForm,
|
||
() => {
|
||
pagination.page = 1
|
||
loadData()
|
||
},
|
||
{ debounce: 300, deep: true }
|
||
)
|
||
|
||
// ── 初始化 ────────────────────────────────────────────────────────────
|
||
watch([projectFilter, teamFilter], () => {
|
||
pagination.page = 1
|
||
loadData()
|
||
})
|
||
onMounted(() => {
|
||
loadData()
|
||
})
|
||
</script>
|
||
|
||
<style scoped lang="scss">
|
||
.status-filter-group {
|
||
display: flex;
|
||
gap: 8px;
|
||
flex-wrap: wrap;
|
||
align-items: center;
|
||
}
|
||
</style>
|