2822 lines
91 KiB
Plaintext
2822 lines
91 KiB
Plaintext
<template>
|
||
<DefaultLayout>
|
||
<div class="task-hall-page">
|
||
<div class="container">
|
||
|
||
<!-- 数据总览 -->
|
||
<div class="overview-section">
|
||
<div class="overview-header">
|
||
<h2 class="section-title">数据总览</h2>
|
||
<div class="overview-actions">
|
||
<a-month-picker
|
||
v-model:value="selectedMonth"
|
||
placeholder="选择月份"
|
||
format="YYYY-MM"
|
||
@change="handleMonthChange"
|
||
style="width: 150px; margin-right: 12px;"
|
||
/>
|
||
<a-button type="primary" size="large" @click="handleCreateTask">
|
||
<Icon name="plus" size="sm" style="margin-right: 6px;" />
|
||
发起任务
|
||
</a-button>
|
||
</div>
|
||
</div>
|
||
<a-row :gutter="[16, 16]">
|
||
<a-col
|
||
v-for="card in dataCards"
|
||
:key="card.id"
|
||
:xs="24"
|
||
:sm="12"
|
||
:md="8"
|
||
:lg="4"
|
||
>
|
||
<div class="stat-card">
|
||
<div class="stat-icon">
|
||
<Icon :name="card.iconName" size="xl" color="#31f08b" />
|
||
</div>
|
||
<div class="stat-content">
|
||
<div class="stat-label">{{ card.label }}</div>
|
||
<div class="stat-value">{{ card.value }}</div>
|
||
</div>
|
||
</div>
|
||
</a-col>
|
||
</a-row>
|
||
</div>
|
||
|
||
<!-- 标签页切换 -->
|
||
<a-tabs v-model:activeKey="activeTab" @change="handleTabChange" class="task-tabs">
|
||
<a-tab-pane key="available" tab="可接任务">
|
||
<!-- 搜索和筛选栏(合并) -->
|
||
<div class="search-filter-bar chart-card">
|
||
<!-- 第一行:搜索、排序和统计 -->
|
||
<div class="search-row">
|
||
<a-input-search
|
||
v-model:value="filters.keyword"
|
||
placeholder="搜索任务标题、SPU信息、发布人..."
|
||
class="search-input"
|
||
@search="handleFilterChange"
|
||
@pressEnter="handleFilterChange"
|
||
/>
|
||
<a-select
|
||
v-model:value="filters.sortBy"
|
||
placeholder="排序方式"
|
||
class="sort-select"
|
||
@change="handleFilterChange"
|
||
>
|
||
<a-select-option value="latest">最新发布</a-select-option>
|
||
<a-select-option value="score">积分最高</a-select-option>
|
||
<a-select-option value="deadline">时效最短</a-select-option>
|
||
<a-select-option value="hot">最热门</a-select-option>
|
||
</a-select>
|
||
<span class="stats-text">共 {{ total }} 个任务</span>
|
||
</div>
|
||
|
||
<!-- 第二行:筛选条件 -->
|
||
<div class="filter-row">
|
||
<a-select
|
||
v-model:value="filters.taskTypeId"
|
||
placeholder="任务类型"
|
||
class="filter-select"
|
||
allowClear
|
||
@change="handleFilterChange"
|
||
>
|
||
<a-select-option
|
||
v-for="type in demandTypes"
|
||
:key="type.id"
|
||
:value="type.id"
|
||
>
|
||
{{ type.name }}
|
||
</a-select-option>
|
||
</a-select>
|
||
|
||
<a-select
|
||
v-model:value="filters.demandTypeId"
|
||
placeholder="需求类型"
|
||
class="filter-select"
|
||
allowClear
|
||
@change="handleFilterChange"
|
||
>
|
||
<a-select-option
|
||
v-for="type in demandTypes"
|
||
:key="type.id"
|
||
:value="type.id"
|
||
>
|
||
{{ type.name }}
|
||
</a-select-option>
|
||
</a-select>
|
||
|
||
<a-select
|
||
v-model:value="filters.brandId"
|
||
placeholder="品牌"
|
||
class="filter-select"
|
||
allowClear
|
||
@change="handleFilterChange"
|
||
>
|
||
<a-select-option
|
||
v-for="brand in brands"
|
||
:key="brand.id"
|
||
:value="brand.id"
|
||
>
|
||
{{ brand.name }}
|
||
</a-select-option>
|
||
</a-select>
|
||
|
||
<a-select
|
||
v-model:value="filters.categoryId"
|
||
placeholder="类目"
|
||
class="filter-select"
|
||
allowClear
|
||
@change="handleFilterChange"
|
||
>
|
||
<a-select-option
|
||
v-for="category in categories"
|
||
:key="category.id"
|
||
:value="category.id"
|
||
>
|
||
{{ category.name }}
|
||
</a-select-option>
|
||
</a-select>
|
||
|
||
<a-select
|
||
v-model:value="filters.platformId"
|
||
placeholder="平台"
|
||
class="filter-select"
|
||
allowClear
|
||
@change="handleFilterChange"
|
||
>
|
||
<a-select-option
|
||
v-for="platform in platforms"
|
||
:key="platform.id"
|
||
:value="platform.id"
|
||
>
|
||
{{ platform.name }}
|
||
</a-select-option>
|
||
</a-select>
|
||
|
||
<a-select
|
||
v-model:value="filters.timeRange"
|
||
placeholder="发布时间"
|
||
class="filter-select"
|
||
allowClear
|
||
@change="handleFilterChange"
|
||
>
|
||
<a-select-option value="today">今天</a-select-option>
|
||
<a-select-option value="week">本周</a-select-option>
|
||
<a-select-option value="month">本月</a-select-option>
|
||
</a-select>
|
||
|
||
<div class="score-range">
|
||
<a-input
|
||
v-model:value="filters.minScore"
|
||
placeholder="最小积分"
|
||
class="score-input"
|
||
@pressEnter="handleFilterChange"
|
||
/>
|
||
<span class="score-separator">-</span>
|
||
<a-input
|
||
v-model:value="filters.maxScore"
|
||
placeholder="最大积分"
|
||
class="score-input"
|
||
@pressEnter="handleFilterChange"
|
||
/>
|
||
</div>
|
||
|
||
<a-button @click="handleReset" class="reset-btn">重置</a-button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 任务列表 -->
|
||
<div v-if="loading" class="loading-container">
|
||
<a-spin size="large" />
|
||
</div>
|
||
|
||
<div v-else-if="taskList.length === 0" class="empty-state chart-card">
|
||
<div class="empty-icon">
|
||
<Icon name="inbox" size="xl" color="#6B6B6B" />
|
||
</div>
|
||
<h3>暂无可接任务</h3>
|
||
<p>当前没有待接单的任务,请稍后再来查看</p>
|
||
</div>
|
||
|
||
<div v-else class="task-grid">
|
||
<div
|
||
v-for="task in taskList"
|
||
:key="task.id"
|
||
class="task-card chart-card"
|
||
@click="handleTaskClick(task)"
|
||
>
|
||
<!-- 任务标题 -->
|
||
<div class="task-header">
|
||
<h3 class="task-title">{{ task.title }}</h3>
|
||
<div class="task-badges">
|
||
<span v-if="isNewTask(task)" class="badge badge-new">
|
||
<Icon name="sparkles" size="sm" color="#31f08b" style="margin-right: 4px; vertical-align: middle;" />
|
||
新发布
|
||
</span>
|
||
<span v-if="isHighScoreTask(task)" class="badge badge-high-score">
|
||
<Icon name="star" size="sm" color="#F59E0B" style="margin-right: 4px; vertical-align: middle;" />
|
||
高分
|
||
</span>
|
||
<span v-if="task.isUrgent" class="badge badge-urgent">
|
||
<Icon name="fire" size="sm" color="#F59E0B" style="margin-right: 4px; vertical-align: middle;" />
|
||
紧急
|
||
</span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 任务信息 -->
|
||
<div class="task-info">
|
||
<div class="info-item">
|
||
<span class="label">任务类型</span>
|
||
<span class="value">{{ getTaskTypeName(task.taskTypeId) }}</span>
|
||
</div>
|
||
<div class="info-item">
|
||
<span class="label">预估积分</span>
|
||
<span class="value text-brand">{{ task.score }} 分</span>
|
||
</div>
|
||
<div class="info-item">
|
||
<span class="label">数量</span>
|
||
<span class="value">{{ task.quantity || '--' }} 个</span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 任务详情 -->
|
||
<div class="task-details">
|
||
<div v-if="task.brandName" class="detail-item">
|
||
<span class="icon">
|
||
<Icon name="tag" size="sm" color="#31f08b" />
|
||
</span>
|
||
<span>品牌: {{ task.brandName }}</span>
|
||
</div>
|
||
<div v-if="task.categoryName" class="detail-item">
|
||
<span class="icon">
|
||
<Icon name="folder" size="sm" color="#31f08b" />
|
||
</span>
|
||
<span>类目: {{ task.categoryName }}</span>
|
||
</div>
|
||
<div v-if="task.platformName" class="detail-item">
|
||
<span class="icon">
|
||
<Icon name="monitor" size="sm" color="#31f08b" />
|
||
</span>
|
||
<span>平台: {{ task.platformName }}</span>
|
||
</div>
|
||
<div v-if="task.spuInfo" class="detail-item">
|
||
<span class="icon">
|
||
<Icon name="tag" size="sm" color="#31f08b" />
|
||
</span>
|
||
<span>SPU: {{ task.spuInfo }}</span>
|
||
</div>
|
||
<div class="detail-item">
|
||
<span class="icon">
|
||
<Icon name="user" size="sm" color="#31f08b" />
|
||
</span>
|
||
<span>发布人: {{ task.publisherName || '未知' }}</span>
|
||
</div>
|
||
<div class="detail-item">
|
||
<span class="icon">
|
||
<Icon name="clock" size="sm" color="#31f08b" />
|
||
</span>
|
||
<span>截止: {{ formatDeadline(task.deadline) }}</span>
|
||
</div>
|
||
<div class="detail-item">
|
||
<span class="icon">
|
||
<Icon name="calendar" size="sm" color="#31f08b" />
|
||
</span>
|
||
<span>发布时间: {{ formatTime(task.createTime) }}</span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 操作按钮 -->
|
||
<div class="task-actions">
|
||
<button
|
||
class="btn btn-secondary"
|
||
style="flex: 1"
|
||
@click.stop="handleTaskClick(task)"
|
||
>
|
||
查看详情
|
||
</button>
|
||
<button
|
||
class="btn btn-primary"
|
||
style="flex: 1"
|
||
@click.stop="handleGrab(task)"
|
||
>
|
||
抢单
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 分页 -->
|
||
<div v-if="total > 0" class="pagination">
|
||
<a-pagination
|
||
v-model:current="pagination.page"
|
||
v-model:page-size="pagination.pageSize"
|
||
:total="total"
|
||
:show-size-changer="false"
|
||
@change="handlePageChange"
|
||
/>
|
||
</div>
|
||
</a-tab-pane>
|
||
|
||
<!-- 进行中任务标签页 -->
|
||
<a-tab-pane key="inProgress" tab="进行中任务">
|
||
<div class="search-filter-bar chart-card">
|
||
<!-- 第一行:搜索、排序和统计 -->
|
||
<div class="search-row">
|
||
<a-input-search
|
||
v-model:value="inProgressFilters.keyword"
|
||
placeholder="搜索任务标题、SPU信息、发布人..."
|
||
class="search-input"
|
||
@search="loadInProgressTasks"
|
||
@pressEnter="loadInProgressTasks"
|
||
/>
|
||
<a-select
|
||
v-model:value="inProgressFilters.sortBy"
|
||
placeholder="排序方式"
|
||
class="sort-select"
|
||
@change="loadInProgressTasks"
|
||
>
|
||
<a-select-option value="latest">最新发布</a-select-option>
|
||
<a-select-option value="deadline">截止最近</a-select-option>
|
||
<a-select-option value="progress">进度最低</a-select-option>
|
||
</a-select>
|
||
<span class="stats-text">共 {{ inProgressTotal }} 个进行中任务</span>
|
||
</div>
|
||
|
||
<!-- 第二行:筛选条件 -->
|
||
<div class="filter-row">
|
||
<a-select
|
||
v-model:value="inProgressFilters.taskTypeId"
|
||
placeholder="任务类型"
|
||
class="filter-select"
|
||
allowClear
|
||
@change="loadInProgressTasks"
|
||
>
|
||
<a-select-option
|
||
v-for="type in demandTypes"
|
||
:key="type.id"
|
||
:value="type.id"
|
||
>
|
||
{{ type.name }}
|
||
</a-select-option>
|
||
</a-select>
|
||
|
||
<a-select
|
||
v-model:value="inProgressFilters.brandId"
|
||
placeholder="品牌"
|
||
class="filter-select"
|
||
allowClear
|
||
@change="loadInProgressTasks"
|
||
>
|
||
<a-select-option
|
||
v-for="brand in brands"
|
||
:key="brand.id"
|
||
:value="brand.id"
|
||
>
|
||
{{ brand.name }}
|
||
</a-select-option>
|
||
</a-select>
|
||
|
||
<a-select
|
||
v-model:value="inProgressFilters.categoryId"
|
||
placeholder="类目"
|
||
class="filter-select"
|
||
allowClear
|
||
@change="loadInProgressTasks"
|
||
>
|
||
<a-select-option
|
||
v-for="category in categories"
|
||
:key="category.id"
|
||
:value="category.id"
|
||
>
|
||
{{ category.name }}
|
||
</a-select-option>
|
||
</a-select>
|
||
|
||
<a-select
|
||
v-model:value="inProgressFilters.platformId"
|
||
placeholder="平台"
|
||
class="filter-select"
|
||
allowClear
|
||
@change="loadInProgressTasks"
|
||
>
|
||
<a-select-option
|
||
v-for="platform in platforms"
|
||
:key="platform.id"
|
||
:value="platform.id"
|
||
>
|
||
{{ platform.name }}
|
||
</a-select-option>
|
||
</a-select>
|
||
|
||
<a-select
|
||
v-model:value="inProgressFilters.timeRange"
|
||
placeholder="发布时间"
|
||
class="filter-select"
|
||
allowClear
|
||
@change="loadInProgressTasks"
|
||
>
|
||
<a-select-option value="today">今天</a-select-option>
|
||
<a-select-option value="week">本周</a-select-option>
|
||
<a-select-option value="month">本月</a-select-option>
|
||
</a-select>
|
||
</div>
|
||
|
||
<!-- 第三行:进度范围和重置 -->
|
||
<div class="filter-row">
|
||
<a-input-number
|
||
v-model:value="inProgressFilters.minProgress"
|
||
placeholder="最小进度%"
|
||
class="score-input"
|
||
:min="0"
|
||
:max="100"
|
||
@change="loadInProgressTasks"
|
||
/>
|
||
<span class="score-divider">-</span>
|
||
<a-input-number
|
||
v-model:value="inProgressFilters.maxProgress"
|
||
placeholder="最大进度%"
|
||
class="score-input"
|
||
:min="0"
|
||
:max="100"
|
||
@change="loadInProgressTasks"
|
||
/>
|
||
<a-button class="reset-btn" @click="handleInProgressFiltersReset">
|
||
重置
|
||
</a-button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 进行中任务列表 -->
|
||
<div v-if="inProgressLoading" class="loading-container">
|
||
<a-spin size="large" />
|
||
</div>
|
||
|
||
<div v-else-if="inProgressTaskList.length === 0" class="empty-state chart-card">
|
||
<div class="empty-icon">
|
||
<Icon name="clock" size="xl" color="#6B6B6B" />
|
||
</div>
|
||
<h3>暂无进行中任务</h3>
|
||
<p>还没有进行中的任务</p>
|
||
</div>
|
||
|
||
<div v-else class="task-grid">
|
||
<div
|
||
v-for="task in inProgressTaskList"
|
||
:key="task.id"
|
||
class="task-card chart-card in-progress"
|
||
>
|
||
<!-- 任务标题 -->
|
||
<div class="task-header">
|
||
<h3 class="task-title">{{ task.title }}</h3>
|
||
<div class="task-badges">
|
||
<span class="badge badge-in-progress">
|
||
<Icon name="clock" size="sm" color="#F59E0B" style="margin-right: 4px; vertical-align: middle;" />
|
||
进行中
|
||
</span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 任务信息 -->
|
||
<div class="task-info">
|
||
<div class="info-item">
|
||
<span class="label">任务编号</span>
|
||
<span class="value">{{ task.taskNo }}</span>
|
||
</div>
|
||
<div class="info-item">
|
||
<span class="label">任务类型</span>
|
||
<span class="value">{{ getTaskTypeName(task.taskTypeId) }}</span>
|
||
</div>
|
||
<div class="info-item">
|
||
<span class="label">预估积分</span>
|
||
<span class="value text-brand">{{ task.score }} 分</span>
|
||
</div>
|
||
<div class="info-item">
|
||
<span class="label">数量</span>
|
||
<span class="value">{{ task.quantity || '--' }} 个</span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 任务详情 -->
|
||
<div class="task-details">
|
||
<div class="info-item">
|
||
<span class="label">发起人</span>
|
||
<span class="value">{{ task.publisherName || '--' }}</span>
|
||
</div>
|
||
<div class="info-item">
|
||
<span class="label">接单人</span>
|
||
<span class="value">{{ task.receiverName || '--' }}</span>
|
||
</div>
|
||
<div v-if="task.brandName" class="detail-item">
|
||
<span class="icon">
|
||
<Icon name="tag" size="sm" color="#31f08b" />
|
||
</span>
|
||
<span>品牌: {{ task.brandName }}</span>
|
||
</div>
|
||
<div v-if="task.categoryName" class="detail-item">
|
||
<span class="icon">
|
||
<Icon name="folder" size="sm" color="#31f08b" />
|
||
</span>
|
||
<span>类目: {{ task.categoryName }}</span>
|
||
</div>
|
||
<div v-if="task.platformName" class="detail-item">
|
||
<span class="icon">
|
||
<Icon name="monitor" size="sm" color="#31f08b" />
|
||
</span>
|
||
<span>平台: {{ task.platformName }}</span>
|
||
</div>
|
||
<div v-if="task.spuInfo" class="detail-item">
|
||
<span class="icon">
|
||
<Icon name="package" size="sm" color="#31f08b" />
|
||
</span>
|
||
<span>SPU: {{ task.spuInfo }}</span>
|
||
</div>
|
||
<div v-if="task.taskSubType" class="detail-item">
|
||
<span class="icon">
|
||
<Icon name="list" size="sm" color="#31f08b" />
|
||
</span>
|
||
<span>子类型: {{ task.taskSubType }}</span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 进度条 -->
|
||
<div class="task-progress">
|
||
<div class="progress-label">
|
||
<span>任务进度</span>
|
||
<span>{{ task.progress || 0 }}%</span>
|
||
</div>
|
||
<a-progress
|
||
:percent="task.progress || 0"
|
||
:stroke-color="{ '0%': '#31f08b', '100%': '#31f08b' }"
|
||
:show-info="false"
|
||
/>
|
||
</div>
|
||
|
||
<!-- 操作按钮 -->
|
||
<div class="task-actions">
|
||
<a-button size="small" @click="handleViewTaskDetail(task)">查看详情</a-button>
|
||
<a-button v-if="canUpdateProgress(task)" type="primary" size="small" @click="handleUpdateProgress(task)">
|
||
更新进度
|
||
</a-button>
|
||
<a-button v-if="canCompleteTask(task)" type="primary" size="small" @click="handleCompleteTask(task)">
|
||
完结任务
|
||
</a-button>
|
||
</div>
|
||
|
||
<!-- 时间信息 -->
|
||
<div class="task-footer">
|
||
<div class="footer-item">
|
||
<Icon name="calendar" size="sm" color="#31f08b" />
|
||
<span style="color: #ffffff;">发布: {{ formatDate(task.createTime) }}</span>
|
||
</div>
|
||
<div class="footer-item" v-if="task.deadline">
|
||
<Icon name="clock" size="sm" color="#31f08b" />
|
||
<span style="color: #ffffff;">截止: {{ formatDate(task.deadline) }}</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 分页 -->
|
||
<div v-if="inProgressTotal > 0" class="pagination">
|
||
<a-pagination
|
||
v-model:current="inProgressPagination.page"
|
||
v-model:page-size="inProgressPagination.pageSize"
|
||
:total="inProgressTotal"
|
||
:show-size-changer="false"
|
||
@change="handleInProgressPageChange"
|
||
/>
|
||
</div>
|
||
</a-tab-pane>
|
||
|
||
<!-- 待审核任务标签页 -->
|
||
<a-tab-pane key="review" tab="待审核任务">
|
||
<div class="search-filter-bar chart-card">
|
||
<!-- 第一行:搜索、排序和统计 -->
|
||
<div class="search-row">
|
||
<a-input-search
|
||
v-model:value="reviewFilters.keyword"
|
||
placeholder="搜索任务标题、SPU信息、发布人..."
|
||
class="search-input"
|
||
@search="loadReviewTasks"
|
||
@pressEnter="loadReviewTasks"
|
||
/>
|
||
<a-select
|
||
v-model:value="reviewFilters.sortBy"
|
||
placeholder="排序方式"
|
||
class="sort-select"
|
||
@change="loadReviewTasks"
|
||
>
|
||
<a-select-option value="latest">最新提交</a-select-option>
|
||
<a-select-option value="score">积分最高</a-select-option>
|
||
</a-select>
|
||
<span class="stats-text">共 {{ reviewTotal }} 个待审核任务</span>
|
||
</div>
|
||
|
||
<!-- 第二行:筛选条件 -->
|
||
<div class="filter-row">
|
||
<a-select
|
||
v-model:value="reviewFilters.taskTypeId"
|
||
placeholder="任务类型"
|
||
class="filter-select"
|
||
allowClear
|
||
@change="loadReviewTasks"
|
||
>
|
||
<a-select-option
|
||
v-for="type in demandTypes"
|
||
:key="type.id"
|
||
:value="type.id"
|
||
>
|
||
{{ type.name }}
|
||
</a-select-option>
|
||
</a-select>
|
||
|
||
<a-select
|
||
v-model:value="reviewFilters.brandId"
|
||
placeholder="品牌"
|
||
class="filter-select"
|
||
allowClear
|
||
@change="loadReviewTasks"
|
||
>
|
||
<a-select-option
|
||
v-for="brand in brands"
|
||
:key="brand.id"
|
||
:value="brand.id"
|
||
>
|
||
{{ brand.name }}
|
||
</a-select-option>
|
||
</a-select>
|
||
|
||
<a-select
|
||
v-model:value="reviewFilters.categoryId"
|
||
placeholder="类目"
|
||
class="filter-select"
|
||
allowClear
|
||
@change="loadReviewTasks"
|
||
>
|
||
<a-select-option
|
||
v-for="category in categories"
|
||
:key="category.id"
|
||
:value="category.id"
|
||
>
|
||
{{ category.name }}
|
||
</a-select-option>
|
||
</a-select>
|
||
|
||
<a-select
|
||
v-model:value="reviewFilters.platformId"
|
||
placeholder="平台"
|
||
class="filter-select"
|
||
allowClear
|
||
@change="loadReviewTasks"
|
||
>
|
||
<a-select-option
|
||
v-for="platform in platforms"
|
||
:key="platform.id"
|
||
:value="platform.id"
|
||
>
|
||
{{ platform.name }}
|
||
</a-select-option>
|
||
</a-select>
|
||
|
||
<a-select
|
||
v-model:value="reviewFilters.timeRange"
|
||
placeholder="提交时间"
|
||
class="filter-select"
|
||
allowClear
|
||
@change="loadReviewTasks"
|
||
>
|
||
<a-select-option value="today">今天</a-select-option>
|
||
<a-select-option value="week">本周</a-select-option>
|
||
<a-select-option value="month">本月</a-select-option>
|
||
</a-select>
|
||
</div>
|
||
|
||
<!-- 第三行:积分范围和重置 -->
|
||
<div class="filter-row">
|
||
<a-input-number
|
||
v-model:value="reviewFilters.minScore"
|
||
placeholder="最小积分"
|
||
class="score-input"
|
||
:min="0"
|
||
@change="loadReviewTasks"
|
||
/>
|
||
<span class="score-divider">-</span>
|
||
<a-input-number
|
||
v-model:value="reviewFilters.maxScore"
|
||
placeholder="最大积分"
|
||
class="score-input"
|
||
:min="0"
|
||
@change="loadReviewTasks"
|
||
/>
|
||
<a-button class="reset-btn" @click="handleReviewFiltersReset">
|
||
重置
|
||
</a-button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 待审核任务列表 -->
|
||
<div v-if="reviewLoading" class="loading-container">
|
||
<a-spin size="large" />
|
||
</div>
|
||
|
||
<div v-else-if="reviewTaskList.length === 0" class="empty-state chart-card">
|
||
<div class="empty-icon">
|
||
<Icon name="file-text" size="xl" color="#6B6B6B" />
|
||
</div>
|
||
<h3>暂无待审核任务</h3>
|
||
<p>还没有需要审核的任务</p>
|
||
</div>
|
||
|
||
<div v-else class="task-grid">
|
||
<div
|
||
v-for="task in reviewTaskList"
|
||
:key="task.id"
|
||
class="task-card chart-card review"
|
||
>
|
||
<!-- 任务标题 -->
|
||
<div class="task-header">
|
||
<h3 class="task-title">{{ task.title }}</h3>
|
||
<div class="task-badges">
|
||
<span class="badge badge-review">
|
||
<Icon name="file-text" size="sm" color="#3B82F6" style="margin-right: 4px; vertical-align: middle;" />
|
||
待审核
|
||
</span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 任务信息 -->
|
||
<div class="task-info">
|
||
<div class="info-item">
|
||
<span class="label">任务编号</span>
|
||
<span class="value">{{ task.taskNo }}</span>
|
||
</div>
|
||
<div class="info-item">
|
||
<span class="label">任务类型</span>
|
||
<span class="value">{{ getTaskTypeName(task.taskTypeId) }}</span>
|
||
</div>
|
||
<div class="info-item">
|
||
<span class="label">预估积分</span>
|
||
<span class="value text-brand">{{ task.score }} 分</span>
|
||
</div>
|
||
<div class="info-item">
|
||
<span class="label">数量</span>
|
||
<span class="value">{{ task.quantity || '--' }} 个</span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 任务详情 -->
|
||
<div class="task-details">
|
||
<div class="info-item">
|
||
<span class="label">发起人</span>
|
||
<span class="value">{{ task.publisherName || '--' }}</span>
|
||
</div>
|
||
<div class="info-item">
|
||
<span class="label">接单人</span>
|
||
<span class="value">{{ task.receiverName || '--' }}</span>
|
||
</div>
|
||
<div v-if="task.brandName" class="detail-item">
|
||
<span class="icon">
|
||
<Icon name="tag" size="sm" color="#31f08b" />
|
||
</span>
|
||
<span>品牌: {{ task.brandName }}</span>
|
||
</div>
|
||
<div v-if="task.categoryName" class="detail-item">
|
||
<span class="icon">
|
||
<Icon name="folder" size="sm" color="#31f08b" />
|
||
</span>
|
||
<span>类目: {{ task.categoryName }}</span>
|
||
</div>
|
||
<div v-if="task.platformName" class="detail-item">
|
||
<span class="icon">
|
||
<Icon name="monitor" size="sm" color="#31f08b" />
|
||
</span>
|
||
<span>平台: {{ task.platformName }}</span>
|
||
</div>
|
||
<div v-if="task.spuInfo" class="detail-item">
|
||
<span class="icon">
|
||
<Icon name="package" size="sm" color="#31f08b" />
|
||
</span>
|
||
<span>SPU: {{ task.spuInfo }}</span>
|
||
</div>
|
||
<div v-if="task.taskSubType" class="detail-item">
|
||
<span class="icon">
|
||
<Icon name="list" size="sm" color="#31f08b" />
|
||
</span>
|
||
<span>子类型: {{ task.taskSubType }}</span>
|
||
</div>
|
||
<div v-if="task.deliveryPath" class="detail-item">
|
||
<span class="icon">
|
||
<Icon name="link" size="sm" color="#31f08b" />
|
||
</span>
|
||
<span>交付路径: {{ task.deliveryPath }}</span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 操作按钮 -->
|
||
<div class="task-actions">
|
||
<a-button size="small" @click="handleViewTaskDetail(task)">查看详情</a-button>
|
||
<a-button v-if="canApproveTask(task)" type="primary" size="small" @click="handleApproveTask(task)">
|
||
通过审核
|
||
</a-button>
|
||
<a-button v-if="canRejectTask(task)" danger size="small" @click="handleRejectTask(task)">
|
||
驳回任务
|
||
</a-button>
|
||
</div>
|
||
|
||
<!-- 时间信息 -->
|
||
<div class="task-footer">
|
||
<div class="footer-item">
|
||
<Icon name="calendar" size="sm" color="#31f08b" />
|
||
<span style="color: #ffffff;">提交: {{ formatDate(task.submitTime || task.updateTime) }}</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 分页 -->
|
||
<div v-if="reviewTotal > 0" class="pagination">
|
||
<a-pagination
|
||
v-model:current="reviewPagination.page"
|
||
v-model:page-size="reviewPagination.pageSize"
|
||
:total="reviewTotal"
|
||
:show-size-changer="false"
|
||
@change="handleReviewPageChange"
|
||
/>
|
||
</div>
|
||
</a-tab-pane>
|
||
|
||
<!-- 已完成任务标签页 -->
|
||
<a-tab-pane key="completed" tab="已完成任务">
|
||
<div class="search-filter-bar chart-card">
|
||
<!-- 第一行:搜索、排序和统计 -->
|
||
<div class="search-row">
|
||
<a-input-search
|
||
v-model:value="completedFilters.keyword"
|
||
placeholder="搜索任务标题、SPU信息、发布人..."
|
||
class="search-input"
|
||
@search="loadCompletedTasks"
|
||
@pressEnter="loadCompletedTasks"
|
||
/>
|
||
<a-select
|
||
v-model:value="completedFilters.sortBy"
|
||
placeholder="排序方式"
|
||
class="sort-select"
|
||
@change="loadCompletedTasks"
|
||
>
|
||
<a-select-option value="latest">最新完成</a-select-option>
|
||
<a-select-option value="score">积分最高</a-select-option>
|
||
</a-select>
|
||
<span class="stats-text">共 {{ completedTotal }} 个已完成任务</span>
|
||
</div>
|
||
|
||
<!-- 第二行:筛选条件 -->
|
||
<div class="filter-row">
|
||
<a-select
|
||
v-model:value="completedFilters.taskTypeId"
|
||
placeholder="任务类型"
|
||
class="filter-select"
|
||
allowClear
|
||
@change="loadCompletedTasks"
|
||
>
|
||
<a-select-option
|
||
v-for="type in demandTypes"
|
||
:key="type.id"
|
||
:value="type.id"
|
||
>
|
||
{{ type.name }}
|
||
</a-select-option>
|
||
</a-select>
|
||
|
||
<a-select
|
||
v-model:value="completedFilters.demandTypeId"
|
||
placeholder="需求类型"
|
||
class="filter-select"
|
||
allowClear
|
||
@change="loadCompletedTasks"
|
||
>
|
||
<a-select-option
|
||
v-for="type in demandTypes"
|
||
:key="type.id"
|
||
:value="type.id"
|
||
>
|
||
{{ type.name }}
|
||
</a-select-option>
|
||
</a-select>
|
||
|
||
<a-select
|
||
v-model:value="completedFilters.brandId"
|
||
placeholder="品牌"
|
||
class="filter-select"
|
||
allowClear
|
||
@change="loadCompletedTasks"
|
||
>
|
||
<a-select-option
|
||
v-for="brand in brands"
|
||
:key="brand.id"
|
||
:value="brand.id"
|
||
>
|
||
{{ brand.name }}
|
||
</a-select-option>
|
||
</a-select>
|
||
|
||
<a-select
|
||
v-model:value="completedFilters.categoryId"
|
||
placeholder="类目"
|
||
class="filter-select"
|
||
allowClear
|
||
@change="loadCompletedTasks"
|
||
>
|
||
<a-select-option
|
||
v-for="category in categories"
|
||
:key="category.id"
|
||
:value="category.id"
|
||
>
|
||
{{ category.name }}
|
||
</a-select-option>
|
||
</a-select>
|
||
|
||
<a-select
|
||
v-model:value="completedFilters.platformId"
|
||
placeholder="平台"
|
||
class="filter-select"
|
||
allowClear
|
||
@change="loadCompletedTasks"
|
||
>
|
||
<a-select-option
|
||
v-for="platform in platforms"
|
||
:key="platform.id"
|
||
:value="platform.id"
|
||
>
|
||
{{ platform.name }}
|
||
</a-select-option>
|
||
</a-select>
|
||
|
||
<a-select
|
||
v-model:value="completedFilters.timeRange"
|
||
placeholder="发布时间"
|
||
class="filter-select"
|
||
allowClear
|
||
@change="loadCompletedTasks"
|
||
>
|
||
<a-select-option value="today">今天</a-select-option>
|
||
<a-select-option value="week">本周</a-select-option>
|
||
<a-select-option value="month">本月</a-select-option>
|
||
</a-select>
|
||
</div>
|
||
|
||
<!-- 第三行:积分范围和重置 -->
|
||
<div class="filter-row">
|
||
<a-input-number
|
||
v-model:value="completedFilters.minScore"
|
||
placeholder="最小积分"
|
||
class="score-input"
|
||
:min="0"
|
||
@change="loadCompletedTasks"
|
||
/>
|
||
<span class="score-divider">-</span>
|
||
<a-input-number
|
||
v-model:value="completedFilters.maxScore"
|
||
placeholder="最大积分"
|
||
class="score-input"
|
||
:min="0"
|
||
@change="loadCompletedTasks"
|
||
/>
|
||
<a-button class="reset-btn" @click="handleCompletedFiltersReset">
|
||
重置
|
||
</a-button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 已完成任务列表 -->
|
||
<div v-if="completedLoading" class="loading-container">
|
||
<a-spin size="large" />
|
||
</div>
|
||
|
||
<div v-else-if="completedTaskList.length === 0" class="empty-state chart-card">
|
||
<div class="empty-icon">
|
||
<Icon name="check-circle" size="xl" color="#6B6B6B" />
|
||
</div>
|
||
<h3>暂无已完成任务</h3>
|
||
<p>还没有完成的任务记录</p>
|
||
</div>
|
||
|
||
<div v-else class="task-grid">
|
||
<div
|
||
v-for="task in completedTaskList"
|
||
:key="task.id"
|
||
class="task-card chart-card completed"
|
||
>
|
||
<!-- 任务标题 -->
|
||
<div class="task-header">
|
||
<h3 class="task-title">{{ task.title }}</h3>
|
||
<div class="task-badges">
|
||
<span class="badge badge-completed">
|
||
<Icon name="check-circle" size="sm" color="#31f08b" style="margin-right: 4px; vertical-align: middle;" />
|
||
已完成
|
||
</span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 任务信息 -->
|
||
<div class="task-info">
|
||
<div class="info-item">
|
||
<span class="label">任务编号</span>
|
||
<span class="value">{{ task.taskNo }}</span>
|
||
</div>
|
||
<div class="info-item">
|
||
<span class="label">任务类型</span>
|
||
<span class="value">{{ getTaskTypeName(task.taskTypeId) }}</span>
|
||
</div>
|
||
<div class="info-item">
|
||
<span class="label">获得积分</span>
|
||
<span class="value text-brand">{{ task.score }} 分</span>
|
||
</div>
|
||
<div class="info-item">
|
||
<span class="label">数量</span>
|
||
<span class="value">{{ task.quantity || '--' }} 个</span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 任务详情 -->
|
||
<div class="task-details">
|
||
<div class="info-item">
|
||
<span class="label">发起人</span>
|
||
<span class="value">{{ task.publisherName || '--' }}</span>
|
||
</div>
|
||
<div class="info-item">
|
||
<span class="label">完成人</span>
|
||
<span class="value">{{ task.receiverName || '--' }}</span>
|
||
</div>
|
||
<div v-if="task.brandName" class="detail-item">
|
||
<span class="icon">
|
||
<Icon name="tag" size="sm" color="#31f08b" />
|
||
</span>
|
||
<span>品牌: {{ task.brandName }}</span>
|
||
</div>
|
||
<div v-if="task.categoryName" class="detail-item">
|
||
<span class="icon">
|
||
<Icon name="folder" size="sm" color="#31f08b" />
|
||
</span>
|
||
<span>类目: {{ task.categoryName }}</span>
|
||
</div>
|
||
<div v-if="task.platformName" class="detail-item">
|
||
<span class="icon">
|
||
<Icon name="monitor" size="sm" color="#31f08b" />
|
||
</span>
|
||
<span>平台: {{ task.platformName }}</span>
|
||
</div>
|
||
<div v-if="task.spuInfo" class="detail-item">
|
||
<span class="icon">
|
||
<Icon name="package" size="sm" color="#31f08b" />
|
||
</span>
|
||
<span>SPU: {{ task.spuInfo }}</span>
|
||
</div>
|
||
<div v-if="task.taskSubType" class="detail-item">
|
||
<span class="icon">
|
||
<Icon name="list" size="sm" color="#31f08b" />
|
||
</span>
|
||
<span>子类型: {{ task.taskSubType }}</span>
|
||
</div>
|
||
<div v-if="task.deliveryPath" class="detail-item">
|
||
<span class="icon">
|
||
<Icon name="link" size="sm" color="#31f08b" />
|
||
</span>
|
||
<span>交付: {{ task.deliveryPath }}</span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 时间信息 -->
|
||
<div class="task-footer">
|
||
<div class="footer-item">
|
||
<Icon name="calendar" size="sm" color="#31f08b" />
|
||
<span style="color: #ffffff;">发布: {{ formatDate(task.createTime) }}</span>
|
||
</div>
|
||
<div class="footer-item">
|
||
<Icon name="check" size="sm" color="#31f08b" />
|
||
<span style="color: #ffffff;">完成: {{ formatDate(task.completeTime) }}</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 分页 -->
|
||
<div v-if="completedTotal > 0" class="pagination">
|
||
<a-pagination
|
||
v-model:current="completedPagination.page"
|
||
v-model:page-size="completedPagination.pageSize"
|
||
:total="completedTotal"
|
||
:show-size-changer="false"
|
||
@change="handleCompletedPageChange"
|
||
/>
|
||
</div>
|
||
</a-tab-pane>
|
||
|
||
<!-- 我发起的任务标签页 -->
|
||
<a-tab-pane key="published" tab="我发起的任务">
|
||
<div class="search-filter-bar chart-card">
|
||
<!-- 第一行:搜索、排序和统计 -->
|
||
<div class="search-row">
|
||
<a-input-search
|
||
v-model:value="publishedFilters.keyword"
|
||
placeholder="搜索任务标题、SPU信息、发布人..."
|
||
class="search-input"
|
||
@search="loadPublishedTasks"
|
||
@pressEnter="loadPublishedTasks"
|
||
/>
|
||
<a-select
|
||
v-model:value="publishedFilters.sortBy"
|
||
placeholder="排序方式"
|
||
class="sort-select"
|
||
@change="loadPublishedTasks"
|
||
>
|
||
<a-select-option value="latest">最新发布</a-select-option>
|
||
<a-select-option value="score">积分最高</a-select-option>
|
||
<a-select-option value="deadline">时效最短</a-select-option>
|
||
</a-select>
|
||
<span class="stats-text">共 {{ publishedTotal }} 个任务</span>
|
||
</div>
|
||
|
||
<!-- 第二行:筛选条件 -->
|
||
<div class="filter-row">
|
||
<a-select
|
||
v-model:value="publishedFilters.status"
|
||
placeholder="任务状态"
|
||
class="filter-select"
|
||
allowClear
|
||
@change="loadPublishedTasks"
|
||
>
|
||
<a-select-option value="">全部</a-select-option>
|
||
<a-select-option value="pending">待接单</a-select-option>
|
||
<a-select-option value="in_progress">进行中</a-select-option>
|
||
<a-select-option value="review">待审核</a-select-option>
|
||
<a-select-option value="completed">已完成</a-select-option>
|
||
<a-select-option value="rejected">已驳回</a-select-option>
|
||
</a-select>
|
||
|
||
<a-select
|
||
v-model:value="publishedFilters.taskTypeId"
|
||
placeholder="任务类型"
|
||
class="filter-select"
|
||
allowClear
|
||
@change="loadPublishedTasks"
|
||
>
|
||
<a-select-option
|
||
v-for="type in demandTypes"
|
||
:key="type.id"
|
||
:value="type.id"
|
||
>
|
||
{{ type.name }}
|
||
</a-select-option>
|
||
</a-select>
|
||
|
||
<a-select
|
||
v-model:value="publishedFilters.brandId"
|
||
placeholder="品牌"
|
||
class="filter-select"
|
||
allowClear
|
||
@change="loadPublishedTasks"
|
||
>
|
||
<a-select-option
|
||
v-for="brand in brands"
|
||
:key="brand.id"
|
||
:value="brand.id"
|
||
>
|
||
{{ brand.name }}
|
||
</a-select-option>
|
||
</a-select>
|
||
|
||
<a-select
|
||
v-model:value="publishedFilters.categoryId"
|
||
placeholder="类目"
|
||
class="filter-select"
|
||
allowClear
|
||
@change="loadPublishedTasks"
|
||
>
|
||
<a-select-option
|
||
v-for="category in categories"
|
||
:key="category.id"
|
||
:value="category.id"
|
||
>
|
||
{{ category.name }}
|
||
</a-select-option>
|
||
</a-select>
|
||
|
||
<a-select
|
||
v-model:value="publishedFilters.platformId"
|
||
placeholder="平台"
|
||
class="filter-select"
|
||
allowClear
|
||
@change="loadPublishedTasks"
|
||
>
|
||
<a-select-option
|
||
v-for="platform in platforms"
|
||
:key="platform.id"
|
||
:value="platform.id"
|
||
>
|
||
{{ platform.name }}
|
||
</a-select-option>
|
||
</a-select>
|
||
|
||
<a-select
|
||
v-model:value="publishedFilters.timeRange"
|
||
placeholder="发布时间"
|
||
class="filter-select"
|
||
allowClear
|
||
@change="loadPublishedTasks"
|
||
>
|
||
<a-select-option value="today">今天</a-select-option>
|
||
<a-select-option value="week">本周</a-select-option>
|
||
<a-select-option value="month">本月</a-select-option>
|
||
</a-select>
|
||
</div>
|
||
|
||
<!-- 第三行:积分范围和重置 -->
|
||
<div class="filter-row">
|
||
<a-input-number
|
||
v-model:value="publishedFilters.minScore"
|
||
placeholder="最小积分"
|
||
class="score-input"
|
||
:min="0"
|
||
@change="loadPublishedTasks"
|
||
/>
|
||
<span class="score-divider">-</span>
|
||
<a-input-number
|
||
v-model:value="publishedFilters.maxScore"
|
||
placeholder="最大积分"
|
||
class="score-input"
|
||
:min="0"
|
||
@change="loadPublishedTasks"
|
||
/>
|
||
<a-button class="reset-btn" @click="handlePublishedFiltersReset">
|
||
重置
|
||
</a-button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 我发起的任务列表 -->
|
||
<div v-if="publishedLoading" class="loading-container">
|
||
<a-spin size="large" />
|
||
</div>
|
||
|
||
<div v-else-if="publishedTaskList.length === 0" class="empty-state chart-card">
|
||
<div class="empty-icon">
|
||
<Icon name="inbox" size="xl" color="#6B6B6B" />
|
||
</div>
|
||
<h3>暂无发起的任务</h3>
|
||
<p>您还没有发起任何任务</p>
|
||
<router-link
|
||
to="/tasks/create"
|
||
class="btn btn-primary"
|
||
style="margin-top: 16px"
|
||
>
|
||
+ 发起新任务
|
||
</router-link>
|
||
</div>
|
||
|
||
<div v-else class="task-grid">
|
||
<div
|
||
v-for="task in publishedTaskList"
|
||
:key="task.id"
|
||
class="task-card chart-card"
|
||
:class="[`status-${task.status}`]"
|
||
>
|
||
<!-- 任务标题 -->
|
||
<div class="task-header">
|
||
<h3 class="task-title">{{ task.title }}</h3>
|
||
<div class="task-badges">
|
||
<span :class="['badge', `status-${task.status}`]">
|
||
{{ getTaskStatusText(task.status) }}
|
||
</span>
|
||
<span v-if="task.isUrgent" class="badge badge-urgent">
|
||
<Icon name="fire" size="sm" color="#F59E0B" style="margin-right: 4px; vertical-align: middle;" />
|
||
紧急
|
||
</span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 任务信息 -->
|
||
<div class="task-info">
|
||
<div class="info-item">
|
||
<span class="label">任务编号</span>
|
||
<span class="value">{{ task.taskNo }}</span>
|
||
</div>
|
||
<div class="info-item">
|
||
<span class="label">任务类型</span>
|
||
<span class="value">{{ getTaskTypeName(task.taskTypeId) }}</span>
|
||
</div>
|
||
<div class="info-item">
|
||
<span class="label">积分</span>
|
||
<span class="value text-brand">{{ task.score }} 分</span>
|
||
</div>
|
||
<div class="info-item">
|
||
<span class="label">数量</span>
|
||
<span class="value">{{ task.quantity || '--' }} 个</span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 任务详情 -->
|
||
<div class="task-details">
|
||
<div class="info-item">
|
||
<span class="label">接单人</span>
|
||
<span class="value">{{ task.receiverName || '--' }}</span>
|
||
</div>
|
||
<div class="info-item">
|
||
<span class="label">进度</span>
|
||
<span class="value">{{ task.progress || 0 }}%</span>
|
||
</div>
|
||
<div v-if="task.brandName" class="detail-item">
|
||
<span class="icon">
|
||
<Icon name="tag" size="sm" color="#31f08b" />
|
||
</span>
|
||
<span>品牌: {{ task.brandName }}</span>
|
||
</div>
|
||
<div v-if="task.categoryName" class="detail-item">
|
||
<span class="icon">
|
||
<Icon name="folder" size="sm" color="#31f08b" />
|
||
</span>
|
||
<span>类目: {{ task.categoryName }}</span>
|
||
</div>
|
||
<div v-if="task.platformName" class="detail-item">
|
||
<span class="icon">
|
||
<Icon name="monitor" size="sm" color="#31f08b" />
|
||
</span>
|
||
<span>平台: {{ task.platformName }}</span>
|
||
</div>
|
||
<div v-if="task.spuInfo" class="detail-item">
|
||
<span class="icon">
|
||
<Icon name="package" size="sm" color="#31f08b" />
|
||
</span>
|
||
<span>SPU: {{ task.spuInfo }}</span>
|
||
</div>
|
||
<div v-if="task.taskSubType" class="detail-item">
|
||
<span class="icon">
|
||
<Icon name="list" size="sm" color="#31f08b" />
|
||
</span>
|
||
<span>子类型: {{ task.taskSubType }}</span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 时间信息 -->
|
||
<div class="task-footer">
|
||
<div class="footer-item">
|
||
<Icon name="calendar" size="sm" color="#31f08b" />
|
||
<span style="color: #ffffff;">发布: {{ formatDate(task.createTime) }}</span>
|
||
</div>
|
||
<div class="footer-item" v-if="task.deadline">
|
||
<Icon name="clock" size="sm" color="#31f08b" />
|
||
<span style="color: #ffffff;">截止: {{ formatDate(task.deadline) }}</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 分页 -->
|
||
<div v-if="publishedTotal > 0" class="pagination">
|
||
<a-pagination
|
||
v-model:current="publishedPagination.page"
|
||
v-model:page-size="publishedPagination.pageSize"
|
||
:total="publishedTotal"
|
||
:show-size-changer="false"
|
||
@change="handlePublishedPageChange"
|
||
/>
|
||
</div>
|
||
</a-tab-pane>
|
||
</a-tabs>
|
||
</div>
|
||
|
||
<!-- 任务详情抽屉 -->
|
||
<a-drawer
|
||
v-model:visible="detailDrawerVisible"
|
||
title="任务详情"
|
||
:width="600"
|
||
placement="right"
|
||
>
|
||
<div v-if="selectedTask" class="task-detail">
|
||
<div class="detail-section">
|
||
<h3>{{ selectedTask.title }}</h3>
|
||
<div class="detail-badges">
|
||
<span v-if="isNewTask(selectedTask)" class="badge badge-new">新发布</span>
|
||
<span v-if="isHighScoreTask(selectedTask)" class="badge badge-high-score">高分</span>
|
||
<span v-if="selectedTask.isUrgent" class="badge badge-urgent">紧急</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="detail-section">
|
||
<h4>基本信息</h4>
|
||
<a-descriptions :column="2" bordered>
|
||
<a-descriptions-item label="任务编号">{{ selectedTask.taskNo }}</a-descriptions-item>
|
||
<a-descriptions-item label="任务类型">{{ getTaskTypeName(selectedTask.taskTypeId) }}</a-descriptions-item>
|
||
<a-descriptions-item label="预估积分">{{ selectedTask.score }} 分</a-descriptions-item>
|
||
<a-descriptions-item label="预估工时">{{ selectedTask.estimatedHours || '--' }} 小时</a-descriptions-item>
|
||
<a-descriptions-item label="数量">{{ selectedTask.quantity || '--' }} 个</a-descriptions-item>
|
||
<a-descriptions-item label="截止时间">{{ formatDeadline(selectedTask.deadline) }}</a-descriptions-item>
|
||
</a-descriptions>
|
||
</div>
|
||
|
||
<div class="detail-section">
|
||
<h4>关联信息</h4>
|
||
<a-descriptions :column="2" bordered>
|
||
<a-descriptions-item label="品牌">{{ selectedTask.brandName || '--' }}</a-descriptions-item>
|
||
<a-descriptions-item label="类目">{{ selectedTask.categoryName || '--' }}</a-descriptions-item>
|
||
<a-descriptions-item label="平台">{{ selectedTask.platformName || '--' }}</a-descriptions-item>
|
||
<a-descriptions-item label="发布人">{{ selectedTask.publisherName || '未知' }}</a-descriptions-item>
|
||
</a-descriptions>
|
||
</div>
|
||
|
||
<div v-if="selectedTask.spuInfo" class="detail-section">
|
||
<h4>SPU信息</h4>
|
||
<p>{{ selectedTask.spuInfo }}</p>
|
||
</div>
|
||
|
||
<div v-if="selectedTask.remark" class="detail-section">
|
||
<h4>备注说明</h4>
|
||
<p>{{ selectedTask.remark }}</p>
|
||
</div>
|
||
|
||
<div v-if="selectedTask.attachmentUrl" class="detail-section">
|
||
<h4>附件</h4>
|
||
<div class="attachment-links">
|
||
<a v-for="(url, index) in (selectedTask.attachmentUrl || '').split(',')"
|
||
:key="index"
|
||
:href="url.trim()"
|
||
target="_blank"
|
||
class="attachment-link">
|
||
<Icon name="download" size="sm" style="margin-right: 4px;" />
|
||
附件 {{ index + 1 }}
|
||
</a>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 根据任务状态显示不同的操作按钮 -->
|
||
<div class="detail-actions">
|
||
<!-- 可接任务:显示抢单 -->
|
||
<a-button
|
||
v-if="selectedTask.status === 'pending'"
|
||
type="primary"
|
||
size="large"
|
||
block
|
||
@click="handleGrabFromDetail">
|
||
确认抢单
|
||
</a-button>
|
||
|
||
<!-- 进行中任务:显示更新进度和完结 -->
|
||
<div v-else-if="selectedTask.status === 'in_progress'" class="action-buttons">
|
||
<a-button
|
||
v-if="canUpdateProgress(selectedTask)"
|
||
type="primary"
|
||
size="large"
|
||
@click="handleUpdateProgress(selectedTask); detailDrawerVisible = false">
|
||
更新进度
|
||
</a-button>
|
||
<a-button
|
||
v-if="canCompleteTask(selectedTask)"
|
||
type="primary"
|
||
size="large"
|
||
@click="handleCompleteTask(selectedTask); detailDrawerVisible = false">
|
||
完结任务
|
||
</a-button>
|
||
</div>
|
||
|
||
<!-- 待审核任务:显示审核按钮 -->
|
||
<div v-else-if="selectedTask.status === 'review'" class="action-buttons">
|
||
<a-button
|
||
v-if="canApproveTask(selectedTask)"
|
||
type="primary"
|
||
size="large"
|
||
@click="handleApproveTask(selectedTask); detailDrawerVisible = false">
|
||
通过审核
|
||
</a-button>
|
||
<a-button
|
||
v-if="canRejectTask(selectedTask)"
|
||
danger
|
||
size="large"
|
||
@click="handleRejectTask(selectedTask); detailDrawerVisible = false">
|
||
驳回任务
|
||
</a-button>
|
||
</div>
|
||
|
||
<!-- 已完成任务:显示评分(如果还没评分) -->
|
||
<a-button
|
||
v-else-if="selectedTask.status === 'completed' && !selectedTask.rating && canApproveTask(selectedTask)"
|
||
type="primary"
|
||
size="large"
|
||
block>
|
||
评分
|
||
</a-button>
|
||
|
||
<!-- 其他状态:仅显示关闭按钮 -->
|
||
<a-button
|
||
v-else
|
||
size="large"
|
||
block
|
||
@click="detailDrawerVisible = false">
|
||
关闭
|
||
</a-button>
|
||
</div>
|
||
</div>
|
||
</a-drawer>
|
||
</div>
|
||
</DefaultLayout>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import { ref, reactive, computed, onMounted, onUnmounted, h } from 'vue'
|
||
import { useRouter } from 'vue-router'
|
||
import { message, Modal, InputNumber, Textarea } from 'ant-design-vue'
|
||
import DefaultLayout from '@/layouts/DefaultLayout.vue'
|
||
import Icon from '@/components/Icon.vue'
|
||
import * as taskApi from '@/api/task'
|
||
import * as configApi from '@/api/config'
|
||
import type { Task } from '@/api/task'
|
||
import type { DemandType, Brand, Platform, Category } from '@/api/config'
|
||
import { useConfigStore } from '@/stores/config'
|
||
import dayjs from 'dayjs'
|
||
|
||
const router = useRouter()
|
||
|
||
// 全局配置store
|
||
const configStore = useConfigStore()
|
||
|
||
// 标签页
|
||
const activeTab = ref('available')
|
||
|
||
// 状态
|
||
const loading = ref(false)
|
||
const taskList = ref<Task[]>([])
|
||
const total = ref(0)
|
||
const detailDrawerVisible = ref(false)
|
||
const selectedTask = ref<Task | null>(null)
|
||
|
||
// 已完成任务状态
|
||
const completedLoading = ref(false)
|
||
const completedTaskList = ref<Task[]>([])
|
||
const completedTotal = ref(0)
|
||
|
||
// 我发起的任务状态
|
||
const publishedLoading = ref(false)
|
||
const publishedTaskList = ref<Task[]>([])
|
||
const publishedTotal = ref(0)
|
||
|
||
// 进行中任务状态
|
||
const inProgressLoading = ref(false)
|
||
const inProgressTaskList = ref<Task[]>([])
|
||
const inProgressTotal = ref(0)
|
||
|
||
// 待审核任务状态
|
||
const reviewLoading = ref(false)
|
||
const reviewTaskList = ref<Task[]>([])
|
||
const reviewTotal = ref(0)
|
||
|
||
// 数据总览卡片
|
||
const dataCards = ref([
|
||
{ id: 1, label: '可接任务数', value: 0, iconName: 'inbox' },
|
||
{ id: 2, label: '进行中任务', value: 0, iconName: 'clock' },
|
||
{ id: 3, label: '已完成任务', value: 0, iconName: 'check-circle' },
|
||
{ id: 4, label: '待审核任务', value: 0, iconName: 'file-text' },
|
||
{ id: 5, label: '当日新增任务', value: 0, iconName: 'plus-circle' },
|
||
{ id: 6, label: '累计积分', value: 0, iconName: 'award' }
|
||
])
|
||
|
||
// 月份选择
|
||
const selectedMonth = ref(dayjs())
|
||
|
||
// 使用全局配置store的数据
|
||
const demandTypes = computed(() => configStore.demandTypes)
|
||
const brands = computed(() => configStore.brands)
|
||
const platforms = computed(() => configStore.platforms)
|
||
const categories = computed(() => configStore.categories)
|
||
|
||
// 筛选条件
|
||
const filters = reactive({
|
||
taskTypeId: undefined as number | undefined,
|
||
demandTypeId: undefined as number | undefined,
|
||
brandId: undefined as number | undefined,
|
||
categoryId: undefined as number | undefined,
|
||
platformId: undefined as number | undefined,
|
||
taskSubType: undefined as string | undefined,
|
||
minScore: undefined as number | undefined,
|
||
maxScore: undefined as number | undefined,
|
||
keyword: undefined as string | undefined,
|
||
publisherName: undefined as string | undefined,
|
||
timeRange: undefined as 'today' | 'week' | 'month' | undefined,
|
||
sortBy: 'latest' as 'latest' | 'score' | 'deadline' | 'hot'
|
||
})
|
||
|
||
// 已完成任务筛选条件
|
||
const completedFilters = reactive({
|
||
keyword: undefined as string | undefined,
|
||
sortBy: 'latest' as 'latest' | 'score',
|
||
taskTypeId: undefined as number | undefined,
|
||
demandTypeId: undefined as number | undefined,
|
||
brandId: undefined as number | undefined,
|
||
categoryId: undefined as number | undefined,
|
||
platformId: undefined as number | undefined,
|
||
timeRange: undefined as 'today' | 'week' | 'month' | undefined,
|
||
minScore: undefined as number | undefined,
|
||
maxScore: undefined as number | undefined
|
||
})
|
||
|
||
// 我发起的任务筛选条件
|
||
const publishedFilters = reactive({
|
||
keyword: undefined as string | undefined,
|
||
status: '' as string,
|
||
sortBy: 'latest' as 'latest' | 'score' | 'deadline',
|
||
taskTypeId: undefined as number | undefined,
|
||
brandId: undefined as number | undefined,
|
||
categoryId: undefined as number | undefined,
|
||
platformId: undefined as number | undefined,
|
||
timeRange: undefined as 'today' | 'week' | 'month' | undefined,
|
||
minScore: undefined as number | undefined,
|
||
maxScore: undefined as number | undefined
|
||
})
|
||
|
||
// 进行中任务筛选条件
|
||
const inProgressFilters = reactive({
|
||
keyword: undefined as string | undefined,
|
||
sortBy: 'latest' as 'latest' | 'deadline' | 'progress',
|
||
taskTypeId: undefined as number | undefined,
|
||
brandId: undefined as number | undefined,
|
||
categoryId: undefined as number | undefined,
|
||
platformId: undefined as number | undefined,
|
||
timeRange: undefined as 'today' | 'week' | 'month' | undefined,
|
||
minProgress: undefined as number | undefined,
|
||
maxProgress: undefined as number | undefined
|
||
})
|
||
|
||
// 待审核任务筛选条件
|
||
const reviewFilters = reactive({
|
||
keyword: undefined as string | undefined,
|
||
sortBy: 'latest' as 'latest' | 'score',
|
||
taskTypeId: undefined as number | undefined,
|
||
brandId: undefined as number | undefined,
|
||
categoryId: undefined as number | undefined,
|
||
platformId: undefined as number | undefined,
|
||
timeRange: undefined as 'today' | 'week' | 'month' | undefined,
|
||
minScore: undefined as number | undefined,
|
||
maxScore: undefined as number | undefined
|
||
})
|
||
|
||
// 分页
|
||
const pagination = reactive({
|
||
page: 1,
|
||
pageSize: 12
|
||
})
|
||
|
||
// 已完成任务分页
|
||
const completedPagination = reactive({
|
||
page: 1,
|
||
pageSize: 12
|
||
})
|
||
|
||
// 获取任务类型名称
|
||
const getTaskTypeName = (typeId: number) => {
|
||
const type = demandTypes.value.find(t => t.id === typeId)
|
||
return type?.name || '未知类型'
|
||
}
|
||
|
||
// 格式化时间
|
||
const formatTime = (time: string | undefined) => {
|
||
if (!time) return '--'
|
||
return dayjs(time).format('YYYY-MM-DD HH:mm')
|
||
}
|
||
|
||
// 格式化日期(用于已完成任务)
|
||
const formatDate = (date: string | undefined) => {
|
||
if (!date) return '--'
|
||
return dayjs(date).format('YYYY-MM-DD')
|
||
}
|
||
|
||
// 格式化截止时间
|
||
const formatDeadline = (deadline: string | undefined) => {
|
||
if (!deadline) return '--'
|
||
const deadlineTime = dayjs(deadline)
|
||
const now = dayjs()
|
||
const diffHours = deadlineTime.diff(now, 'hour')
|
||
|
||
if (diffHours < 0) {
|
||
return `已过期 (${deadlineTime.format('YYYY-MM-DD HH:mm')})`
|
||
} else if (diffHours < 24) {
|
||
return `${diffHours} 小时后截止`
|
||
} else {
|
||
const diffDays = Math.floor(diffHours / 24)
|
||
return `${diffDays} 天后截止 (${deadlineTime.format('YYYY-MM-DD HH:mm')})`
|
||
}
|
||
}
|
||
|
||
// 判断是否为新发布任务(1小时内)
|
||
const isNewTask = (task: Task) => {
|
||
if (!task.createTime) return false
|
||
const createTime = dayjs(task.createTime)
|
||
const now = dayjs()
|
||
return now.diff(createTime, 'hour') < 1
|
||
}
|
||
|
||
// 判断是否为高分任务(积分 >= 500)
|
||
const isHighScoreTask = (task: Task) => {
|
||
return task.score && task.score >= 500
|
||
}
|
||
|
||
// 加载配置数据(从全局store加载)
|
||
const loadConfigs = async () => {
|
||
try {
|
||
await configStore.loadAllConfigs()
|
||
} catch (error) {
|
||
console.error('加载配置数据失败:', error)
|
||
}
|
||
}
|
||
|
||
// 加载任务列表
|
||
const loadTasks = async () => {
|
||
loading.value = true
|
||
try {
|
||
const params: any = {
|
||
page: pagination.page,
|
||
pageSize: pagination.pageSize
|
||
}
|
||
|
||
// 添加筛选条件
|
||
if (filters.taskTypeId) params.taskTypeId = filters.taskTypeId
|
||
if (filters.demandTypeId) params.demandTypeId = filters.demandTypeId
|
||
if (filters.brandId) params.brandId = filters.brandId
|
||
if (filters.categoryId) params.categoryId = filters.categoryId
|
||
if (filters.platformId) params.platformId = filters.platformId
|
||
if (filters.taskSubType) params.taskSubType = filters.taskSubType
|
||
if (filters.minScore) params.minScore = Number(filters.minScore)
|
||
if (filters.maxScore) params.maxScore = Number(filters.maxScore)
|
||
if (filters.keyword) params.keyword = filters.keyword.trim()
|
||
if (filters.publisherName) params.publisherName = filters.publisherName.trim()
|
||
if (filters.timeRange) params.timeRange = filters.timeRange
|
||
if (filters.sortBy) params.sortBy = filters.sortBy
|
||
|
||
const response = await taskApi.getTaskHall(params)
|
||
|
||
if (response.code === 200) {
|
||
taskList.value = response.data.records || []
|
||
total.value = response.data.total || 0
|
||
}
|
||
} catch (error) {
|
||
console.error('加载任务失败:', error)
|
||
message.error('加载任务失败')
|
||
} finally {
|
||
loading.value = false
|
||
}
|
||
}
|
||
|
||
// 筛选变更
|
||
const handleFilterChange = () => {
|
||
pagination.page = 1
|
||
loadTasks()
|
||
}
|
||
|
||
// 重置筛选
|
||
const handleReset = () => {
|
||
filters.taskTypeId = undefined
|
||
filters.demandTypeId = undefined
|
||
filters.brandId = undefined
|
||
filters.categoryId = undefined
|
||
filters.platformId = undefined
|
||
filters.taskSubType = undefined
|
||
filters.minScore = undefined
|
||
filters.maxScore = undefined
|
||
filters.keyword = undefined
|
||
filters.publisherName = undefined
|
||
filters.timeRange = undefined
|
||
filters.sortBy = 'latest'
|
||
pagination.page = 1
|
||
loadTasks()
|
||
}
|
||
|
||
// 页码变更
|
||
const handlePageChange = () => {
|
||
loadTasks()
|
||
}
|
||
|
||
// 加载已完成任务列表
|
||
const loadCompletedTasks = async () => {
|
||
completedLoading.value = true
|
||
try {
|
||
const params: any = {
|
||
page: completedPagination.page,
|
||
pageSize: completedPagination.pageSize,
|
||
status: 'completed'
|
||
}
|
||
|
||
// 添加筛选条件
|
||
if (completedFilters.keyword) params.keyword = completedFilters.keyword
|
||
if (completedFilters.sortBy) params.sortBy = completedFilters.sortBy
|
||
|
||
const response = await taskApi.getCompletedTasks(params)
|
||
completedTaskList.value = response.data.records || []
|
||
completedTotal.value = response.data.total || 0
|
||
} catch (error) {
|
||
console.error('加载已完成任务失败:', error)
|
||
message.error('加载已完成任务失败')
|
||
} finally {
|
||
completedLoading.value = false
|
||
}
|
||
}
|
||
|
||
// 标签页切换
|
||
const handleTabChange = (key: string) => {
|
||
if (key === 'completed') {
|
||
loadCompletedTasks()
|
||
} else if (key === 'published') {
|
||
loadPublishedTasks()
|
||
} else if (key === 'inProgress') {
|
||
loadInProgressTasks()
|
||
} else if (key === 'review') {
|
||
loadReviewTasks()
|
||
} else {
|
||
loadTasks()
|
||
}
|
||
}
|
||
|
||
// 已完成任务分页变化
|
||
const handleCompletedPageChange = (page: number) => {
|
||
completedPagination.page = page
|
||
loadCompletedTasks()
|
||
}
|
||
|
||
// 我发起的任务分页
|
||
const publishedPagination = reactive({
|
||
page: 1,
|
||
pageSize: 10
|
||
})
|
||
|
||
// 进行中任务分页
|
||
const inProgressPagination = reactive({
|
||
page: 1,
|
||
pageSize: 12
|
||
})
|
||
|
||
// 待审核任务分页
|
||
const reviewPagination = reactive({
|
||
page: 1,
|
||
pageSize: 12
|
||
})
|
||
|
||
// 加载我发起的任务
|
||
const loadPublishedTasks = async () => {
|
||
publishedLoading.value = true
|
||
try {
|
||
const response = await taskApi.getMyTasks({
|
||
role: 'publisher',
|
||
status: publishedFilters.status || undefined,
|
||
keyword: publishedFilters.keyword || undefined,
|
||
page: publishedPagination.page,
|
||
pageSize: publishedPagination.pageSize
|
||
})
|
||
|
||
if (response.code === 200) {
|
||
publishedTaskList.value = response.data.records || []
|
||
publishedTotal.value = response.data.total || 0
|
||
}
|
||
} catch (error) {
|
||
console.error('加载我发起的任务失败:', error)
|
||
message.error('加载失败')
|
||
} finally {
|
||
publishedLoading.value = false
|
||
}
|
||
}
|
||
|
||
// 我发起的任务分页变化
|
||
const handlePublishedPageChange = (page: number) => {
|
||
publishedPagination.page = page
|
||
loadPublishedTasks()
|
||
}
|
||
|
||
// 重置已完成任务筛选条件
|
||
const handleCompletedFiltersReset = () => {
|
||
completedFilters.keyword = undefined
|
||
completedFilters.sortBy = 'latest'
|
||
completedFilters.taskTypeId = undefined
|
||
completedFilters.demandTypeId = undefined
|
||
completedFilters.brandId = undefined
|
||
completedFilters.categoryId = undefined
|
||
completedFilters.platformId = undefined
|
||
completedFilters.timeRange = undefined
|
||
completedFilters.minScore = undefined
|
||
completedFilters.maxScore = undefined
|
||
completedPagination.page = 1
|
||
loadCompletedTasks()
|
||
}
|
||
|
||
// 重置我发起的任务筛选条件
|
||
const handlePublishedFiltersReset = () => {
|
||
publishedFilters.keyword = undefined
|
||
publishedFilters.status = ''
|
||
publishedFilters.sortBy = 'latest'
|
||
publishedFilters.taskTypeId = undefined
|
||
publishedFilters.brandId = undefined
|
||
publishedFilters.categoryId = undefined
|
||
publishedFilters.platformId = undefined
|
||
publishedFilters.timeRange = undefined
|
||
publishedFilters.minScore = undefined
|
||
publishedFilters.maxScore = undefined
|
||
publishedPagination.page = 1
|
||
loadPublishedTasks()
|
||
}
|
||
|
||
// 加载数据总览
|
||
const loadDashboardStats = async () => {
|
||
try {
|
||
const month = selectedMonth.value?.format('YYYY-MM')
|
||
const response = await taskApi.getDashboardStats(month ? { month } : undefined)
|
||
if (response.code === 200) {
|
||
const data = response.data
|
||
dataCards.value[0].value = data.availableCount || 0 // 可接任务数
|
||
dataCards.value[1].value = data.inProgressCount || 0 // 进行中任务
|
||
dataCards.value[2].value = data.completedCount || 0 // 已完成任务
|
||
dataCards.value[3].value = data.reviewCount || 0 // 待审核任务
|
||
dataCards.value[4].value = data.todayNewCount || 0 // 当日新增任务
|
||
dataCards.value[5].value = data.totalScore || 0 // 累计积分
|
||
}
|
||
} catch (error) {
|
||
console.error('加载数据总览失败:', error)
|
||
}
|
||
}
|
||
|
||
// 月份选择改变
|
||
const handleMonthChange = () => {
|
||
loadDashboardStats()
|
||
}
|
||
|
||
// 发起任务
|
||
const handleCreateTask = () => {
|
||
router.push('/tasks/create')
|
||
}
|
||
|
||
// 加载进行中任务
|
||
const loadInProgressTasks = async () => {
|
||
inProgressLoading.value = true
|
||
try {
|
||
const params: any = {
|
||
page: inProgressPagination.page,
|
||
pageSize: inProgressPagination.pageSize,
|
||
status: 'in_progress'
|
||
}
|
||
|
||
// 添加筛选条件
|
||
if (inProgressFilters.keyword) params.keyword = inProgressFilters.keyword
|
||
if (inProgressFilters.sortBy) params.sortBy = inProgressFilters.sortBy
|
||
if (inProgressFilters.taskTypeId) params.taskTypeId = inProgressFilters.taskTypeId
|
||
if (inProgressFilters.brandId) params.brandId = inProgressFilters.brandId
|
||
if (inProgressFilters.categoryId) params.categoryId = inProgressFilters.categoryId
|
||
if (inProgressFilters.platformId) params.platformId = inProgressFilters.platformId
|
||
if (inProgressFilters.timeRange) params.timeRange = inProgressFilters.timeRange
|
||
if (inProgressFilters.minProgress !== undefined) params.minProgress = inProgressFilters.minProgress
|
||
if (inProgressFilters.maxProgress !== undefined) params.maxProgress = inProgressFilters.maxProgress
|
||
|
||
const response = await taskApi.getTasks(params)
|
||
inProgressTaskList.value = response.data.records || []
|
||
inProgressTotal.value = response.data.total || 0
|
||
} catch (error) {
|
||
console.error('加载进行中任务失败:', error)
|
||
message.error('加载进行中任务失败')
|
||
} finally {
|
||
inProgressLoading.value = false
|
||
}
|
||
}
|
||
|
||
// 加载待审核任务
|
||
const loadReviewTasks = async () => {
|
||
reviewLoading.value = true
|
||
try {
|
||
const params: any = {
|
||
page: reviewPagination.page,
|
||
pageSize: reviewPagination.pageSize,
|
||
status: 'review'
|
||
}
|
||
|
||
// 添加筛选条件
|
||
if (reviewFilters.keyword) params.keyword = reviewFilters.keyword
|
||
if (reviewFilters.sortBy) params.sortBy = reviewFilters.sortBy
|
||
if (reviewFilters.taskTypeId) params.taskTypeId = reviewFilters.taskTypeId
|
||
if (reviewFilters.brandId) params.brandId = reviewFilters.brandId
|
||
if (reviewFilters.categoryId) params.categoryId = reviewFilters.categoryId
|
||
if (reviewFilters.platformId) params.platformId = reviewFilters.platformId
|
||
if (reviewFilters.timeRange) params.timeRange = reviewFilters.timeRange
|
||
if (reviewFilters.minScore !== undefined) params.minScore = reviewFilters.minScore
|
||
if (reviewFilters.maxScore !== undefined) params.maxScore = reviewFilters.maxScore
|
||
|
||
const response = await taskApi.getTasks(params)
|
||
reviewTaskList.value = response.data.records || []
|
||
reviewTotal.value = response.data.total || 0
|
||
} catch (error) {
|
||
console.error('加载待审核任务失败:', error)
|
||
message.error('加载待审核任务失败')
|
||
} finally {
|
||
reviewLoading.value = false
|
||
}
|
||
}
|
||
|
||
// 进行中任务分页变化
|
||
const handleInProgressPageChange = (page: number) => {
|
||
inProgressPagination.page = page
|
||
loadInProgressTasks()
|
||
}
|
||
|
||
// 待审核任务分页变化
|
||
const handleReviewPageChange = (page: number) => {
|
||
reviewPagination.page = page
|
||
loadReviewTasks()
|
||
}
|
||
|
||
// 重置进行中任务筛选条件
|
||
const handleInProgressFiltersReset = () => {
|
||
inProgressFilters.keyword = undefined
|
||
inProgressFilters.sortBy = 'latest'
|
||
inProgressFilters.taskTypeId = undefined
|
||
inProgressFilters.brandId = undefined
|
||
inProgressFilters.categoryId = undefined
|
||
inProgressFilters.platformId = undefined
|
||
inProgressFilters.timeRange = undefined
|
||
inProgressFilters.minProgress = undefined
|
||
inProgressFilters.maxProgress = undefined
|
||
inProgressPagination.page = 1
|
||
loadInProgressTasks()
|
||
}
|
||
|
||
// 重置待审核任务筛选条件
|
||
const handleReviewFiltersReset = () => {
|
||
reviewFilters.keyword = undefined
|
||
reviewFilters.sortBy = 'latest'
|
||
reviewFilters.taskTypeId = undefined
|
||
reviewFilters.brandId = undefined
|
||
reviewFilters.categoryId = undefined
|
||
reviewFilters.platformId = undefined
|
||
reviewFilters.timeRange = undefined
|
||
reviewFilters.minScore = undefined
|
||
reviewFilters.maxScore = undefined
|
||
reviewPagination.page = 1
|
||
loadReviewTasks()
|
||
}
|
||
|
||
// 判断是否可以更新进度(接单人)
|
||
const canUpdateProgress = (task: Task) => {
|
||
const userInfo = JSON.parse(localStorage.getItem('userInfo') || '{}')
|
||
return task.receiverId === userInfo.id
|
||
}
|
||
|
||
// 判断是否可以完结任务(接单人)
|
||
const canCompleteTask = (task: Task) => {
|
||
const userInfo = JSON.parse(localStorage.getItem('userInfo') || '{}')
|
||
return task.receiverId === userInfo.id && (task.progress || 0) >= 100
|
||
}
|
||
|
||
// 判断是否可以审核任务(发起人或部门负责人)
|
||
const canApproveTask = (task: Task) => {
|
||
const userInfo = JSON.parse(localStorage.getItem('userInfo') || '{}')
|
||
return task.publisherId === userInfo.id || userInfo.roleId === 2 // 2是部门负责人角色
|
||
}
|
||
|
||
// 判断是否可以驳回任务
|
||
const canRejectTask = (task: Task) => {
|
||
return canApproveTask(task)
|
||
}
|
||
|
||
// 更新进度
|
||
const handleUpdateProgress = (task: Task) => {
|
||
let newProgress = task.progress || 0
|
||
|
||
Modal.confirm({
|
||
title: '更新任务进度',
|
||
content: h('div', [
|
||
h('div', { style: 'margin-bottom: 12px;' }, `任务:${task.title}`),
|
||
h('div', { style: 'margin-bottom: 12px;' }, `当前进度:${task.progress || 0}%`),
|
||
h(InputNumber, {
|
||
min: 0,
|
||
max: 100,
|
||
defaultValue: task.progress || 0,
|
||
style: 'width: 100%',
|
||
placeholder: '输入新的进度(0-100)',
|
||
onChange: (value: number) => { newProgress = value }
|
||
})
|
||
]),
|
||
onOk: async () => {
|
||
try {
|
||
await taskApi.updateTaskProgress(task.id, newProgress)
|
||
message.success('进度更新成功')
|
||
loadInProgressTasks()
|
||
loadDashboardStats()
|
||
} catch (error: any) {
|
||
message.error(error.message || '进度更新失败')
|
||
}
|
||
}
|
||
})
|
||
}
|
||
|
||
// 完结任务
|
||
const handleCompleteTask = (task: Task) => {
|
||
let deliveryPath = ''
|
||
|
||
Modal.confirm({
|
||
title: '完结任务',
|
||
content: h('div', [
|
||
h('div', { style: 'margin-bottom: 12px;' }, `确认完结任务:${task.title}`),
|
||
h('div', { style: 'margin-bottom: 12px;' }, '交付路径:'),
|
||
h(Textarea, {
|
||
rows: 4,
|
||
placeholder: '请输入交付路径或说明',
|
||
style: 'width: 100%',
|
||
onChange: (e: any) => { deliveryPath = e.target.value }
|
||
})
|
||
]),
|
||
onOk: async () => {
|
||
if (!deliveryPath.trim()) {
|
||
message.error('请输入交付路径')
|
||
return Promise.reject()
|
||
}
|
||
|
||
try {
|
||
await taskApi.completeTask(task.id, { deliveryPath })
|
||
message.success('任务已提交审核')
|
||
loadInProgressTasks()
|
||
loadDashboardStats()
|
||
} catch (error: any) {
|
||
message.error(error.message || '任务完结失败')
|
||
}
|
||
}
|
||
})
|
||
}
|
||
|
||
// 审核通过
|
||
const handleApproveTask = (task: Task) => {
|
||
Modal.confirm({
|
||
title: '审核通过',
|
||
content: `确认通过任务审核:${task.title}?`,
|
||
onOk: async () => {
|
||
try {
|
||
await taskApi.approveTask(task.id)
|
||
message.success('审核通过')
|
||
loadReviewTasks()
|
||
loadDashboardStats()
|
||
} catch (error: any) {
|
||
message.error(error.message || '审核失败')
|
||
}
|
||
}
|
||
})
|
||
}
|
||
|
||
// 驳回任务
|
||
const handleRejectTask = (task: Task) => {
|
||
let reason = ''
|
||
|
||
Modal.confirm({
|
||
title: '驳回任务',
|
||
content: h('div', [
|
||
h('div', { style: 'margin-bottom: 12px;' }, `驳回任务:${task.title}`),
|
||
h('div', { style: 'margin-bottom: 12px;' }, '驳回原因:'),
|
||
h(Textarea, {
|
||
rows: 4,
|
||
placeholder: '请输入驳回原因',
|
||
style: 'width: 100%',
|
||
onChange: (e: any) => { reason = e.target.value }
|
||
})
|
||
]),
|
||
onOk: async () => {
|
||
if (!reason.trim()) {
|
||
message.error('请输入驳回原因')
|
||
return Promise.reject()
|
||
}
|
||
|
||
try {
|
||
await taskApi.rejectTask(task.id, { reason })
|
||
message.success('任务已驳回')
|
||
loadReviewTasks()
|
||
loadDashboardStats()
|
||
} catch (error: any) {
|
||
message.error(error.message || '驳回失败')
|
||
}
|
||
}
|
||
})
|
||
}
|
||
|
||
// 查看任务详情(进行中/待审核)
|
||
const handleViewTaskDetail = (task: Task) => {
|
||
selectedTask.value = task
|
||
detailDrawerVisible.value = true
|
||
}
|
||
|
||
// 任务点击
|
||
const handleTaskClick = (task: Task) => {
|
||
selectedTask.value = task
|
||
detailDrawerVisible.value = true
|
||
}
|
||
|
||
// 抢单
|
||
const handleGrab = async (task: Task) => {
|
||
Modal.confirm({
|
||
title: '确认抢单',
|
||
content: `确定要抢单"${task.title}"吗?`,
|
||
okText: '确认',
|
||
cancelText: '取消',
|
||
onOk: async () => {
|
||
try {
|
||
const response = await taskApi.grabTask(task.id!)
|
||
if (response.code === 200) {
|
||
message.success('抢单成功!')
|
||
loadTasks() // 刷新列表
|
||
}
|
||
} catch (error: any) {
|
||
console.error('抢单失败:', error)
|
||
message.error(error.message || '抢单失败')
|
||
}
|
||
}
|
||
})
|
||
}
|
||
|
||
// 从详情抽屉抢单
|
||
const handleGrabFromDetail = async () => {
|
||
if (!selectedTask.value) return
|
||
await handleGrab(selectedTask.value)
|
||
detailDrawerVisible.value = false
|
||
}
|
||
|
||
// 监听配置更新事件
|
||
const handleConfigUpdate = async (event: CustomEvent) => {
|
||
console.log('配置已更新,刷新配置数据:', event.detail)
|
||
await loadConfigs()
|
||
}
|
||
|
||
// 组件挂载
|
||
// 获取任务状态文本
|
||
const getTaskStatusText = (status: string) => {
|
||
const statusMap: Record<string, string> = {
|
||
'pending': '待接单',
|
||
'in_progress': '进行中',
|
||
'review': '待审核',
|
||
'completed': '已完成',
|
||
'rejected': '已驳回',
|
||
'cancelled': '已取消'
|
||
}
|
||
return statusMap[status] || status
|
||
}
|
||
|
||
onMounted(() => {
|
||
loadConfigs()
|
||
loadDashboardStats()
|
||
loadTasks()
|
||
// 监听配置更新事件
|
||
window.addEventListener('configUpdated', handleConfigUpdate as EventListener)
|
||
})
|
||
|
||
// 组件卸载
|
||
onUnmounted(() => {
|
||
window.removeEventListener('configUpdated', handleConfigUpdate as EventListener)
|
||
})
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.task-hall-page {
|
||
min-height: 100vh;
|
||
padding: $space-xl 0;
|
||
background: $bg-primary;
|
||
}
|
||
|
||
// 数据总览样式
|
||
.overview-section {
|
||
margin-bottom: $space-xl;
|
||
|
||
.overview-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: $space-lg;
|
||
|
||
.section-title {
|
||
font-size: $font-size-xl;
|
||
font-weight: $font-weight-bold;
|
||
color: $text-primary;
|
||
margin-bottom: 0;
|
||
}
|
||
|
||
.overview-actions {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: $space-md;
|
||
}
|
||
}
|
||
}
|
||
|
||
.stat-card {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: $space-lg;
|
||
padding: $space-xl;
|
||
background: $bg-card;
|
||
border-radius: $radius-lg;
|
||
border: 1px solid $border-primary;
|
||
transition: all $transition-base $ease-out;
|
||
cursor: pointer;
|
||
|
||
&:hover {
|
||
border-color: $border-color;
|
||
background: $bg-card-hover;
|
||
transform: translateY(-2px);
|
||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||
}
|
||
|
||
.stat-icon {
|
||
flex-shrink: 0;
|
||
width: 48px;
|
||
height: 48px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
background: rgba(49, 240, 139, 0.1);
|
||
border-radius: $radius-md;
|
||
}
|
||
|
||
.stat-content {
|
||
flex: 1;
|
||
|
||
.stat-label {
|
||
font-size: $font-size-sm;
|
||
color: $text-secondary;
|
||
margin-bottom: $space-xs;
|
||
}
|
||
|
||
.stat-value {
|
||
font-size: 28px;
|
||
font-weight: $font-weight-bold;
|
||
color: $text-primary;
|
||
line-height: 1.2;
|
||
}
|
||
}
|
||
}
|
||
|
||
.container {
|
||
max-width: $container-max-width;
|
||
margin: 0 auto;
|
||
padding: 0 $space-xl;
|
||
|
||
@media (max-width: $breakpoint-tablet) {
|
||
padding: 0 $space-lg;
|
||
}
|
||
|
||
@media (max-width: $breakpoint-mobile) {
|
||
padding: 0 $space-md;
|
||
}
|
||
}
|
||
|
||
.header-actions {
|
||
display: flex;
|
||
justify-content: flex-end;
|
||
margin-bottom: $space-2xl;
|
||
|
||
button {
|
||
min-width: 140px;
|
||
}
|
||
}
|
||
|
||
// 图表卡片 - Cursor 风格
|
||
.chart-card {
|
||
background: $bg-card;
|
||
border: 1px solid $border-primary;
|
||
border-radius: $radius-lg;
|
||
padding: $space-xl;
|
||
transition: all $transition-base $ease-out;
|
||
|
||
&:hover {
|
||
border-color: $border-color;
|
||
background: $bg-card-hover;
|
||
}
|
||
}
|
||
|
||
// 标签页样式统一
|
||
:deep(.ant-tabs) {
|
||
.ant-tabs-nav {
|
||
margin-bottom: $space-xl;
|
||
|
||
&::before {
|
||
border-bottom: 1px solid $border-primary;
|
||
}
|
||
}
|
||
|
||
.ant-tabs-tab {
|
||
padding: $space-md $space-xl;
|
||
font-size: $font-size-base;
|
||
font-weight: $font-weight-medium;
|
||
color: $text-secondary;
|
||
transition: all $transition-base $ease-out;
|
||
|
||
&:hover {
|
||
color: $text-primary;
|
||
}
|
||
|
||
&.ant-tabs-tab-active {
|
||
.ant-tabs-tab-btn {
|
||
color: #31f08b;
|
||
font-weight: $font-weight-semibold;
|
||
}
|
||
}
|
||
}
|
||
|
||
.ant-tabs-ink-bar {
|
||
background: #31f08b;
|
||
height: 3px;
|
||
}
|
||
|
||
.ant-tabs-content {
|
||
padding: 0;
|
||
}
|
||
}
|
||
|
||
// 搜索和筛选栏(合并)
|
||
.search-filter-bar {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: $space-md;
|
||
margin-bottom: $space-xl;
|
||
padding: $space-lg;
|
||
}
|
||
|
||
// 搜索行(搜索、排序、统计)
|
||
.search-row {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: $space-md;
|
||
width: 100%;
|
||
|
||
.search-input {
|
||
flex: 1;
|
||
min-width: 300px;
|
||
max-width: 500px;
|
||
}
|
||
|
||
.sort-select {
|
||
width: 150px;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.stats-text {
|
||
font-size: $font-size-base;
|
||
color: $text-secondary;
|
||
white-space: nowrap;
|
||
margin-left: auto;
|
||
}
|
||
|
||
@media (max-width: $breakpoint-mobile) {
|
||
flex-wrap: wrap;
|
||
|
||
.stats-text {
|
||
margin-left: 0;
|
||
width: 100%;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 筛选行(紧凑布局)
|
||
.filter-row {
|
||
display: flex;
|
||
align-items: center;
|
||
flex-wrap: wrap;
|
||
gap: $space-sm;
|
||
width: 100%;
|
||
|
||
.filter-select {
|
||
width: 140px;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.score-range {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: $space-xs;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.score-input {
|
||
width: 100px;
|
||
}
|
||
|
||
.score-separator {
|
||
color: $text-secondary;
|
||
font-size: $font-size-base;
|
||
padding: 0 $space-xs;
|
||
}
|
||
|
||
.reset-btn {
|
||
margin-left: auto;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
@media (max-width: $breakpoint-mobile) {
|
||
.filter-select {
|
||
width: calc(50% - #{$space-sm / 2});
|
||
}
|
||
|
||
.score-range {
|
||
width: 100%;
|
||
margin-top: $space-xs;
|
||
}
|
||
|
||
.reset-btn {
|
||
margin-left: 0;
|
||
width: 100%;
|
||
margin-top: $space-xs;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 加载状态
|
||
.loading-container {
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
min-height: 400px;
|
||
}
|
||
|
||
// 空状态
|
||
.empty-state {
|
||
text-align: center;
|
||
padding: $space-xxxl;
|
||
|
||
.empty-icon {
|
||
font-size: 64px;
|
||
margin-bottom: $space-lg;
|
||
}
|
||
|
||
h3 {
|
||
font-size: $font-size-2xl;
|
||
color: $text-primary;
|
||
margin-bottom: $space-sm;
|
||
}
|
||
|
||
p {
|
||
color: $text-secondary;
|
||
}
|
||
}
|
||
|
||
// 任务网格
|
||
.task-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fill, minmax(350px, 1fr));
|
||
gap: $space-lg;
|
||
margin-bottom: $space-xl;
|
||
|
||
@media (max-width: $breakpoint-mobile) {
|
||
grid-template-columns: 1fr;
|
||
}
|
||
}
|
||
|
||
// 任务卡片
|
||
.task-card {
|
||
transition: all 0.2s ease-out;
|
||
cursor: pointer;
|
||
|
||
&:hover {
|
||
transform: translateY(-2px);
|
||
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.1);
|
||
}
|
||
}
|
||
|
||
.task-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: flex-start;
|
||
margin-bottom: $space-md;
|
||
gap: $space-sm;
|
||
}
|
||
|
||
.task-badges {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: $space-xs;
|
||
align-items: center;
|
||
}
|
||
|
||
.task-title {
|
||
font-size: $font-size-xl;
|
||
font-weight: $font-weight-semibold;
|
||
color: $text-primary;
|
||
margin: 0;
|
||
flex: 1;
|
||
}
|
||
|
||
.badge {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
padding: 4px 8px;
|
||
border-radius: $radius-small;
|
||
font-size: $font-size-xs;
|
||
font-weight: $font-weight-medium;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.badge-new {
|
||
background: rgba($color-success, 0.1);
|
||
border: 1px solid rgba($color-success, 0.3);
|
||
color: $color-success;
|
||
}
|
||
|
||
.badge-high-score {
|
||
background: rgba($color-warning, 0.1);
|
||
border: 1px solid rgba($color-warning, 0.3);
|
||
color: $color-warning;
|
||
}
|
||
|
||
.badge-urgent {
|
||
background: rgba($color-error, 0.1);
|
||
border: 1px solid rgba($color-error, 0.3);
|
||
color: $color-error;
|
||
}
|
||
|
||
.badge-completed {
|
||
background: rgba(49, 240, 139, 0.1);
|
||
color: #31f08b;
|
||
border: 1px solid rgba(49, 240, 139, 0.2);
|
||
}
|
||
|
||
// 任务信息
|
||
.task-info {
|
||
display: grid;
|
||
grid-template-columns: repeat(3, 1fr);
|
||
gap: $space-md;
|
||
margin-bottom: $space-md;
|
||
padding-bottom: $space-md;
|
||
border-bottom: 1px solid $border-primary;
|
||
}
|
||
|
||
.info-item {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 4px;
|
||
|
||
.label {
|
||
font-size: $font-size-xs;
|
||
color: $text-tertiary;
|
||
}
|
||
|
||
.value {
|
||
font-size: $font-size-base;
|
||
font-weight: $font-weight-medium;
|
||
color: $text-primary;
|
||
}
|
||
}
|
||
|
||
// 任务详情
|
||
.task-details {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: $space-sm;
|
||
margin-bottom: $space-md;
|
||
}
|
||
|
||
.detail-item {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: $space-sm;
|
||
font-size: $font-size-sm;
|
||
color: $text-secondary;
|
||
|
||
.icon {
|
||
font-size: $font-size-base;
|
||
}
|
||
}
|
||
|
||
// 任务操作
|
||
.task-actions {
|
||
display: flex;
|
||
gap: $space-sm;
|
||
}
|
||
|
||
// 任务详情抽屉
|
||
.task-detail {
|
||
.detail-section {
|
||
margin-bottom: $space-xl;
|
||
|
||
h3 {
|
||
font-size: $font-size-2xl;
|
||
font-weight: $font-weight-bold;
|
||
color: $text-primary;
|
||
margin-bottom: $space-md;
|
||
}
|
||
|
||
h4 {
|
||
font-size: $font-size-lg;
|
||
font-weight: $font-weight-semibold;
|
||
color: $text-primary;
|
||
margin-bottom: $space-md;
|
||
}
|
||
|
||
p {
|
||
color: $text-secondary;
|
||
line-height: 1.6;
|
||
}
|
||
}
|
||
|
||
.detail-badges {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: $space-xs;
|
||
margin-top: $space-sm;
|
||
}
|
||
|
||
.detail-actions {
|
||
margin-top: $space-2xl;
|
||
padding-top: $space-xl;
|
||
border-top: 1px solid $border-primary;
|
||
|
||
.action-buttons {
|
||
display: flex;
|
||
gap: $space-md;
|
||
|
||
button {
|
||
flex: 1;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.attachment-links {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: $space-sm;
|
||
|
||
.attachment-link {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
padding: $space-sm $space-md;
|
||
background: $bg-secondary;
|
||
border: 1px solid $border-primary;
|
||
border-radius: $radius-md;
|
||
color: $text-primary;
|
||
text-decoration: none;
|
||
transition: all $transition-base;
|
||
|
||
&:hover {
|
||
background: $bg-card-hover;
|
||
border-color: #31f08b;
|
||
color: #31f08b;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 分页
|
||
.pagination {
|
||
display: flex;
|
||
justify-content: center;
|
||
margin-top: $space-xl;
|
||
}
|
||
|
||
// 任务进度条
|
||
.task-progress {
|
||
margin: $space-md 0;
|
||
padding: $space-md;
|
||
background: rgba(49, 240, 139, 0.05);
|
||
border-radius: $radius-md;
|
||
|
||
.progress-label {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
margin-bottom: $space-sm;
|
||
font-size: $font-size-sm;
|
||
color: $text-secondary;
|
||
|
||
span:last-child {
|
||
font-weight: $font-weight-medium;
|
||
color: #31f08b;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 任务操作按钮
|
||
.task-actions {
|
||
display: flex;
|
||
gap: $space-sm;
|
||
padding-top: $space-md;
|
||
border-top: 1px solid $border-primary;
|
||
margin-top: $space-md;
|
||
|
||
button {
|
||
flex: 1;
|
||
}
|
||
}
|
||
|
||
// 任务卡片特定样式
|
||
.task-card.in-progress {
|
||
border-left: 3px solid #F59E0B;
|
||
}
|
||
|
||
.task-card.review {
|
||
border-left: 3px solid #3B82F6;
|
||
}
|
||
|
||
// badge样式补充
|
||
.badge-in-progress {
|
||
background: rgba(245, 158, 11, 0.1);
|
||
color: #F59E0B;
|
||
border: 1px solid rgba(245, 158, 11, 0.2);
|
||
}
|
||
|
||
.badge-review {
|
||
background: rgba(59, 130, 246, 0.1);
|
||
color: #3B82F6;
|
||
border: 1px solid rgba(59, 130, 246, 0.2);
|
||
}
|
||
</style>
|
||
|