优化筛选组件UI:深灰色背景、绿色选中文字、添加label属性
This commit is contained in:
parent
8789143501
commit
218f01d6b1
600
frontend/src/components/TaskFilters.vue
Normal file
600
frontend/src/components/TaskFilters.vue
Normal file
@ -0,0 +1,600 @@
|
||||
<template>
|
||||
<div class="search-filter-bar chart-card">
|
||||
<!-- 第一行:搜索、排序和统计 -->
|
||||
<div class="search-row">
|
||||
<a-input-search
|
||||
v-model:value="localFilters.keyword"
|
||||
:placeholder="searchPlaceholder"
|
||||
class="search-input"
|
||||
@search="handleFilterChange"
|
||||
@pressEnter="handleFilterChange"
|
||||
/>
|
||||
<a-select
|
||||
v-model:value="localFilters.sortBy"
|
||||
placeholder="排序方式"
|
||||
class="sort-select"
|
||||
@change="handleFilterChange"
|
||||
>
|
||||
<a-select-option
|
||||
v-for="option in sortOptions"
|
||||
:key="option.value"
|
||||
:value="option.value"
|
||||
:label="option.label"
|
||||
>
|
||||
{{ option.label }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
<span class="stats-text">{{ statsText }}</span>
|
||||
</div>
|
||||
|
||||
<!-- 第二行:筛选条件 -->
|
||||
<div class="filter-row">
|
||||
<!-- 状态筛选 -->
|
||||
<a-select
|
||||
v-if="showStatus"
|
||||
v-model:value="localFilters.status"
|
||||
:placeholder="statusPlaceholder"
|
||||
class="filter-select"
|
||||
allowClear
|
||||
@change="handleFilterChange"
|
||||
>
|
||||
<a-select-option
|
||||
v-for="option in statusOptions"
|
||||
:key="option.value"
|
||||
:value="option.value"
|
||||
:label="option.label"
|
||||
>
|
||||
{{ option.label }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
|
||||
<!-- 任务类型筛选 -->
|
||||
<a-select
|
||||
v-if="showTaskType"
|
||||
v-model:value="localFilters.taskTypeId"
|
||||
placeholder="任务类型"
|
||||
class="filter-select"
|
||||
allowClear
|
||||
@change="handleFilterChange"
|
||||
>
|
||||
<a-select-option
|
||||
v-for="type in demandTypes"
|
||||
:key="type.id"
|
||||
:value="type.id"
|
||||
:label="type.name"
|
||||
>
|
||||
{{ type.name }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
|
||||
<!-- 需求类型筛选(新品/优化/视频/实拍) -->
|
||||
<a-select
|
||||
v-if="showTaskSubType"
|
||||
v-model:value="localFilters.taskSubType"
|
||||
placeholder="需求类型"
|
||||
class="filter-select"
|
||||
allowClear
|
||||
@change="handleFilterChange"
|
||||
>
|
||||
<a-select-option
|
||||
v-for="subType in taskSubTypes"
|
||||
:key="subType.id"
|
||||
:value="subType.name"
|
||||
:label="subType.name"
|
||||
>
|
||||
{{ subType.name }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
|
||||
<!-- 品牌筛选 -->
|
||||
<a-select
|
||||
v-if="showBrand"
|
||||
v-model:value="localFilters.brandId"
|
||||
placeholder="品牌"
|
||||
class="filter-select"
|
||||
allowClear
|
||||
@change="handleFilterChange"
|
||||
>
|
||||
<a-select-option
|
||||
v-for="brand in brands"
|
||||
:key="brand.id"
|
||||
:value="brand.id"
|
||||
:label="brand.name"
|
||||
>
|
||||
{{ brand.name }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
|
||||
<!-- 类目筛选 -->
|
||||
<a-select
|
||||
v-if="showCategory"
|
||||
v-model:value="localFilters.categoryId"
|
||||
placeholder="类目"
|
||||
class="filter-select"
|
||||
allowClear
|
||||
@change="handleFilterChange"
|
||||
>
|
||||
<a-select-option
|
||||
v-for="category in categories"
|
||||
:key="category.id"
|
||||
:value="category.id"
|
||||
:label="category.name"
|
||||
>
|
||||
{{ category.name }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
|
||||
<!-- 平台筛选 -->
|
||||
<a-select
|
||||
v-if="showPlatform"
|
||||
v-model:value="localFilters.platformId"
|
||||
placeholder="平台"
|
||||
class="filter-select"
|
||||
allowClear
|
||||
@change="handleFilterChange"
|
||||
>
|
||||
<a-select-option
|
||||
v-for="platform in platforms"
|
||||
:key="platform.id"
|
||||
:value="platform.id"
|
||||
:label="platform.name"
|
||||
>
|
||||
{{ platform.name }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
|
||||
<!-- 接单部门筛选 -->
|
||||
<a-select
|
||||
v-if="showReceivingDept"
|
||||
v-model:value="localFilters.filterDeptId"
|
||||
placeholder="接单部门"
|
||||
class="filter-select"
|
||||
allowClear
|
||||
@change="handleFilterChange"
|
||||
>
|
||||
<a-select-option
|
||||
v-for="dept in receivingDeptList"
|
||||
:key="dept.id"
|
||||
:value="dept.id"
|
||||
:label="dept.deptName"
|
||||
>
|
||||
{{ dept.deptName }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
|
||||
<!-- 时间范围筛选 -->
|
||||
<a-select
|
||||
v-if="showTimeRange"
|
||||
v-model:value="localFilters.timeRange"
|
||||
:placeholder="timeRangePlaceholder"
|
||||
class="filter-select"
|
||||
allowClear
|
||||
@change="handleFilterChange"
|
||||
>
|
||||
<a-select-option value="today" label="今天">今天</a-select-option>
|
||||
<a-select-option value="week" label="本周">本周</a-select-option>
|
||||
<a-select-option value="month" label="本月">本月</a-select-option>
|
||||
</a-select>
|
||||
|
||||
<!-- 积分范围筛选 -->
|
||||
<div v-if="showScoreRange" class="score-range">
|
||||
<a-input
|
||||
v-model:value="localFilters.minScore"
|
||||
placeholder="最小积分"
|
||||
class="score-input"
|
||||
@pressEnter="handleFilterChange"
|
||||
/>
|
||||
<span class="score-separator">-</span>
|
||||
<a-input
|
||||
v-model:value="localFilters.maxScore"
|
||||
placeholder="最大积分"
|
||||
class="score-input"
|
||||
@pressEnter="handleFilterChange"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 进度范围筛选(用于进行中任务) -->
|
||||
<div v-if="showProgressRange" class="score-range">
|
||||
<a-input-number
|
||||
v-model:value="localFilters.minProgress"
|
||||
placeholder="最小进度%"
|
||||
class="score-input"
|
||||
:min="0"
|
||||
:max="100"
|
||||
@change="handleFilterChange"
|
||||
/>
|
||||
<span class="score-separator">-</span>
|
||||
<a-input-number
|
||||
v-model:value="localFilters.maxProgress"
|
||||
placeholder="最大进度%"
|
||||
class="score-input"
|
||||
:min="0"
|
||||
:max="100"
|
||||
@change="handleFilterChange"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<a-button @click="handleReset" class="reset-btn">重置</a-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { reactive, watch, PropType } from 'vue'
|
||||
|
||||
// 筛选项类型定义
|
||||
interface FilterOption {
|
||||
value: string
|
||||
label: string
|
||||
}
|
||||
|
||||
interface DemandType {
|
||||
id: number
|
||||
name: string
|
||||
}
|
||||
|
||||
interface TaskSubType {
|
||||
id: number
|
||||
name: string
|
||||
}
|
||||
|
||||
interface Brand {
|
||||
id: number
|
||||
name: string
|
||||
}
|
||||
|
||||
interface Category {
|
||||
id: number
|
||||
name: string
|
||||
}
|
||||
|
||||
interface Platform {
|
||||
id: number
|
||||
name: string
|
||||
}
|
||||
|
||||
interface Department {
|
||||
id: number
|
||||
deptName: string
|
||||
}
|
||||
|
||||
interface FilterValues {
|
||||
keyword?: string
|
||||
status?: string
|
||||
sortBy?: string
|
||||
taskTypeId?: number
|
||||
taskSubType?: string
|
||||
brandId?: number
|
||||
categoryId?: number
|
||||
platformId?: number
|
||||
filterDeptId?: number
|
||||
timeRange?: string
|
||||
minScore?: number
|
||||
maxScore?: number
|
||||
minProgress?: number
|
||||
maxProgress?: number
|
||||
}
|
||||
|
||||
// Props 定义
|
||||
const props = defineProps({
|
||||
// 筛选条件值(双向绑定)
|
||||
modelValue: {
|
||||
type: Object as PropType<FilterValues>,
|
||||
default: () => ({})
|
||||
},
|
||||
// 搜索框占位符
|
||||
searchPlaceholder: {
|
||||
type: String,
|
||||
default: '搜索任务编号、标题、SPU信息、发布人...'
|
||||
},
|
||||
// 统计文本
|
||||
statsText: {
|
||||
type: String,
|
||||
default: '共 0 个任务'
|
||||
},
|
||||
// 排序选项
|
||||
sortOptions: {
|
||||
type: Array as PropType<FilterOption[]>,
|
||||
default: () => [
|
||||
{ value: 'latest', label: '最新发布' },
|
||||
{ value: 'score', label: '积分最高' },
|
||||
{ value: 'deadline', label: '时效最短' }
|
||||
]
|
||||
},
|
||||
// 状态筛选配置
|
||||
showStatus: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
statusPlaceholder: {
|
||||
type: String,
|
||||
default: '任务状态'
|
||||
},
|
||||
statusOptions: {
|
||||
type: Array as PropType<FilterOption[]>,
|
||||
default: () => [
|
||||
{ value: '', label: '全部状态' }
|
||||
]
|
||||
},
|
||||
// 显示控制
|
||||
showTaskType: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
showTaskSubType: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
showBrand: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
showCategory: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
showPlatform: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
showReceivingDept: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
showTimeRange: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
timeRangePlaceholder: {
|
||||
type: String,
|
||||
default: '发布时间'
|
||||
},
|
||||
showScoreRange: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
showProgressRange: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 数据源
|
||||
demandTypes: {
|
||||
type: Array as PropType<DemandType[]>,
|
||||
default: () => []
|
||||
},
|
||||
taskSubTypes: {
|
||||
type: Array as PropType<TaskSubType[]>,
|
||||
default: () => []
|
||||
},
|
||||
brands: {
|
||||
type: Array as PropType<Brand[]>,
|
||||
default: () => []
|
||||
},
|
||||
categories: {
|
||||
type: Array as PropType<Category[]>,
|
||||
default: () => []
|
||||
},
|
||||
platforms: {
|
||||
type: Array as PropType<Platform[]>,
|
||||
default: () => []
|
||||
},
|
||||
receivingDeptList: {
|
||||
type: Array as PropType<Department[]>,
|
||||
default: () => []
|
||||
}
|
||||
})
|
||||
|
||||
// Emits 定义
|
||||
const emit = defineEmits(['update:modelValue', 'change', 'reset'])
|
||||
|
||||
// 本地筛选状态
|
||||
const localFilters = reactive<FilterValues>({
|
||||
keyword: props.modelValue?.keyword,
|
||||
status: props.modelValue?.status || '',
|
||||
sortBy: props.modelValue?.sortBy || 'latest',
|
||||
taskTypeId: props.modelValue?.taskTypeId,
|
||||
taskSubType: props.modelValue?.taskSubType,
|
||||
brandId: props.modelValue?.brandId,
|
||||
categoryId: props.modelValue?.categoryId,
|
||||
platformId: props.modelValue?.platformId,
|
||||
filterDeptId: props.modelValue?.filterDeptId,
|
||||
timeRange: props.modelValue?.timeRange,
|
||||
minScore: props.modelValue?.minScore,
|
||||
maxScore: props.modelValue?.maxScore,
|
||||
minProgress: props.modelValue?.minProgress,
|
||||
maxProgress: props.modelValue?.maxProgress
|
||||
})
|
||||
|
||||
// 监听 props.modelValue 变化,同步到本地状态
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(newVal) => {
|
||||
if (newVal) {
|
||||
Object.assign(localFilters, newVal)
|
||||
}
|
||||
},
|
||||
{ deep: true }
|
||||
)
|
||||
|
||||
// 筛选条件变更
|
||||
const handleFilterChange = () => {
|
||||
emit('update:modelValue', { ...localFilters })
|
||||
emit('change', { ...localFilters })
|
||||
}
|
||||
|
||||
// 重置筛选条件
|
||||
const handleReset = () => {
|
||||
localFilters.keyword = undefined
|
||||
localFilters.status = ''
|
||||
localFilters.sortBy = props.sortOptions[0]?.value || 'latest'
|
||||
localFilters.taskTypeId = undefined
|
||||
localFilters.taskSubType = undefined
|
||||
localFilters.brandId = undefined
|
||||
localFilters.categoryId = undefined
|
||||
localFilters.platformId = undefined
|
||||
localFilters.filterDeptId = undefined
|
||||
localFilters.timeRange = undefined
|
||||
localFilters.minScore = undefined
|
||||
localFilters.maxScore = undefined
|
||||
localFilters.minProgress = undefined
|
||||
localFilters.maxProgress = undefined
|
||||
|
||||
emit('update:modelValue', { ...localFilters })
|
||||
emit('reset')
|
||||
emit('change', { ...localFilters })
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '@/styles/variables.scss';
|
||||
|
||||
// 复用图表卡片样式,与任务卡片统一
|
||||
.chart-card {
|
||||
background: $bg-card;
|
||||
border: 1px solid $border-primary;
|
||||
border-radius: $radius-lg;
|
||||
padding: $space-md; // 筛选栏稍微紧凑一点,用 md 而不是 xl
|
||||
transition: all $transition-base $ease-out;
|
||||
|
||||
&:hover {
|
||||
border-color: rgba(255, 255, 255, 0.1); // 微调 hover 边框颜色
|
||||
background: $bg-card-hover;
|
||||
}
|
||||
}
|
||||
|
||||
.search-filter-bar {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.search-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: $space-md;
|
||||
margin-bottom: $space-md;
|
||||
}
|
||||
|
||||
.search-input {
|
||||
flex: 1;
|
||||
max-width: 400px;
|
||||
}
|
||||
|
||||
.sort-select {
|
||||
width: 130px;
|
||||
}
|
||||
|
||||
.stats-text {
|
||||
color: $text-secondary;
|
||||
font-size: $font-size-base;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.filter-row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: $space-sm;
|
||||
}
|
||||
|
||||
.filter-select {
|
||||
min-width: 110px;
|
||||
max-width: 140px;
|
||||
}
|
||||
|
||||
.score-range {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: $space-xs;
|
||||
}
|
||||
|
||||
.score-input {
|
||||
width: 90px;
|
||||
}
|
||||
|
||||
.score-separator {
|
||||
color: $text-secondary;
|
||||
}
|
||||
|
||||
.reset-btn {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
// 深色主题适配 - 表单控件统一风格
|
||||
:deep(.ant-select-selector) {
|
||||
background-color: #1a1a1a !important; // 深灰色背景
|
||||
border-color: #333333 !important; // 深色边框
|
||||
color: $brand-primary !important; // 选中的文字使用绿色,更醒目
|
||||
border-radius: $radius-base !important; // 统一圆角
|
||||
|
||||
&:hover {
|
||||
border-color: $brand-primary !important;
|
||||
}
|
||||
}
|
||||
|
||||
// 选中项文字颜色为绿色
|
||||
:deep(.ant-select-selection-item) {
|
||||
color: $brand-primary !important;
|
||||
}
|
||||
|
||||
:deep(.ant-select-arrow) {
|
||||
color: $brand-primary; // 箭头也用绿色
|
||||
}
|
||||
|
||||
:deep(.ant-select-selection-placeholder) {
|
||||
color: $text-tertiary !important; // placeholder 保持灰色
|
||||
}
|
||||
|
||||
:deep(.ant-input) {
|
||||
background-color: #1a1a1a; // 深灰色背景
|
||||
border-color: #333333; // 深色边框
|
||||
color: $text-primary;
|
||||
border-radius: $radius-base;
|
||||
|
||||
&::placeholder {
|
||||
color: $text-tertiary;
|
||||
}
|
||||
|
||||
&:hover, &:focus {
|
||||
border-color: $brand-primary;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.ant-input-search-button) {
|
||||
border-color: $border-primary;
|
||||
border-radius: 0 $radius-base $radius-base 0 !important; // 修复搜索按钮圆角
|
||||
}
|
||||
|
||||
:deep(.ant-input-number) {
|
||||
background-color: #1a1a1a; // 深灰色背景
|
||||
border-color: #333333; // 深色边框
|
||||
border-radius: $radius-base;
|
||||
width: 100%;
|
||||
|
||||
.ant-input-number-input {
|
||||
color: $text-primary;
|
||||
|
||||
&::placeholder {
|
||||
color: $text-tertiary;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover, &:focus {
|
||||
border-color: $brand-primary;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.ant-input-number-focused) {
|
||||
border-color: $brand-primary;
|
||||
}
|
||||
|
||||
:deep(.ant-btn) {
|
||||
background-color: #1a1a1a; // 深灰色背景
|
||||
border-color: #333333; // 深色边框
|
||||
color: $text-primary;
|
||||
border-radius: $radius-base;
|
||||
|
||||
&:hover {
|
||||
border-color: $brand-primary;
|
||||
color: $brand-primary;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
1
frontend/src/types/components.d.ts
vendored
1
frontend/src/types/components.d.ts
vendored
@ -69,5 +69,6 @@ declare module 'vue' {
|
||||
RouterLink: typeof import('vue-router')['RouterLink']
|
||||
RouterView: typeof import('vue-router')['RouterView']
|
||||
TaskCard: typeof import('./../components/TaskCard.vue')['default']
|
||||
TaskFilters: typeof import('./../components/TaskFilters.vue')['default']
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user