Parcourir la source

feat: 分包优化主包体积并调整返回图标

- 删除未使用的 login-backpage.png(3.5MB),主包降至 1MB
- 客户管理/合同管理页面移入 pages-customer/pages-contract 分包
- TopBar 返回图标改用 uni-icons 组件

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
xuyunhui il y a 1 semaine
Parent
commit
eb875aba60

+ 1 - 1
src/api/request.ts

@@ -1,6 +1,6 @@
 // HTTP 请求封装
 
-const BASE_URL = 'http://localhost:8080/api'
+const BASE_URL = 'http://localhost:8081/api'
 
 interface RequestOptions {
   url: string

+ 17 - 18
src/components/common/StatusTag.vue

@@ -1,34 +1,30 @@
 <template>
-  <view class="inline-flex items-center px-2 rounded text-xs font-medium" :style="{ backgroundColor: bgColor, color: textColor, paddingTop: '2px', paddingBottom: '2px' }">
+  <view class="inline-flex items-centerrounded text-xs font-medium StatusTag"
+        :style="{ backgroundColor: bgColor, color: textColor, paddingTop: '2px', paddingBottom: '2px' }">
     {{ label }}
   </view>
 </template>
 
 <script setup lang="ts">
-import { computed } from 'vue'
-import {
-  getStatusText,
-  getStatusColor,
-  getStatusBgColor,
-  getProjectStatusText
-} from '@/utils'
+import {computed} from 'vue'
+import {getProjectStatusText, getStatusBgColor, getStatusColor, getStatusText} from '@/utils'
 
 type StatusTagType = 'task' | 'project'
 
 const props = withDefaults(
-  defineProps<{
-    status: string
-    type?: StatusTagType
-  }>(),
-  {
-    type: 'task'
-  }
+    defineProps<{
+      status: string
+      type?: StatusTagType
+    }>(),
+    {
+      type: 'task'
+    }
 )
 
 const label = computed(() => {
   return props.type === 'project'
-    ? getProjectStatusText(props.status)
-    : getStatusText(props.status)
+      ? getProjectStatusText(props.status)
+      : getStatusText(props.status)
 })
 
 const textColor = computed(() => getStatusColor(props.status))
@@ -36,5 +32,8 @@ const bgColor = computed(() => getStatusBgColor(props.status))
 </script>
 
 <style scoped>
-/* Tailwind 工具类已覆盖 */
+.StatusTag {
+  width: 88rpx;
+  padding: 0 10rpx;
+}
 </style>

+ 4 - 3
src/components/common/TopBar.vue

@@ -12,9 +12,10 @@
           class="mr-2 p-2 -ml-2 clickable rounded-lg"
           @click="goBack"
         >
-          <text
-            class="uni-icons text-xl"
-            :class="props.gradient ? 'uniui-arrow-left text-white' : 'uniui-arrow-left text-gray-600'"
+          <uni-icons
+            type="left"
+            size="20"
+            :color="props.gradient ? '#ffffff' : '#4b5563'"
           />
         </view>
         <slot name="left" />

+ 2 - 2
src/components/home/SalesHome.vue

@@ -224,11 +224,11 @@ function goToKnowledgeDetail() {
 }
 
 function goToCustomer() {
-  uni.navigateTo({ url: '/pages/customer/customer' })
+  uni.navigateTo({ url: '/subPackages/pages-customer/customer' })
 }
 
 function goToContract() {
-  uni.navigateTo({ url: '/pages/contract/contract' })
+  uni.navigateTo({ url: '/subPackages/pages-contract/contract' })
 }
 
 onMounted(() => {

+ 69 - 14
src/components/task/TaskListView.vue

@@ -8,15 +8,13 @@
       </template>
     </TopBar>
 
-    <!-- 筛选标签 -->
-    <view class="bg-white px-4 py-3 flex gap-2 overflow-x-auto hide-scrollbar">
+    <!-- 筛选标签:药丸式分段控件 -->
+    <view class="filter-tabs">
       <view
         v-for="status in filterStatuses"
         :key="status.value"
-        class="filter-tab px-3 py-2 rounded-full text-sm whitespace-nowrap transition-all duration-150"
-        :class="currentFilter === status.value ? 'bg-primary text-white' : 'bg-surface text-gray-500'"
-        hover-class="scale-down"
-        :hover-stay-time="150"
+        class="filter-tab"
+        :class="{ 'filter-tab-active': currentFilter === status.value }"
         @click="currentFilter = status.value"
       >
         {{ status.label }}
@@ -64,14 +62,10 @@
       </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 v-if="config.publishPath && role !== 'dispatch'" class="fab" @click="goToPublishTask">
+      <text class="uni-icons uniui-plusempty fab-icon"></text>
+      <text class="fab-text">发布</text>
     </view>
   </view>
 </template>
@@ -179,4 +173,65 @@ defineExpose({ refresh })
 .scale-down:active {
   transform: scale(0.95);
 }
+
+/* ==================== 筛选标签:药丸式分段控件 ==================== */
+.filter-tabs {
+  display: flex;
+  gap: 8px;
+  padding: 10px 16px;
+  background-color: #ffffff;
+  overflow-x: auto;
+  white-space: nowrap;
+}
+
+.filter-tab {
+  flex-shrink: 0;
+  padding: 6px 16px;
+  border-radius: 999px;
+  font-size: 13px;
+  color: #709484;                      /* 晨雾灰绿 */
+  background-color: #f6fbf9;           /* 云雾白 */
+  border: 1px solid rgba(164, 216, 152, 0.4);
+  transition: all 0.18s ease;
+}
+
+.filter-tab-active {
+  color: #ffffff;
+  background: linear-gradient(135deg, #368f6f 0%, #5ab8d0 100%); /* 青山绿 → 流水青 */
+  border-color: transparent;
+  box-shadow: 0 6px 14px -6px rgba(54, 143, 111, 0.6);
+}
+
+/* ==================== 悬浮按钮(位于 tabBar 上方) ==================== */
+.fab {
+  position: fixed;
+  right: 20px;
+  bottom: calc(80px + env(safe-area-inset-bottom));
+  z-index: 99;
+  display: flex;
+  align-items: center;
+  gap: 4px;
+  height: 50px;
+  padding: 0 22px;
+  border-radius: 999px;
+  background: linear-gradient(135deg, #368f6f 0%, #5ab8d0 100%);
+  box-shadow: 0 10px 22px -8px rgba(54, 143, 111, 0.65);
+}
+
+.fab:active {
+  transform: scale(0.95);
+  opacity: 0.9;
+}
+
+.fab-icon {
+  color: #ffffff;
+  font-size: 18px;
+}
+
+.fab-text {
+  color: #ffffff;
+  font-size: 15px;
+  font-weight: 600;
+  letter-spacing: 1px;
+}
 </style>

+ 16 - 16
src/pages.json

@@ -48,22 +48,6 @@
         "navigationBarTitleText": "排班",
         "navigationStyle": "custom"
       }
-    },
-    {
-      "path": "pages/customer/customer",
-      "style": {
-        "navigationBarTitleText": "客户管理",
-        "navigationStyle": "custom",
-        "enablePullDownRefresh": true
-      }
-    },
-    {
-      "path": "pages/contract/contract",
-      "style": {
-        "navigationBarTitleText": "合同管理",
-        "navigationStyle": "custom",
-        "enablePullDownRefresh": true
-      }
     }
   ],
   "tabBar": {
@@ -397,6 +381,14 @@
     {
       "root": "subPackages/pages-customer",
       "pages": [
+        {
+          "path": "customer",
+          "style": {
+            "navigationBarTitleText": "客户管理",
+            "navigationStyle": "custom",
+            "enablePullDownRefresh": true
+          }
+        },
         {
           "path": "customerDetail",
           "style": {
@@ -423,6 +415,14 @@
     {
       "root": "subPackages/pages-contract",
       "pages": [
+        {
+          "path": "contract",
+          "style": {
+            "navigationBarTitleText": "合同管理",
+            "navigationStyle": "custom",
+            "enablePullDownRefresh": true
+          }
+        },
         {
           "path": "contractDetail",
           "style": {

+ 152 - 7
src/pages/index/index.vue

@@ -1,10 +1,27 @@
 <template>
-  <view class="launch-page flex flex-col items-center justify-center min-h-screen bg-background">
-    <view class="w-20 h-20 bg-primary rounded-2xl flex items-center justify-center mb-6 shadow-lg">
-      <text class="text-3xl font-bold text-white">清</text>
+  <view class="launch-page">
+    <!-- 背景图:与登录页一致的青山绿水 -->
+    <image class="bg-img" src="/static/login-bg.jpeg" mode="aspectFill" />
+    <!-- 柔光蒙版:与登录页一致的渐变 -->
+    <view class="veil"></view>
+
+    <!-- 内容层 -->
+    <view class="content">
+      <view class="brand">
+        <view class="logo">
+          <view class="leaf">
+            <view class="leaf-vein"></view>
+          </view>
+        </view>
+        <text class="title">绿水青山</text>
+        <text class="subtitle">与自然同行</text>
+      </view>
+
+      <!-- 加载指示:嫩芽绿呼吸点 -->
+      <view class="loading">
+        <view class="dot" v-for="i in 3" :key="i"></view>
+      </view>
     </view>
-    <text class="text-xl font-bold text-gray-800 mb-2">清道夫系统</text>
-    <text class="text-sm text-gray-400">智能清洁管理平台</text>
   </view>
 </template>
 
@@ -16,7 +33,7 @@ import { delay } from '@/utils'
 const authStore = useAuthStore()
 
 onLoad(async () => {
-  await delay(500)
+  await delay(800)
   authStore.checkLogin()
   if (authStore.isLoggedIn && authStore.userRole) {
     uni.switchTab({ url: '/pages/home/home' })
@@ -27,8 +44,136 @@ onLoad(async () => {
 </script>
 
 <style scoped>
+/* ==================== 页面外壳:与登录页一致 ==================== */
 .launch-page {
-  max-width: 430px;
+  position: relative;
+  max-width: 448px;
   margin: 0 auto;
+  min-height: 100vh;
+  display: flex;
+  flex-direction: column;
+  overflow: hidden;
+  background-color: #f6fbf9;     /* 云雾白兜底 */
+}
+
+.bg-img {
+  position: absolute;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  z-index: 0;
+}
+
+.veil {
+  position: absolute;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  z-index: 1;
+  background: linear-gradient(
+    180deg,
+    rgba(255, 255, 255, 0.52) 0%,
+    rgba(246, 251, 249, 0.18) 38%,
+    rgba(246, 251, 249, 0.10) 58%,
+    rgba(31, 71, 56, 0.22) 100%
+  );
+}
+
+/* ==================== 内容层 ==================== */
+.content {
+  position: relative;
+  z-index: 2;
+  flex: 1;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  padding: 56px 32px calc(40px + env(safe-area-inset-bottom));
+}
+
+.brand {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+}
+
+/* 磨砂玻璃 Logo 容器:与登录页一致 */
+.logo {
+  width: 76px;
+  height: 76px;
+  border-radius: 24px;
+  background-color: rgba(255, 255, 255, 0.86);
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  box-shadow: 0 10px 28px -8px rgba(54, 143, 111, 0.45);
+  backdrop-filter: blur(8px);
+  -webkit-backdrop-filter: blur(8px);
+}
+
+/* 叶子 Logo(CSS 绘制,与登录页一致) */
+.leaf {
+  position: relative;
+  width: 38px;
+  height: 38px;
+  background: linear-gradient(135deg, #a4d898 0%, #368f6f 100%);
+  border-radius: 4px 28px 4px 28px;
+  transform: rotate(45deg);
+}
+
+.leaf-vein {
+  position: absolute;
+  left: 50%;
+  top: 4px;
+  bottom: 4px;
+  width: 2px;
+  margin-left: -1px;
+  border-radius: 2px;
+  background-color: rgba(255, 255, 255, 0.7);
+}
+
+.title {
+  margin-top: 22px;
+  font-size: 34px;
+  font-weight: 800;
+  letter-spacing: 6px;
+  color: #368f6f;                /* 青山绿 */
+}
+
+.subtitle {
+  margin-top: 10px;
+  font-size: 14px;
+  letter-spacing: 4px;
+  color: #709484;                /* 晨雾灰绿 */
+}
+
+/* ==================== 加载指示 ==================== */
+.loading {
+  display: flex;
+  gap: 10px;
+  margin-top: 48px;
+}
+
+.dot {
+  width: 8px;
+  height: 8px;
+  border-radius: 50%;
+  background-color: #a4d898;     /* 嫩芽绿 */
+  animation: breathe 1.2s ease-in-out infinite;
+}
+
+.dot:nth-child(2) {
+  animation-delay: 0.2s;
+}
+
+.dot:nth-child(3) {
+  animation-delay: 0.4s;
+}
+
+@keyframes breathe {
+  0%, 100% { opacity: 0.3; transform: scale(0.8); }
+  50% { opacity: 1; transform: scale(1.2); }
 }
 </style>

BIN
src/static/login-backpage.png


+ 42 - 9
src/subPackages/pages-common/noticeList.vue

@@ -2,14 +2,18 @@
   <view class="app-container pb-20">
     <TopBar title="通知公告" show-back />
     
-    <!-- 筛选标签 - 使用 uni-segmented-control -->
-    <uni-segmented-control 
-      :current="currentFilterIndex" 
-      :values="filterLabels"
-      style-type="text"
-      active-color="#4f8ef7"
-      @clickItem="onFilterChange"
-    />
+    <!-- 筛选标签:药丸式分段控件 -->
+    <view class="filter-tabs">
+      <view
+        v-for="(label, index) in filterLabels"
+        :key="index"
+        class="filter-tab"
+        :class="{ 'filter-tab-active': currentFilterIndex === index }"
+        @click="onFilterChange(index)"
+      >
+        {{ label }}
+      </view>
+    </view>
 
     <!-- 通知列表 -->
     <view class="mt-2">
@@ -54,7 +58,6 @@ import EmptyState from '../../components/common/EmptyState.vue'
 import UniList from '../../components/uni-list/uni-list.vue'
 import UniListItem from '../../components/uni-list/uni-list-item.vue'
 import UniSwipeAction from '../../components/uni-swipe-action/uni-swipe-action.vue'
-import UniSegmentedControl from '../../components/uni-segmented-control/uni-segmented-control.vue'
 import { getNoticeList } from '../../api/notice'
 import { formatDate } from '../../utils'
 
@@ -130,3 +133,33 @@ onMounted(async () => {
   }
 })
 </script>
+
+<style scoped>
+/* ==================== 筛选标签:药丸式分段控件 ==================== */
+.filter-tabs {
+  display: flex;
+  gap: 8px;
+  padding: 10px 16px;
+  background-color: #ffffff;
+  overflow-x: auto;
+  white-space: nowrap;
+}
+
+.filter-tab {
+  flex-shrink: 0;
+  padding: 6px 16px;
+  border-radius: 999px;
+  font-size: 13px;
+  color: #709484;                      /* 晨雾灰绿 */
+  background-color: #f6fbf9;           /* 云雾白 */
+  border: 1px solid rgba(164, 216, 152, 0.4);
+  transition: all 0.18s ease;
+}
+
+.filter-tab-active {
+  color: #ffffff;
+  background: linear-gradient(135deg, #368f6f 0%, #5ab8d0 100%); /* 青山绿 → 流水青 */
+  border-color: transparent;
+  box-shadow: 0 6px 14px -6px rgba(54, 143, 111, 0.6);
+}
+</style>

+ 94 - 28
src/subPackages/pages-common/projectList.vue

@@ -1,46 +1,43 @@
 
 <template>
   <view class="app-container pb-24">
-    <TopBar title="项目库" show-back>
-      <template #right>
-        <view class="px-2 py-1 flex items-center" @click="goToAdd">
-          <text class="uni-icons uniui-plusempty text-primary text-xl"></text>
-          <text class="text-primary text-sm ml-1">新增</text>
-        </view>
-      </template>
-    </TopBar>
-    
+    <TopBar title="项目库" show-back />
+
     <!-- 顶部搜索栏 -->
     <view class="bg-white px-4 py-3 sticky top-0 z-40">
       <view class="flex items-center bg-gray-100 rounded-2xl px-4 py-2">
         <text class="uni-icons uniui-search text-gray-400 mr-2 text-base"></text>
-        <input 
-          class="flex-1 bg-transparent text-sm text-gray-700" 
-          placeholder="搜索项目名称" 
+        <input
+          class="flex-1 bg-transparent text-sm text-gray-700"
+          placeholder="搜索项目名称"
           v-model="searchKeyword"
         />
       </view>
     </view>
 
-    <!-- 顶部筛选标签 - 使用 uni-segmented-control -->
-    <uni-segmented-control 
-      :current="currentFilterIndex" 
-      :values="filterLabels"
-      style-type="text"
-      active-color="#4f8ef7"
-      @clickItem="onFilterChange"
-    />
+    <!-- 顶部筛选标签:药丸式分段控件 -->
+    <view class="filter-tabs">
+      <view
+        v-for="(label, index) in filterLabels"
+        :key="index"
+        class="filter-tab"
+        :class="{ 'filter-tab-active': currentFilterIndex === index }"
+        @click="onFilterChange(index)"
+      >
+        {{ label }}
+      </view>
+    </view>
 
     <!-- 项目列表 - 使用 uni-list + uni-card + uni-swipe-action -->
     <view class="mt-2">
       <view v-if="projectStore.loading" class="py-16 text-center">
         <text class="text-gray-400">加载中...</text>
       </view>
-      
+
       <view v-else-if="filteredProjects.length === 0">
         <EmptyState message="暂无项目" />
       </view>
-      
+
       <uni-list v-else border>
         <uni-swipe-action
           v-for="project in filteredProjects"
@@ -48,7 +45,7 @@
           :actions="swipeActions"
           @click="onSwipeAction(project.id, $event)"
         >
-          <uni-list-item 
+          <uni-list-item
             :title="project.name"
             :note="project.address + ' | ' + project.customerName"
             :right-text="project.type"
@@ -65,10 +62,16 @@
       </uni-list>
     </view>
 
+    <!-- 新增悬浮按钮:避开胶囊按钮 -->
+    <view class="fab" @click="goToAdd">
+      <text class="uni-icons uniui-plusempty fab-icon"></text>
+      <text class="fab-text">新增</text>
+    </view>
+
     <!-- 加载更多 -->
     <view v-if="filteredProjects.length > 0" class="px-4 mt-4 mb-4">
       <view v-if="projectStore.hasMore" class="text-center py-4">
-        <text 
+        <text
           class="text-sm text-blue-500 font-medium"
           @click="loadMore"
         >
@@ -91,7 +94,6 @@ import TopBar from '../../components/common/TopBar.vue'
 import UniList from '../../components/uni-list/uni-list.vue'
 import UniListItem from '../../components/uni-list/uni-list-item.vue'
 import UniSwipeAction from '../../components/uni-swipe-action/uni-swipe-action.vue'
-import UniSegmentedControl from '../../components/uni-segmented-control/uni-segmented-control.vue'
 
 const projectStore = useProjectStore()
 const searchKeyword = ref('')
@@ -110,15 +112,15 @@ const filterLabels = filterTypes.map(t => t.label)
 const filteredProjects = computed(() => {
   let list = projectStore.projects
   const filterValue = filterTypes[currentFilterIndex.value].value
-  
+
   if (filterValue !== 'all') {
     list = list.filter(p => p.type === filterValue)
   }
-  
+
   if (searchKeyword.value) {
     list = list.filter(p => p.name.includes(searchKeyword.value))
   }
-  
+
   return list
 })
 
@@ -177,3 +179,67 @@ onPullDownRefresh(() => {
   refreshData()
 })
 </script>
+
+<style scoped>
+/* ==================== 筛选标签:药丸式分段控件 ==================== */
+.filter-tabs {
+  display: flex;
+  gap: 8px;
+  padding: 10px 16px;
+  background-color: #ffffff;
+  overflow-x: auto;
+  white-space: nowrap;
+}
+
+.filter-tab {
+  flex-shrink: 0;
+  padding: 6px 16px;
+  border-radius: 999px;
+  font-size: 13px;
+  color: #709484;                      /* 晨雾灰绿 */
+  background-color: #f6fbf9;           /* 云雾白 */
+  border: 1px solid rgba(164, 216, 152, 0.4);
+  transition: all 0.18s ease;
+}
+
+.filter-tab-active {
+  color: #ffffff;
+  background: linear-gradient(135deg, #368f6f 0%, #5ab8d0 100%); /* 青山绿 → 流水青 */
+  border-color: transparent;
+  box-shadow: 0 6px 14px -6px rgba(54, 143, 111, 0.6);
+}
+
+/* ==================== 新增悬浮按钮 ==================== */
+.fab {
+  position: fixed;
+  right: 20px;
+  bottom: calc(28px + env(safe-area-inset-bottom));
+  z-index: 99;
+  display: flex;
+  align-items: center;
+  gap: 4px;
+  height: 50px;
+  padding: 0 22px;
+  border-radius: 999px;
+  background: linear-gradient(135deg, #368f6f 0%, #5ab8d0 100%);
+  box-shadow: 0 10px 22px -8px rgba(54, 143, 111, 0.65);
+}
+
+.fab:active {
+  transform: scale(0.95);
+  opacity: 0.9;
+}
+
+.fab-icon {
+  color: #ffffff;
+  font-size: 18px;
+}
+
+.fab-text {
+  color: #ffffff;
+  font-size: 15px;
+  font-weight: 600;
+  letter-spacing: 1px;
+}
+</style>
+

+ 82 - 17
src/pages/contract/contract.vue → src/subPackages/pages-contract/contract.vue

@@ -1,13 +1,6 @@
 <template>
   <view class="app-container pb-24">
-    <TopBar title="合同管理" show-back>
-      <template #right>
-        <view class="px-2 py-1 flex items-center" @click="goToAdd">
-          <text class="uni-icons uniui-plusempty text-primary text-xl"></text>
-          <text class="text-primary text-sm ml-1">新增</text>
-        </view>
-      </template>
-    </TopBar>
+    <TopBar title="合同管理" show-back />
 
     <!-- 搜索栏 -->
     <view class="bg-white px-4 py-3 sticky top-0 z-40">
@@ -23,14 +16,18 @@
       </view>
     </view>
 
-    <!-- 状态筛选 -->
-    <uni-segmented-control
-      :current="currentFilterIndex"
-      :values="filterLabels"
-      style-type="text"
-      active-color="#4f8ef7"
-      @clickItem="onFilterChange"
-    />
+    <!-- 状态筛选:药丸式分段控件 -->
+    <view class="filter-tabs">
+      <view
+        v-for="(label, index) in filterLabels"
+        :key="index"
+        class="filter-tab"
+        :class="{ 'filter-tab-active': currentFilterIndex === index }"
+        @click="onFilterChange(index)"
+      >
+        {{ label }}
+      </view>
+    </view>
 
     <!-- 列表 -->
     <view class="mt-2">
@@ -73,6 +70,12 @@
       </uni-list>
     </view>
 
+    <!-- 新增悬浮按钮:避开胶囊按钮 -->
+    <view class="fab" @click="goToAdd">
+      <text class="uni-icons uniui-plusempty fab-icon"></text>
+      <text class="fab-text">新增</text>
+    </view>
+
     <!-- 加载更多 -->
     <view v-if="contractStore.contracts.length > 0" class="px-4 mt-4 mb-4">
       <view v-if="contractStore.hasMore" class="text-center py-4">
@@ -104,7 +107,6 @@ import EmptyState from '@/components/common/EmptyState.vue'
 import UniList from '@/components/uni-list/uni-list.vue'
 import UniListItem from '@/components/uni-list/uni-list-item.vue'
 import UniSwipeAction from '@/components/uni-swipe-action/uni-swipe-action.vue'
-import UniSegmentedControl from '@/components/uni-segmented-control/uni-segmented-control.vue'
 
 const contractStore = useContractStore()
 const searchKeyword = ref('')
@@ -201,3 +203,66 @@ onPullDownRefresh(() => {
   refreshData()
 })
 </script>
+
+<style scoped>
+/* ==================== 筛选标签:药丸式分段控件 ==================== */
+.filter-tabs {
+  display: flex;
+  gap: 8px;
+  padding: 10px 16px;
+  background-color: #ffffff;
+  overflow-x: auto;
+  white-space: nowrap;
+}
+
+.filter-tab {
+  flex-shrink: 0;
+  padding: 6px 16px;
+  border-radius: 999px;
+  font-size: 13px;
+  color: #709484;                      /* 晨雾灰绿 */
+  background-color: #f6fbf9;           /* 云雾白 */
+  border: 1px solid rgba(164, 216, 152, 0.4);
+  transition: all 0.18s ease;
+}
+
+.filter-tab-active {
+  color: #ffffff;
+  background: linear-gradient(135deg, #368f6f 0%, #5ab8d0 100%); /* 青山绿 → 流水青 */
+  border-color: transparent;
+  box-shadow: 0 6px 14px -6px rgba(54, 143, 111, 0.6);
+}
+
+/* ==================== 新增悬浮按钮 ==================== */
+.fab {
+  position: fixed;
+  right: 20px;
+  bottom: calc(28px + env(safe-area-inset-bottom));
+  z-index: 99;
+  display: flex;
+  align-items: center;
+  gap: 4px;
+  height: 50px;
+  padding: 0 22px;
+  border-radius: 999px;
+  background: linear-gradient(135deg, #368f6f 0%, #5ab8d0 100%);
+  box-shadow: 0 10px 22px -8px rgba(54, 143, 111, 0.65);
+}
+
+.fab:active {
+  transform: scale(0.95);
+  opacity: 0.9;
+}
+
+.fab-icon {
+  color: #ffffff;
+  font-size: 18px;
+}
+
+.fab-text {
+  color: #ffffff;
+  font-size: 15px;
+  font-weight: 600;
+  letter-spacing: 1px;
+}
+</style>

+ 82 - 17
src/pages/customer/customer.vue → src/subPackages/pages-customer/customer.vue

@@ -1,13 +1,6 @@
 <template>
   <view class="app-container pb-24">
-    <TopBar title="客户管理" show-back>
-      <template #right>
-        <view class="px-2 py-1 flex items-center" @click="goToAdd">
-          <text class="uni-icons uniui-plusempty text-primary text-xl"></text>
-          <text class="text-primary text-sm ml-1">新增</text>
-        </view>
-      </template>
-    </TopBar>
+    <TopBar title="客户管理" show-back />
 
     <!-- 搜索栏 -->
     <view class="bg-white px-4 py-3 sticky top-0 z-40">
@@ -23,14 +16,18 @@
       </view>
     </view>
 
-    <!-- 状态筛选 -->
-    <uni-segmented-control
-      :current="currentFilterIndex"
-      :values="filterLabels"
-      style-type="text"
-      active-color="#4f8ef7"
-      @clickItem="onFilterChange"
-    />
+    <!-- 状态筛选:药丸式分段控件 -->
+    <view class="filter-tabs">
+      <view
+        v-for="(label, index) in filterLabels"
+        :key="index"
+        class="filter-tab"
+        :class="{ 'filter-tab-active': currentFilterIndex === index }"
+        @click="onFilterChange(index)"
+      >
+        {{ label }}
+      </view>
+    </view>
 
     <!-- 列表 -->
     <view class="mt-2">
@@ -73,6 +70,12 @@
       </uni-list>
     </view>
 
+    <!-- 新增悬浮按钮:避开胶囊按钮 -->
+    <view class="fab" @click="goToAdd">
+      <text class="uni-icons uniui-plusempty fab-icon"></text>
+      <text class="fab-text">新增</text>
+    </view>
+
     <!-- 加载更多 -->
     <view v-if="filteredCustomers.length > 0" class="px-4 mt-4 mb-4">
       <view v-if="customerStore.hasMore" class="text-center py-4">
@@ -102,7 +105,6 @@ import EmptyState from '@/components/common/EmptyState.vue'
 import UniList from '@/components/uni-list/uni-list.vue'
 import UniListItem from '@/components/uni-list/uni-list-item.vue'
 import UniSwipeAction from '@/components/uni-swipe-action/uni-swipe-action.vue'
-import UniSegmentedControl from '@/components/uni-segmented-control/uni-segmented-control.vue'
 
 const customerStore = useCustomerStore()
 const searchKeyword = ref('')
@@ -200,3 +202,66 @@ onPullDownRefresh(() => {
   refreshData()
 })
 </script>
+
+<style scoped>
+/* ==================== 筛选标签:药丸式分段控件 ==================== */
+.filter-tabs {
+  display: flex;
+  gap: 8px;
+  padding: 10px 16px;
+  background-color: #ffffff;
+  overflow-x: auto;
+  white-space: nowrap;
+}
+
+.filter-tab {
+  flex-shrink: 0;
+  padding: 6px 16px;
+  border-radius: 999px;
+  font-size: 13px;
+  color: #709484;                      /* 晨雾灰绿 */
+  background-color: #f6fbf9;           /* 云雾白 */
+  border: 1px solid rgba(164, 216, 152, 0.4);
+  transition: all 0.18s ease;
+}
+
+.filter-tab-active {
+  color: #ffffff;
+  background: linear-gradient(135deg, #368f6f 0%, #5ab8d0 100%); /* 青山绿 → 流水青 */
+  border-color: transparent;
+  box-shadow: 0 6px 14px -6px rgba(54, 143, 111, 0.6);
+}
+
+/* ==================== 新增悬浮按钮 ==================== */
+.fab {
+  position: fixed;
+  right: 20px;
+  bottom: calc(28px + env(safe-area-inset-bottom));
+  z-index: 99;
+  display: flex;
+  align-items: center;
+  gap: 4px;
+  height: 50px;
+  padding: 0 22px;
+  border-radius: 999px;
+  background: linear-gradient(135deg, #368f6f 0%, #5ab8d0 100%);
+  box-shadow: 0 10px 22px -8px rgba(54, 143, 111, 0.65);
+}
+
+.fab:active {
+  transform: scale(0.95);
+  opacity: 0.9;
+}
+
+.fab-icon {
+  color: #ffffff;
+  font-size: 18px;
+}
+
+.fab-text {
+  color: #ffffff;
+  font-size: 15px;
+  font-weight: 600;
+  letter-spacing: 1px;
+}
+</style>

+ 39 - 10
src/subPackages/pages-dispatch/availableVehicles.vue

@@ -20,15 +20,17 @@
       </view>
     </view>
 
-    <!-- 筛选 -->
-    <view class="px-4 pb-2">
-      <uni-segmented-control
-        :current="currentFilterIndex"
-        :values="filterLabels"
-        style-type="text"
-        active-color="#4f8ef7"
-        @clickItem="onFilterChange"
-      />
+    <!-- 筛选:药丸式分段控件 -->
+    <view class="filter-tabs">
+      <view
+        v-for="(label, index) in filterLabels"
+        :key="index"
+        class="filter-tab"
+        :class="{ 'filter-tab-active': currentFilterIndex === index }"
+        @click="onFilterChange(index)"
+      >
+        {{ label }}
+      </view>
     </view>
 
     <!-- 车辆列表 -->
@@ -105,7 +107,6 @@
 import { ref, computed, onMounted } from 'vue'
 import TopBar from '../../components/common/TopBar.vue'
 import EmptyState from '../../components/common/EmptyState.vue'
-import UniSegmentedControl from '../../components/uni-segmented-control/uni-segmented-control.vue'
 import { useVehicleStore } from '../../stores/vehicle'
 
 const vehicleStore = useVehicleStore()
@@ -216,4 +217,32 @@ onMounted(() => {
   background: #f3f4f6;
   color: #6b7280;
 }
+
+/* ==================== 筛选标签:药丸式分段控件 ==================== */
+.filter-tabs {
+  display: flex;
+  gap: 8px;
+  padding: 10px 16px;
+  background-color: #ffffff;
+  overflow-x: auto;
+  white-space: nowrap;
+}
+
+.filter-tab {
+  flex-shrink: 0;
+  padding: 6px 16px;
+  border-radius: 999px;
+  font-size: 13px;
+  color: #709484;
+  background-color: #f6fbf9;
+  border: 1px solid rgba(164, 216, 152, 0.4);
+  transition: all 0.18s ease;
+}
+
+.filter-tab-active {
+  color: #ffffff;
+  background: linear-gradient(135deg, #368f6f 0%, #5ab8d0 100%);
+  border-color: transparent;
+  box-shadow: 0 6px 14px -6px rgba(54, 143, 111, 0.6);
+}
 </style>

+ 41 - 10
src/subPackages/pages-dispatch/dutyPersonnel.vue

@@ -2,15 +2,17 @@
   <view class="app-container pb-20">
     <TopBar title="当班人员" show-back />
     
-    <!-- 筛选 -->
-    <view class="bg-white px-4 py-3">
-      <uni-segmented-control 
-        :current="currentIndex" 
-        :values="teams.map(t => t.label)"
-        style-type="text"
-        active-color="#4f8ef7"
-        @clickItem="onTabChange"
-      />
+    <!-- 筛选:药丸式分段控件 -->
+    <view class="filter-tabs">
+      <view
+        v-for="(team, index) in teams"
+        :key="team.value"
+        class="filter-tab"
+        :class="{ 'filter-tab-active': currentIndex === index }"
+        @click="onTabChange(index)"
+      >
+        {{ team.label }}
+      </view>
     </view>
 
     <!-- 当班人员列表 -->
@@ -46,7 +48,6 @@
 import { ref, computed, onMounted } from 'vue'
 import TopBar from '../../components/common/TopBar.vue'
 import EmptyState from '../../components/common/EmptyState.vue'
-import UniSegmentedControl from '../../components/uni-segmented-control/uni-segmented-control.vue'
 import UniList from '../../components/uni-list/uni-list.vue'
 import UniListItem from '../../components/uni-list/uni-list-item.vue'
 import { getStaffList } from '../../api/staff'
@@ -92,3 +93,33 @@ onMounted(async () => {
   }
 })
 </script>
+
+<style scoped>
+/* ==================== 筛选标签:药丸式分段控件 ==================== */
+.filter-tabs {
+  display: flex;
+  gap: 8px;
+  padding: 10px 16px;
+  background-color: #ffffff;
+  overflow-x: auto;
+  white-space: nowrap;
+}
+
+.filter-tab {
+  flex-shrink: 0;
+  padding: 6px 16px;
+  border-radius: 999px;
+  font-size: 13px;
+  color: #709484;
+  background-color: #f6fbf9;
+  border: 1px solid rgba(164, 216, 152, 0.4);
+  transition: all 0.18s ease;
+}
+
+.filter-tab-active {
+  color: #ffffff;
+  background: linear-gradient(135deg, #368f6f 0%, #5ab8d0 100%);
+  border-color: transparent;
+  box-shadow: 0 6px 14px -6px rgba(54, 143, 111, 0.6);
+}
+</style>

+ 42 - 9
src/subPackages/pages-dispatch/personnel.vue

@@ -2,14 +2,18 @@
   <view class="app-container pb-20">
     <TopBar title="人员管理" show-back />
     
-    <!-- 筛选标签 - 使用 uni-segmented-control -->
-    <uni-segmented-control 
-      :current="currentFilterIndex" 
-      :values="filterLabels"
-      style-type="text"
-      active-color="#4f8ef7"
-      @clickItem="onFilterChange"
-    />
+    <!-- 筛选标签:药丸式分段控件 -->
+    <view class="filter-tabs">
+      <view
+        v-for="(label, index) in filterLabels"
+        :key="index"
+        class="filter-tab"
+        :class="{ 'filter-tab-active': currentFilterIndex === index }"
+        @click="onFilterChange(index)"
+      >
+        {{ label }}
+      </view>
+    </view>
 
     <!-- 人员列表 -->
     <view class="mt-2">
@@ -49,7 +53,6 @@ import TopBar from '../../components/common/TopBar.vue'
 import EmptyState from '../../components/common/EmptyState.vue'
 import UniList from '../../components/uni-list/uni-list.vue'
 import UniListItem from '../../components/uni-list/uni-list-item.vue'
-import UniSegmentedControl from '../../components/uni-segmented-control/uni-segmented-control.vue'
 import { getStaffList } from '../../api/staff'
 
 const loading = ref(false)
@@ -132,3 +135,33 @@ onPullDownRefresh(async () => {
   uni.stopPullDownRefresh()
 })
 </script>
+
+<style scoped>
+/* ==================== 筛选标签:药丸式分段控件 ==================== */
+.filter-tabs {
+  display: flex;
+  gap: 8px;
+  padding: 10px 16px;
+  background-color: #ffffff;
+  overflow-x: auto;
+  white-space: nowrap;
+}
+
+.filter-tab {
+  flex-shrink: 0;
+  padding: 6px 16px;
+  border-radius: 999px;
+  font-size: 13px;
+  color: #709484;
+  background-color: #f6fbf9;
+  border: 1px solid rgba(164, 216, 152, 0.4);
+  transition: all 0.18s ease;
+}
+
+.filter-tab-active {
+  color: #ffffff;
+  background: linear-gradient(135deg, #368f6f 0%, #5ab8d0 100%);
+  border-color: transparent;
+  box-shadow: 0 6px 14px -6px rgba(54, 143, 111, 0.6);
+}
+</style>

+ 40 - 9
src/subPackages/pages-dispatch/vehicleList.vue

@@ -2,14 +2,18 @@
   <view class="app-container pb-20">
     <TopBar title="车辆列表" show-back />
 
-    <!-- 筛选标签 -->
-    <uni-segmented-control
-      :current="currentFilterIndex"
-      :values="filterLabels"
-      style-type="text"
-      active-color="#4f8ef7"
-      @clickItem="onFilterChange"
-    />
+    <!-- 筛选标签:药丸式分段控件 -->
+    <view class="filter-tabs">
+      <view
+        v-for="(label, index) in filterLabels"
+        :key="index"
+        class="filter-tab"
+        :class="{ 'filter-tab-active': currentFilterIndex === index }"
+        @click="onFilterChange(index)"
+      >
+        {{ label }}
+      </view>
+    </view>
 
     <!-- 车辆列表 -->
     <view class="mt-2 px-4">
@@ -70,7 +74,6 @@ import { ref, computed, onMounted } from 'vue'
 import { onPullDownRefresh } from '@dcloudio/uni-app'
 import TopBar from '../../components/common/TopBar.vue'
 import EmptyState from '../../components/common/EmptyState.vue'
-import UniSegmentedControl from '../../components/uni-segmented-control/uni-segmented-control.vue'
 import { useVehicleStore } from '../../stores/vehicle'
 import type { Vehicle } from '../../types'
 
@@ -197,4 +200,32 @@ onPullDownRefresh(async () => {
   background: #f3f4f6;
   color: #6b7280;
 }
+
+/* ==================== 筛选标签:药丸式分段控件 ==================== */
+.filter-tabs {
+  display: flex;
+  gap: 8px;
+  padding: 10px 16px;
+  background-color: #ffffff;
+  overflow-x: auto;
+  white-space: nowrap;
+}
+
+.filter-tab {
+  flex-shrink: 0;
+  padding: 6px 16px;
+  border-radius: 999px;
+  font-size: 13px;
+  color: #709484;
+  background-color: #f6fbf9;
+  border: 1px solid rgba(164, 216, 152, 0.4);
+  transition: all 0.18s ease;
+}
+
+.filter-tab-active {
+  color: #ffffff;
+  background: linear-gradient(135deg, #368f6f 0%, #5ab8d0 100%);
+  border-color: transparent;
+  box-shadow: 0 6px 14px -6px rgba(54, 143, 111, 0.6);
+}
 </style>

+ 41 - 10
src/subPackages/pages-sales/messageList.vue

@@ -2,15 +2,17 @@
   <view class="app-container pb-20">
     <TopBar title="消息通知" show-back />
     
-    <!-- 筛选标签 -->
-    <view class="bg-white px-4 py-2">
-      <uni-segmented-control 
-        :current="currentFilterIndex" 
-        :values="filterLabels"
-        style-type="text"
-        active-color="#4f8ef7"
-        @clickItem="onFilterChange"
-      />
+    <!-- 筛选标签:药丸式分段控件 -->
+    <view class="filter-tabs">
+      <view
+        v-for="(label, index) in filterLabels"
+        :key="index"
+        class="filter-tab"
+        :class="{ 'filter-tab-active': currentFilterIndex === index }"
+        @click="onFilterChange(index)"
+      >
+        {{ label }}
+      </view>
     </view>
 
     <!-- 消息列表 -->
@@ -59,7 +61,6 @@ import EmptyState from '../../components/common/EmptyState.vue'
 import UniList from '../../components/uni-list/uni-list.vue'
 import UniListItem from '../../components/uni-list/uni-list-item.vue'
 import UniSwipeAction from '../../components/uni-swipe-action/uni-swipe-action.vue'
-import UniSegmentedControl from '../../components/uni-segmented-control/uni-segmented-control.vue'
 import { formatDate } from '../../utils'
 
 const loading = ref(false)
@@ -136,3 +137,33 @@ onMounted(() => {
   ]
 })
 </script>
+
+<style scoped>
+/* ==================== 筛选标签:药丸式分段控件 ==================== */
+.filter-tabs {
+  display: flex;
+  gap: 8px;
+  padding: 10px 16px;
+  background-color: #ffffff;
+  overflow-x: auto;
+  white-space: nowrap;
+}
+
+.filter-tab {
+  flex-shrink: 0;
+  padding: 6px 16px;
+  border-radius: 999px;
+  font-size: 13px;
+  color: #709484;
+  background-color: #f6fbf9;
+  border: 1px solid rgba(164, 216, 152, 0.4);
+  transition: all 0.18s ease;
+}
+
+.filter-tab-active {
+  color: #ffffff;
+  background: linear-gradient(135deg, #368f6f 0%, #5ab8d0 100%);
+  border-color: transparent;
+  box-shadow: 0 6px 14px -6px rgba(54, 143, 111, 0.6);
+}
+</style>