| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245 |
- <template>
- <view class="app-container pb-24">
- <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="搜索项目名称"
- v-model="searchKeyword"
- />
- </view>
- </view>
- <!-- 顶部筛选标签:药丸式分段控件 -->
- <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"
- :key="project.id"
- :actions="swipeActions"
- @click="onSwipeAction(project.id, $event)"
- >
- <uni-list-item
- :title="project.name"
- :note="project.address + ' | ' + project.customerName"
- :right-text="project.type"
- show-arrow
- @click="goToDetail(project.id)"
- >
- <template #header>
- <view class="w-10 h-10 bg-blue-50 rounded-xl flex items-center justify-center mr-3">
- <text class="uni-icons uniui-folder-add-filled text-blue-500 text-lg"></text>
- </view>
- </template>
- </uni-list-item>
- </uni-swipe-action>
- </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
- class="text-sm text-blue-500 font-medium"
- @click="loadMore"
- >
- {{ projectStore.loadingMore ? '加载中...' : '加载更多' }}
- </text>
- </view>
- <view v-else class="text-center py-4">
- <text class="text-xs text-gray-400">已经到底了</text>
- </view>
- </view>
- </view>
- </template>
- <script setup lang="ts">
- import { ref, computed, onMounted } from 'vue'
- import { onPullDownRefresh } from '@dcloudio/uni-app'
- import { useProjectStore } from '../../stores/project'
- import EmptyState from '../../components/common/EmptyState.vue'
- 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'
- const projectStore = useProjectStore()
- const searchKeyword = ref('')
- const currentFilterIndex = ref(0)
- const filterTypes = [
- { value: 'all', label: '全部' },
- { value: '包年', label: '包年' },
- { value: '单次', label: '单次' },
- { value: '季度', label: '季度' },
- { value: '月付', label: '月付' },
- ]
- 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
- })
- const swipeActions: { text: string; color: string; key: string }[] = [
- { text: '编辑', color: '#4f8ef7', key: 'edit' },
- { text: '删除', color: '#ff3b30', key: 'delete' },
- ]
- function onFilterChange(index: number) {
- currentFilterIndex.value = index
- }
- function onSwipeAction(projectId: string, action: { key?: string; text?: string }) {
- if (action.key === 'delete') {
- uni.showModal({
- title: '确认删除',
- content: '删除后无法恢复,是否继续?',
- success: (res) => {
- if (res.confirm) {
- projectStore.deleteProject(projectId)
- uni.showToast({ title: '已删除', icon: 'success' })
- }
- }
- })
- } else if (action.key === 'edit') {
- projectStore.setCurrentProject(projectId)
- uni.navigateTo({ url: '/subPackages/pages-common/addProject?mode=edit' })
- }
- }
- function goToDetail(projectId: string) {
- projectStore.setCurrentProject(projectId)
- uni.navigateTo({ url: '/subPackages/pages-common/projectDetail' })
- }
- function goToAdd() {
- uni.navigateTo({ url: '/subPackages/pages-common/addProject' })
- }
- async function refreshData() {
- await projectStore.fetchProjects(true)
- uni.stopPullDownRefresh()
- }
- function loadMore() {
- if (projectStore.loadingMore) return
- projectStore.loadMore()
- }
- onMounted(() => {
- projectStore.fetchProjects(true)
- })
- // 下拉刷新
- 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>
|