332 lines
8.3 KiB
Vue
332 lines
8.3 KiB
Vue
|
|
<template>
|
|||
|
|
<view class="wh-full">
|
|||
|
|
<!-- Remove :filters and @filter-change -->
|
|||
|
|
<BasicListLayout @register="register">
|
|||
|
|
<!-- Add #top slot for filters -->
|
|||
|
|
<template #top>
|
|||
|
|
<view class="filter-section">
|
|||
|
|
<!-- Group Filter (Assuming 'group' key exists) -->
|
|||
|
|
<view class="filter-item" @click="openFilterPopup('group')">
|
|||
|
|
<text>全部分组</text>
|
|||
|
|
<uni-icons type="bottom" size="14"></uni-icons>
|
|||
|
|
</view>
|
|||
|
|
<!-- Sending Department Filter -->
|
|||
|
|
<view class="filter-item" @click="openFilterPopup('sendingDept')">
|
|||
|
|
<text>发文部门</text>
|
|||
|
|
<uni-icons type="bottom" size="14"></uni-icons>
|
|||
|
|
</view>
|
|||
|
|
<!-- Receiving Department Filter -->
|
|||
|
|
<view class="filter-item" @click="openFilterPopup('receivingDept')">
|
|||
|
|
<text>收文部门</text>
|
|||
|
|
<uni-icons type="bottom" size="14"></uni-icons>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
</template>
|
|||
|
|
|
|||
|
|
<!-- Define how each item is displayed using the slot -->
|
|||
|
|
<template v-slot:default="{ data }">
|
|||
|
|
<view
|
|||
|
|
class="document-item white-bg-color r-md p-15 mb-15"
|
|||
|
|
@click="goToDetail(data.id)"
|
|||
|
|
>
|
|||
|
|
<view class="item-title font-bold text-lg mb-5">{{
|
|||
|
|
data.title
|
|||
|
|
}}</view>
|
|||
|
|
<view class="item-meta color-9">
|
|||
|
|
<text>发文:{{ data.sendingDept }} {{ data.publishDate }}</text>
|
|||
|
|
<text>收文:{{ data.receivingDept }}</text>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
</template>
|
|||
|
|
</BasicListLayout>
|
|||
|
|
|
|||
|
|
<!-- Filter Popup -->
|
|||
|
|
<uni-popup ref="filterPopupRef" type="bottom" background-color="#fff">
|
|||
|
|
<view class="popup-content">
|
|||
|
|
<view class="popup-header">
|
|||
|
|
<text>{{ currentFilterTitle }}</text>
|
|||
|
|
<uni-icons
|
|||
|
|
type="closeempty"
|
|||
|
|
size="20"
|
|||
|
|
@click="closeFilterPopup"
|
|||
|
|
></uni-icons>
|
|||
|
|
</view>
|
|||
|
|
<scroll-view scroll-y class="popup-options">
|
|||
|
|
<view
|
|||
|
|
class="option-item"
|
|||
|
|
v-for="option in currentFilterOptions"
|
|||
|
|
:key="option.value"
|
|||
|
|
:class="{ active: isOptionSelected(option) }"
|
|||
|
|
@click="selectFilterOption(option)"
|
|||
|
|
>
|
|||
|
|
{{ option.label }}
|
|||
|
|
</view>
|
|||
|
|
</scroll-view>
|
|||
|
|
</view>
|
|||
|
|
</uni-popup>
|
|||
|
|
</view>
|
|||
|
|
</template>
|
|||
|
|
|
|||
|
|
<script setup lang="ts">
|
|||
|
|
import { ref, reactive, computed } from "vue";
|
|||
|
|
import { useLayout } from "@/components/BasicListLayout/hooks/useLayout";
|
|||
|
|
|
|||
|
|
// Placeholder types - replace with actual imports if found
|
|||
|
|
interface FilterOption {
|
|||
|
|
label: string;
|
|||
|
|
value: string | number | null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
interface FilterConfig {
|
|||
|
|
label: string;
|
|||
|
|
key: string;
|
|||
|
|
options: FilterOption[];
|
|||
|
|
}
|
|||
|
|
type Filters = FilterConfig[];
|
|||
|
|
|
|||
|
|
// Define the structure for a document item
|
|||
|
|
interface DocumentItem {
|
|||
|
|
id: string;
|
|||
|
|
title: string;
|
|||
|
|
sendingDept: string;
|
|||
|
|
receivingDept: string;
|
|||
|
|
publishDate: string;
|
|||
|
|
// Add other relevant fields as needed
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const mockTodoList = [
|
|||
|
|
{
|
|||
|
|
id: "1",
|
|||
|
|
title: "公文1",
|
|||
|
|
sendingDept: "办公室1",
|
|||
|
|
receivingDept: "教务处1",
|
|||
|
|
publishDate: "2024-01-01",
|
|||
|
|
},
|
|||
|
|
];
|
|||
|
|
const testList = async (param: any): Promise<Requests<any>> => {
|
|||
|
|
return new Promise((resolve) => {
|
|||
|
|
setTimeout(() => {
|
|||
|
|
resolve({ message: "测试", resultCode: 1, rows: mockTodoList });
|
|||
|
|
}, 1000);
|
|||
|
|
});
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const [register, { reload, setParam }] = useLayout({
|
|||
|
|
api: testList,
|
|||
|
|
componentProps: {},
|
|||
|
|
});
|
|||
|
|
// --- Event Handlers ---
|
|||
|
|
|
|||
|
|
const goToDetail = (id: string) => {
|
|||
|
|
console.log("Navigating to detail for ID:", id);
|
|||
|
|
uni.navigateTo({
|
|||
|
|
url: `/pages/view/routine/GongWenLiuZhuan/detail?id=${id}`,
|
|||
|
|
});
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// --- Filter Popup Methods ---
|
|||
|
|
const openFilterPopup = (type: "group" | "sendingDept" | "receivingDept") => {
|
|||
|
|
currentFilterType.value = type;
|
|||
|
|
filterPopupRef.value?.open();
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const closeFilterPopup = () => {
|
|||
|
|
filterPopupRef.value?.close();
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const selectFilterOption = (option: FilterOption) => {
|
|||
|
|
let paramsToSet: Record<string, any> = {};
|
|||
|
|
switch (currentFilterType.value) {
|
|||
|
|
case "group":
|
|||
|
|
selectedGroup.value = option;
|
|||
|
|
paramsToSet = { group: option.value };
|
|||
|
|
break;
|
|||
|
|
case "sendingDept":
|
|||
|
|
selectedSendingDept.value = option;
|
|||
|
|
paramsToSet = { sendingDept: option.value };
|
|||
|
|
break;
|
|||
|
|
case "receivingDept":
|
|||
|
|
selectedReceivingDept.value = option;
|
|||
|
|
paramsToSet = { receivingDept: option.value };
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
setParam(paramsToSet);
|
|||
|
|
reload(true); // Reload from page 1
|
|||
|
|
closeFilterPopup();
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// --- Filter State ---
|
|||
|
|
const groups = ref<FilterOption[]>([
|
|||
|
|
{ label: "全部分组", value: null },
|
|||
|
|
{ label: "分组A", value: "groupA" },
|
|||
|
|
{ label: "分组B", value: "groupB" },
|
|||
|
|
]);
|
|||
|
|
|
|||
|
|
const sendingDepts = ref<FilterOption[]>([
|
|||
|
|
{ label: "发文部门", value: null },
|
|||
|
|
{ label: "办公室1", value: "办公室1" },
|
|||
|
|
{ label: "办公室2", value: "办公室2" },
|
|||
|
|
{ label: "办公室3", value: "办公室3" },
|
|||
|
|
{ label: "办公室4", value: "办公室4" },
|
|||
|
|
{ label: "办公室5", value: "办公室5" },
|
|||
|
|
]);
|
|||
|
|
|
|||
|
|
const receivingDepts = ref<FilterOption[]>([
|
|||
|
|
{ label: "收文部门", value: null },
|
|||
|
|
{ label: "教务处1", value: "教务处1" },
|
|||
|
|
{ label: "教务处2", value: "教务处2" },
|
|||
|
|
{ label: "教务处3", value: "教务处3" },
|
|||
|
|
]);
|
|||
|
|
|
|||
|
|
// --- Filter State ---
|
|||
|
|
const selectedGroup = ref<FilterOption | null>(groups.value[0]);
|
|||
|
|
const selectedSendingDept = ref<FilterOption | null>(sendingDepts.value[0]);
|
|||
|
|
const selectedReceivingDept = ref<FilterOption | null>(receivingDepts.value[0]);
|
|||
|
|
const filterPopupRef = ref<any>(null);
|
|||
|
|
const currentFilterType = ref<"group" | "sendingDept" | "receivingDept" | null>(
|
|||
|
|
null
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
// --- Computed Properties for Popup ---
|
|||
|
|
const currentFilterOptions = computed(() => {
|
|||
|
|
switch (currentFilterType.value) {
|
|||
|
|
case "group":
|
|||
|
|
return groups.value;
|
|||
|
|
case "sendingDept":
|
|||
|
|
return sendingDepts.value;
|
|||
|
|
case "receivingDept":
|
|||
|
|
return receivingDepts.value;
|
|||
|
|
default:
|
|||
|
|
return [];
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
const currentFilterTitle = computed(() => {
|
|||
|
|
switch (currentFilterType.value) {
|
|||
|
|
case "group":
|
|||
|
|
return "选择分组";
|
|||
|
|
case "sendingDept":
|
|||
|
|
return "选择发文部门";
|
|||
|
|
case "receivingDept":
|
|||
|
|
return "选择收文部门";
|
|||
|
|
default:
|
|||
|
|
return "选择选项";
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
const isOptionSelected = (option: FilterOption) => {
|
|||
|
|
switch (currentFilterType.value) {
|
|||
|
|
case "group":
|
|||
|
|
return selectedGroup.value?.value === option.value;
|
|||
|
|
case "sendingDept":
|
|||
|
|
return selectedSendingDept.value?.value === option.value;
|
|||
|
|
case "receivingDept":
|
|||
|
|
return selectedReceivingDept.value?.value === option.value;
|
|||
|
|
default:
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
</script>
|
|||
|
|
|
|||
|
|
<style lang="scss" scoped>
|
|||
|
|
.document-item {
|
|||
|
|
// Add specific styles for document items if needed
|
|||
|
|
cursor: pointer; // Indicate items are clickable
|
|||
|
|
transition: background-color 0.2s ease;
|
|||
|
|
|
|||
|
|
&:hover {
|
|||
|
|
background-color: #f9f9f9; // Slight hover effect
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.item-title {
|
|||
|
|
// Style for the title
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.item-meta {
|
|||
|
|
// Style for the metadata line
|
|||
|
|
display: flex;
|
|||
|
|
justify-content: space-between;
|
|||
|
|
align-items: center;
|
|||
|
|
width: 100%;
|
|||
|
|
font-size: 24rpx;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.filter-section {
|
|||
|
|
display: flex;
|
|||
|
|
justify-content: space-around;
|
|||
|
|
align-items: center;
|
|||
|
|
padding: 20rpx 30rpx;
|
|||
|
|
background-color: #ffffff;
|
|||
|
|
border-bottom: 1rpx solid #e0e0e0;
|
|||
|
|
position: sticky;
|
|||
|
|
top: 0; // Adjust if necessary based on NavBar height
|
|||
|
|
z-index: 10;
|
|||
|
|
|
|||
|
|
.filter-item {
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
font-size: 28rpx;
|
|||
|
|
color: #333;
|
|||
|
|
padding: 10rpx;
|
|||
|
|
cursor: pointer;
|
|||
|
|
|
|||
|
|
text {
|
|||
|
|
margin-right: 8rpx;
|
|||
|
|
white-space: nowrap;
|
|||
|
|
overflow: hidden;
|
|||
|
|
text-overflow: ellipsis;
|
|||
|
|
max-width: 180rpx; // Adjust width as needed
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
uni-icons {
|
|||
|
|
color: #999;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.popup-content {
|
|||
|
|
background-color: #fff;
|
|||
|
|
border-top-left-radius: 20rpx;
|
|||
|
|
border-top-right-radius: 20rpx;
|
|||
|
|
padding: 20rpx;
|
|||
|
|
padding-bottom: 40rpx;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.popup-header {
|
|||
|
|
display: flex;
|
|||
|
|
justify-content: space-between;
|
|||
|
|
align-items: center;
|
|||
|
|
padding: 15rpx 10rpx;
|
|||
|
|
font-size: 32rpx;
|
|||
|
|
font-weight: bold;
|
|||
|
|
border-bottom: 1rpx solid #eee;
|
|||
|
|
margin-bottom: 10rpx;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.popup-options {
|
|||
|
|
max-height: 60vh;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.option-item {
|
|||
|
|
padding: 25rpx 20rpx;
|
|||
|
|
font-size: 28rpx;
|
|||
|
|
color: #333;
|
|||
|
|
border-bottom: 1rpx solid #f5f5f5;
|
|||
|
|
cursor: pointer;
|
|||
|
|
|
|||
|
|
&:last-child {
|
|||
|
|
border-bottom: none;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
&.active {
|
|||
|
|
color: #409eff;
|
|||
|
|
font-weight: bold;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
&:hover {
|
|||
|
|
background-color: #f9f9f9;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
</style>
|