Просмотр исходного кода

feat: TaskListView use TopBar with CapsuleSafeArea for publish button

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
xuyunhui 1 неделя назад
Родитель
Сommit
ffa0218
1 измененных файлов с 174 добавлено и 0 удалено
  1. 174 0
      src/components/task/TaskListView.vue

+ 174 - 0
src/components/task/TaskListView.vue

@@ -0,0 +1,174 @@
+<template>
+  <view class="app-container pb-20">
+    <TopBar :title="pageTitle">
+      <template #right>
+        <button
+          v-if="config.publishPath"
+          class="px-3 py-2 bg-primary text-white rounded-lg text-sm flex items-center"
+          @click="goToPublishTask"
+        >
+          <text class="uni-icons uniui-plusempty mr-1"></text>
+          {{ publishText }}
+        </button>
+      </template>
+    </TopBar>
+
+    <!-- 筛选标签 -->
+    <view class="bg-white px-4 py-3 flex gap-2 overflow-x-auto hide-scrollbar border-b border-gray-200">
+      <view
+        v-for="status in filterStatuses"
+        :key="status.value"
+        class="filter-tab px-3 py-2 rounded-full text-sm whitespace-nowrap transition-colors"
+        :class="currentFilter === status.value ? 'bg-primary text-white' : 'bg-surface text-gray-500'"
+        @click="currentFilter = status.value"
+      >
+        {{ status.label }}
+      </view>
+    </view>
+
+    <!-- 加载/空态 -->
+    <view class="px-4 mt-3">
+      <view v-if="taskStore.loading" class="py-10 text-center">
+        <text class="text-gray-400">加载中...</text>
+      </view>
+
+      <view v-else-if="filteredTasks.length === 0">
+        <EmptyState message="暂无任务" />
+      </view>
+
+      <!-- 销售端:任务卡片 -->
+      <view v-else-if="role === 'sales'" class="gap-y-3">
+        <SalesTaskCard
+          v-for="task in filteredTasks"
+          :key="task.id"
+          :task="task"
+          @click="goToDetail"
+        />
+      </view>
+
+      <!-- 调度端:任务卡片 -->
+      <view v-else-if="role === 'dispatch'" class="gap-y-3">
+        <DispatchTaskCard
+          v-for="task in filteredTasks"
+          :key="task.id"
+          :task="task"
+          @click="goToDetail"
+        />
+      </view>
+
+      <!-- 施工端:任务卡片 -->
+      <view v-else class="gap-y-3">
+        <ConstructionTaskCard
+          v-for="task in filteredTasks"
+          :key="task.id"
+          :task="task"
+          @click="goToDetail"
+        />
+      </view>
+    </view>
+
+    <!-- 发布/新增任务按钮 -->
+    <view v-if="config.publishPath && role !== 'dispatch'" class="fixed bottom-20 right-4">
+      <button
+        class="w-14 h-14 rounded-full bg-primary text-white flex items-center justify-center shadow-lg text-2xl"
+        @click="goToPublishTask"
+      >
+        +
+      </button>
+    </view>
+  </view>
+</template>
+
+<script setup lang="ts">
+import { ref, computed } from 'vue'
+import { useTaskStore } from '@/stores/task'
+import TopBar from '@/components/common/TopBar.vue'
+import SalesTaskCard from '@/components/task/SalesTaskCard.vue'
+import DispatchTaskCard from '@/components/task/DispatchTaskCard.vue'
+import ConstructionTaskCard from '@/components/task/ConstructionTaskCard.vue'
+import EmptyState from '@/components/common/EmptyState.vue'
+
+const props = defineProps<{
+  role: 'sales' | 'dispatch' | 'construction'
+}>()
+
+const taskStore = useTaskStore()
+const currentFilter = ref('all')
+
+const roleConfig: Record<string, { filterStatuses: { value: string; label: string }[]; detailPath: string; publishPath?: string }> = {
+  sales: {
+    filterStatuses: [
+      { value: 'all', label: '全部' },
+      { value: 'dispatching', label: '调度中' },
+      { value: 'constructing', label: '施工中' },
+      { value: 'completed', label: '已完成' },
+      { value: 'emergency', label: '应急' },
+    ],
+    detailPath: '/subPackages/pages-common/taskDetail',
+    publishPath: '/subPackages/pages-common/publishTask',
+  },
+  dispatch: {
+    filterStatuses: [
+      { value: 'all', label: '全部' },
+      { value: 'pending', label: '待审核' },
+      { value: 'confirmed', label: '待排班' },
+      { value: 'processing', label: '进行中' },
+      { value: 'completed', label: '已完成' },
+      { value: 'rejected', label: '已驳回' },
+    ],
+    detailPath: '/subPackages/pages-dispatch/taskDetail',
+    publishPath: '/subPackages/pages-dispatch/publishTask',
+  },
+  construction: {
+    filterStatuses: [
+      { value: 'all', label: '全部' },
+      { value: 'pending', label: '待确认' },
+      { value: 'scheduled', label: '待出车' },
+      { value: 'departed', label: '已出车' },
+      { value: 'constructing', label: '施工中' },
+      { value: 'inspecting', label: '待验收' },
+    ],
+    detailPath: '/subPackages/pages-construction/taskDetail',
+  },
+}
+
+const config = computed(() => roleConfig[props.role] || roleConfig.sales)
+const filterStatuses = computed(() => config.value.filterStatuses)
+const detailPath = computed(() => config.value.detailPath)
+
+const pageTitle = computed(() => {
+  if (props.role === 'sales') return '我的任务'
+  if (props.role === 'dispatch') return '任务管理'
+  return '我的任务'
+})
+
+const publishText = computed(() => {
+  if (props.role === 'sales') return '发布'
+  if (props.role === 'dispatch') return '新增'
+  return '发布'
+})
+
+const filteredTasks = computed(() => {
+  if (currentFilter.value === 'all') return taskStore.tasks
+  if (currentFilter.value === 'dispatching') return taskStore.tasks.filter(t => ['confirmed', 'scheduled'].includes(t.status))
+  if (currentFilter.value === 'processing') return taskStore.tasks.filter(t => ['scheduled', 'departed', 'arrived', 'constructing'].includes(t.status))
+  if (currentFilter.value === 'emergency') return taskStore.tasks.filter(t => t.type === 'emergency')
+  return taskStore.tasks.filter(t => t.status === currentFilter.value)
+})
+
+function goToDetail(taskId: string) {
+  taskStore.setCurrentTask(taskId)
+  uni.navigateTo({ url: detailPath.value })
+}
+
+function goToPublishTask() {
+  if (!config.value.publishPath) return
+  uni.navigateTo({ url: config.value.publishPath })
+}
+
+function refresh() {
+  return taskStore.fetchTasks()
+}
+
+defineExpose({ refresh })
+</script>