|
|
@@ -41,11 +41,12 @@ const isAdmin = computed(() => userStore.userInfo?.role === 'admin')
|
|
|
|
|
|
// 表格数据
|
|
|
const tableData = ref({
|
|
|
- items: [],
|
|
|
- total: 0,
|
|
|
- page: 0,
|
|
|
- size: 20,
|
|
|
- totalPages: 0
|
|
|
+ content: [],
|
|
|
+ metadata: {
|
|
|
+ page: 0,
|
|
|
+ size: 20,
|
|
|
+ total: 0
|
|
|
+ }
|
|
|
})
|
|
|
|
|
|
// 筛选条件
|
|
|
@@ -57,8 +58,8 @@ const filters = ref({
|
|
|
const fetchData = async () => {
|
|
|
try {
|
|
|
const params = {
|
|
|
- page: tableData.value.page,
|
|
|
- size: tableData.value.size
|
|
|
+ page: tableData.value.metadata.page,
|
|
|
+ size: tableData.value.metadata.size
|
|
|
}
|
|
|
|
|
|
if (filters.value.status) {
|
|
|
@@ -79,8 +80,8 @@ const fetchData = async () => {
|
|
|
|
|
|
// 分页处理
|
|
|
const handlePageChange = (event) => {
|
|
|
- tableData.value.page = event.page
|
|
|
- tableData.value.size = event.rows
|
|
|
+ tableData.value.metadata.page = event.page
|
|
|
+ tableData.value.metadata.size = event.rows
|
|
|
fetchData()
|
|
|
}
|
|
|
|
|
|
@@ -347,11 +348,12 @@ const canPauseTask = (task) => {
|
|
|
const taskDetailDialog = ref(false)
|
|
|
const currentTask = ref(null)
|
|
|
const taskItemsData = ref({
|
|
|
- items: [],
|
|
|
- total: 0,
|
|
|
- page: 0,
|
|
|
- size: 50,
|
|
|
- totalPages: 0
|
|
|
+ content: [],
|
|
|
+ metadata: {
|
|
|
+ page: 0,
|
|
|
+ size: 50,
|
|
|
+ total: 0
|
|
|
+ }
|
|
|
})
|
|
|
const taskItemFilter = ref({
|
|
|
status: null
|
|
|
@@ -360,7 +362,7 @@ const taskItemFilter = ref({
|
|
|
const openTaskDetailDialog = async (task) => {
|
|
|
currentTask.value = task
|
|
|
taskDetailDialog.value = true
|
|
|
- taskItemsData.value.page = 0
|
|
|
+ taskItemsData.value.metadata.page = 0
|
|
|
taskItemFilter.value.status = null
|
|
|
await fetchTaskItems()
|
|
|
}
|
|
|
@@ -368,8 +370,8 @@ const openTaskDetailDialog = async (task) => {
|
|
|
const fetchTaskItems = async () => {
|
|
|
try {
|
|
|
const params = {
|
|
|
- page: taskItemsData.value.page,
|
|
|
- size: taskItemsData.value.size,
|
|
|
+ page: taskItemsData.value.metadata.page,
|
|
|
+ size: taskItemsData.value.metadata.size,
|
|
|
taskId: currentTask.value.id
|
|
|
}
|
|
|
|
|
|
@@ -390,14 +392,14 @@ const fetchTaskItems = async () => {
|
|
|
}
|
|
|
|
|
|
const handleTaskItemPageChange = (event) => {
|
|
|
- taskItemsData.value.page = event.page
|
|
|
- taskItemsData.value.size = event.rows
|
|
|
+ taskItemsData.value.metadata.page = event.page
|
|
|
+ taskItemsData.value.metadata.size = event.rows
|
|
|
fetchTaskItems()
|
|
|
}
|
|
|
|
|
|
// 应用筛选
|
|
|
const applyFilters = () => {
|
|
|
- tableData.value.page = 0
|
|
|
+ tableData.value.metadata.page = 0
|
|
|
fetchData()
|
|
|
}
|
|
|
|
|
|
@@ -406,7 +408,7 @@ const resetFilters = () => {
|
|
|
filters.value = {
|
|
|
status: null
|
|
|
}
|
|
|
- tableData.value.page = 0
|
|
|
+ tableData.value.metadata.page = 0
|
|
|
fetchData()
|
|
|
}
|
|
|
|
|
|
@@ -446,11 +448,11 @@ onMounted(() => {
|
|
|
</div>
|
|
|
|
|
|
<!-- 数据表格 -->
|
|
|
- <DataTable :value="tableData.items" :paginator="true"
|
|
|
+ <DataTable :value="tableData.content" :paginator="true"
|
|
|
paginatorTemplate="CurrentPageReport FirstPageLink PrevPageLink PageLinks NextPageLink LastPageLink RowsPerPageDropdown"
|
|
|
- currentPageReportTemplate="{totalRecords} 条记录" :rows="tableData.size"
|
|
|
- :rowsPerPageOptions="[10, 20, 50, 100]" :totalRecords="tableData.total" @page="handlePageChange" lazy
|
|
|
- scrollable>
|
|
|
+ currentPageReportTemplate="{totalRecords} 条记录" :rows="tableData.metadata.size"
|
|
|
+ :rowsPerPageOptions="[10, 20, 50, 100]" :totalRecords="tableData.metadata.total" @page="handlePageChange"
|
|
|
+ lazy scrollable>
|
|
|
<Column field="id" header="ID" style="min-width: 80px"></Column>
|
|
|
<Column field="name" header="任务名称" style="min-width: 150px"></Column>
|
|
|
<Column field="message" header="短信内容" style="min-width: 200px">
|
|
|
@@ -460,27 +462,28 @@ onMounted(() => {
|
|
|
</div>
|
|
|
</template>
|
|
|
</Column>
|
|
|
- <Column field="status" header="状态" style="min-width: 100px">
|
|
|
- <template #body="slotProps">
|
|
|
- <Tag :value="TaskStatus[slotProps.data.status]"
|
|
|
- :severity="getStatusSeverity(slotProps.data.status)" />
|
|
|
- </template>
|
|
|
- </Column>
|
|
|
<Column header="进度" style="min-width: 200px">
|
|
|
<template #body="slotProps">
|
|
|
- <div class="space-y-1">
|
|
|
+ <div class="space-y-1 flex flex-col">
|
|
|
<div class="text-sm">
|
|
|
{{ slotProps.data.processed }} / {{ slotProps.data.total }}
|
|
|
(成功: {{ slotProps.data.successed }})
|
|
|
</div>
|
|
|
<ProgressBar :value="calculateProgress(slotProps.data)" :showValue="false"
|
|
|
- style="height: 6px" />
|
|
|
+ style="height: 6px; width: 100%;" />
|
|
|
</div>
|
|
|
</template>
|
|
|
</Column>
|
|
|
- <Column field="createdAt" header="创建时间" style="min-width: 180px">
|
|
|
+ <Column field="status" header="状态" style="min-width: 120px" :pt="{
|
|
|
+ columnHeaderContent: {
|
|
|
+ class: 'justify-center'
|
|
|
+ }
|
|
|
+ }">
|
|
|
<template #body="slotProps">
|
|
|
- {{ formatDate(slotProps.data.createdAt) }}
|
|
|
+ <div class="flex justify-center">
|
|
|
+ <Tag :value="TaskStatus[slotProps.data.status]"
|
|
|
+ :severity="getStatusSeverity(slotProps.data.status)" />
|
|
|
+ </div>
|
|
|
</template>
|
|
|
</Column>
|
|
|
<Column field="startedAt" header="开始时间" style="min-width: 180px">
|
|
|
@@ -488,9 +491,18 @@ onMounted(() => {
|
|
|
{{ formatDate(slotProps.data.startedAt) }}
|
|
|
</template>
|
|
|
</Column>
|
|
|
- <Column header="操作" style="min-width: 250px" frozen alignFrozen="right">
|
|
|
+ <Column field="createdAt" header="创建时间" style="min-width: 180px">
|
|
|
<template #body="slotProps">
|
|
|
- <div class="flex gap-1">
|
|
|
+ {{ formatDate(slotProps.data.createdAt) }}
|
|
|
+ </template>
|
|
|
+ </Column>
|
|
|
+ <Column header="操作" style="min-width: 250px" frozen alignFrozen="right" :pt="{
|
|
|
+ columnHeaderContent: {
|
|
|
+ class: 'justify-center'
|
|
|
+ }
|
|
|
+ }">
|
|
|
+ <template #body="slotProps">
|
|
|
+ <div class="flex gap-1 justify-center">
|
|
|
<Button v-if="canStartTask(slotProps.data)" icon="pi pi-play" severity="success" size="small"
|
|
|
text rounded aria-label="开始" v-tooltip.top="'开始'"
|
|
|
@click="handleStartTask(slotProps.data)" />
|
|
|
@@ -563,44 +575,68 @@ onMounted(() => {
|
|
|
|
|
|
<!-- 任务详情对话框(任务项列表) -->
|
|
|
<Dialog v-model:visible="taskDetailDialog" :modal="true" header="任务详情"
|
|
|
- :style="{ width: '90vw', maxWidth: '1200px' }" position="center">
|
|
|
+ :style="{ width: '70vw', maxWidth: '900px' }" position="center">
|
|
|
<div v-if="currentTask" class="mb-4 p-4 bg-[var(--p-surface-50)] rounded-lg">
|
|
|
<div class="grid grid-cols-2 md:grid-cols-4 gap-4">
|
|
|
<div>
|
|
|
- <div class="text-sm text-gray-500">任务名称</div>
|
|
|
- <div class="font-semibold">{{ currentTask.name }}</div>
|
|
|
+ <div
|
|
|
+ class="text-xs font-medium text-[var(--p-text-muted-color)] uppercase tracking-wider mb-1.5">
|
|
|
+ 任务名称
|
|
|
+ </div>
|
|
|
+ <div class="font-semibold text-[var(--p-text-color)]">{{ currentTask.name }}</div>
|
|
|
</div>
|
|
|
<div>
|
|
|
- <div class="text-sm text-gray-500">状态</div>
|
|
|
+ <div
|
|
|
+ class="text-xs font-medium text-[var(--p-text-muted-color)] uppercase tracking-wider mb-1.5">
|
|
|
+ 状态
|
|
|
+ </div>
|
|
|
<Tag :value="TaskStatus[currentTask.status]"
|
|
|
:severity="getStatusSeverity(currentTask.status)" />
|
|
|
</div>
|
|
|
<div>
|
|
|
- <div class="text-sm text-gray-500">总数</div>
|
|
|
- <div class="font-semibold">{{ currentTask.total }}</div>
|
|
|
+ <div
|
|
|
+ class="text-xs font-medium text-[var(--p-text-muted-color)] uppercase tracking-wider mb-1.5">
|
|
|
+ 总数
|
|
|
+ </div>
|
|
|
+ <div class="font-semibold text-[var(--p-text-color)]">{{ currentTask.total }}</div>
|
|
|
</div>
|
|
|
<div>
|
|
|
- <div class="text-sm text-gray-500">已处理/成功</div>
|
|
|
- <div class="font-semibold">{{ currentTask.processed }} / {{ currentTask.successed }}</div>
|
|
|
+ <div
|
|
|
+ class="text-xs font-medium text-[var(--p-text-muted-color)] uppercase tracking-wider mb-1.5">
|
|
|
+ 已处理/成功
|
|
|
+ </div>
|
|
|
+ <div class="font-semibold text-[var(--p-text-color)]">{{ currentTask.processed }} / {{
|
|
|
+ currentTask.successed }}</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
+ <!-- 短信内容展示 -->
|
|
|
+ <div v-if="currentTask"
|
|
|
+ class="mb-4 p-4 bg-[var(--p-surface-50)] rounded-lg border border-[var(--p-content-border-color)]">
|
|
|
+ <div class="text-xs font-medium text-[var(--p-text-muted-color)] uppercase tracking-wider mb-2">短信内容
|
|
|
+ </div>
|
|
|
+ <div class="text-[var(--p-text-color)] leading-relaxed whitespace-pre-wrap break-words">{{
|
|
|
+ currentTask.message
|
|
|
+ }}</div>
|
|
|
+ </div>
|
|
|
+
|
|
|
<div class="mb-4 flex items-center gap-2">
|
|
|
- <FloatLabel variant="on" class="flex-1">
|
|
|
- <Select id="itemStatusFilter" v-model="taskItemFilter.status" :options="taskItemStatusOptions"
|
|
|
- optionLabel="label" optionValue="value" showClear fluid />
|
|
|
- <label for="itemStatusFilter">任务项状态</label>
|
|
|
- </FloatLabel>
|
|
|
+ <div class="field w-40">
|
|
|
+ <FloatLabel variant="on">
|
|
|
+ <Select id="itemStatusFilter" v-model="taskItemFilter.status" :options="taskItemStatusOptions"
|
|
|
+ optionLabel="label" optionValue="value" showClear fluid size="small" />
|
|
|
+ <label for="itemStatusFilter">任务项状态</label>
|
|
|
+ </FloatLabel>
|
|
|
+ </div>
|
|
|
<Button icon="pi pi-search" label="筛选" @click="fetchTaskItems" size="small" />
|
|
|
</div>
|
|
|
|
|
|
- <DataTable :value="taskItemsData.items" :paginator="true"
|
|
|
+ <DataTable :value="taskItemsData.content" :paginator="true"
|
|
|
paginatorTemplate="CurrentPageReport FirstPageLink PrevPageLink PageLinks NextPageLink LastPageLink"
|
|
|
- currentPageReportTemplate="{totalRecords} 条记录" :rows="taskItemsData.size"
|
|
|
- :totalRecords="taskItemsData.total" @page="handleTaskItemPageChange" lazy scrollable
|
|
|
+ currentPageReportTemplate="{totalRecords} 条记录" :rows="taskItemsData.metadata.size"
|
|
|
+ :totalRecords="taskItemsData.metadata.total" @page="handleTaskItemPageChange" lazy scrollable
|
|
|
:scrollHeight="'400px'">
|
|
|
- <Column field="id" header="ID" style="min-width: 80px"></Column>
|
|
|
<Column field="target" header="手机号" style="min-width: 150px"></Column>
|
|
|
<Column field="status" header="状态" style="min-width: 100px">
|
|
|
<template #body="slotProps">
|
|
|
@@ -614,14 +650,6 @@ onMounted(() => {
|
|
|
" />
|
|
|
</template>
|
|
|
</Column>
|
|
|
- <Column field="errorMsg" header="错误信息" style="min-width: 200px">
|
|
|
- <template #body="slotProps">
|
|
|
- <div v-if="slotProps.data.errorMsg" class="text-red-500 text-sm">
|
|
|
- {{ slotProps.data.errorMsg }}
|
|
|
- </div>
|
|
|
- <div v-else>-</div>
|
|
|
- </template>
|
|
|
- </Column>
|
|
|
<Column field="operatingAt" header="操作时间" style="min-width: 180px">
|
|
|
<template #body="slotProps">
|
|
|
{{ formatDate(slotProps.data.operatingAt) }}
|