558 lines
12 KiB
Vue
Raw Normal View History

2025-07-23 22:32:01 +08:00
<!-- src/pages/view/routine/qd/index.vue -->
<template>
<view class="qd-list-page">
<!-- 查询组件 -->
<view class="query-component">
<view class="search-card">
<view class="search-item">
<picker
mode="date"
:value="searchForm.startTime"
@change="onStartTimeChange"
class="date-picker"
>
<view class="picker-text">{{ searchForm.startTime || '开始时间' }}</view>
</picker>
<text class="date-separator">~</text>
<picker
mode="date"
:value="searchForm.endTime"
@change="onEndTimeChange"
class="date-picker"
>
<view class="picker-text">{{ searchForm.endTime || '结束时间' }}</view>
</picker>
</view>
<view class="search-actions">
<u-button
text="查询"
type="primary"
size="small"
@click="handleSearch"
class="search-btn"
/>
<u-button
text="重置"
type="info"
size="small"
@click="handleReset"
class="reset-btn"
/>
</view>
</view>
</view>
<!-- 列表组件 -->
<view class="list-component">
<scroll-view scroll-y class="list-scroll-view">
<view v-if="isLoading" class="loading-indicator">加载中...</view>
<template v-else-if="dataList.length > 0">
<view v-for="data in dataList" :key="data.id" class="qd-card">
<view class="card-header">
<text class="qd-title">{{ data.qdmc }}</text>
<text class="qd-status" :class="getStatusClass(data.qdStatus)">
{{ getStatusText(data.qdStatus) }}
</text>
</view>
<view class="card-body">
<view class="qd-info">
<view class="info-item">
<text class="info-label">签到地点</text>
<text class="info-value">{{ data.qdwz || '未设置' }}</text>
</view>
<view class="info-item">
<text class="info-label">开始时间</text>
<text class="info-value">{{ formatTime(data.qdkstime) }}</text>
</view>
<view class="info-item">
<text class="info-label">结束时间</text>
<text class="info-value">{{ formatTime(data.qdjstime) }}</text>
</view>
<view class="info-item">
<text class="info-label">发布者</text>
<text class="info-value">{{ data.jsxm || '未知' }}</text>
</view>
<view class="info-item">
<text class="info-label">发布时间</text>
<text class="info-value">{{ formatTime(data.qdFbtime) }}</text>
</view>
</view>
</view>
<view class="card-footer">
<view class="footer-actions">
<image
src="/static/base/qr-code.png"
class="footer-action-icon qr-icon"
@click="goToQRCode(data.id)"
/>
<image
src="/static/base/details.png"
class="footer-action-icon details-icon"
@click="goToFeedback(data.id)"
/>
<image
v-if="data.qdStatus === 'A'"
src="/static/base/push.png"
class="footer-action-icon push-icon"
@click="goToPush(data.id)"
/>
</view>
</view>
</view>
</template>
<view v-else class="empty-state">暂无签到数据</view>
</scroll-view>
</view>
<!-- 新增按钮 - 固定在底部 -->
<view class="add-button-fixed">
<u-button
text="新增签到"
type="primary"
@click="goToPublish"
class="add-btn"
/>
</view>
</view>
</template>
<script lang="ts" setup>
import { qdFindPageApi } from "@/api/base/server";
import { ref, reactive, onMounted } from "vue";
import { onShow } from "@dcloudio/uni-app";
interface QdItem {
id: string;
qdmc: string; // 签到名称
qdwz: string; // 签到地点
qdStatus: string; // 发布状态A待推送B暂存C已推送
jsId: string; // 发布人ID
jsxm: string; // 发布人姓名
qdFbtime: string; // 发布时间
qdkstime: string; // 签到开始时间
qdjstime: string; // 签到结束时间
qdry: string; // 签到人员
}
// 搜索表单
const searchForm = reactive({
startTime: '',
endTime: ''
});
// 数据列表
const dataList = ref<QdItem[]>([]);
const isLoading = ref(false);
// 开始时间选择
const onStartTimeChange = (e: any) => {
searchForm.startTime = e.detail.value;
};
// 结束时间选择
const onEndTimeChange = (e: any) => {
searchForm.endTime = e.detail.value;
};
// 处理搜索
const handleSearch = () => {
getQdList();
};
// 重置搜索
const handleReset = () => {
searchForm.startTime = '';
searchForm.endTime = '';
getQdList();
};
// 获取签到列表
const getQdList = async () => {
isLoading.value = true;
try {
const res = await qdFindPageApi({
qdkstime: searchForm.startTime,
qdjstime: searchForm.endTime,
page: 1,
rows: 100
});
dataList.value = res.rows || [];
} catch (error) {
console.error('获取签到列表失败:', error);
dataList.value = [];
} finally {
isLoading.value = false;
}
};
// 获取状态样式类
const getStatusClass = (status: string) => {
switch (status) {
case 'A':
return 'status-pending';
case 'B':
return 'status-draft';
case 'C':
return 'status-pushed';
default:
return 'status-default';
}
};
// 获取状态文本
const getStatusText = (status: string) => {
switch (status) {
case 'A':
return '待推送';
case 'B':
return '暂存';
case 'C':
return '已推送';
default:
return '未知';
}
};
// 格式化时间
const formatTime = (time: string) => {
if (!time) return '未设置';
return new Date(time).toLocaleString('zh-CN', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit'
});
};
// 跳转到签到详情
const goToFeedback = (id: string) => {
uni.navigateTo({
url: `/pages/view/routine/qd/detail?id=${id}`
});
};
// 跳转到推送页面
const goToPush = (id: string) => {
uni.navigateTo({
url: `/pages/view/routine/qd/push-list?qdId=${id}`
});
};
// 跳转到新增页面
const goToPublish = () => {
uni.navigateTo({
url: '/pages/view/routine/qd/publish'
});
};
// 跳转到二维码页面
const goToQRCode = (id: string) => {
uni.navigateTo({
url: `/pages/view/routine/qd/qr-code?qdId=${id}`
});
};
// 页面显示时刷新数据
onShow(() => {
getQdList();
});
// 页面加载时也调用一次
onMounted(() => {
getQdList();
});
</script>
<style lang="scss" scoped>
.qd-list-page {
display: flex;
flex-direction: column;
height: 100vh;
background-color: #f5f7fa;
}
// 查询组件
.query-component {
padding: 12px;
background-color: #fff;
border-bottom: 1px solid #eee;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.04);
flex-shrink: 0;
}
.search-card {
background-color: #ffffff;
border-radius: 12px;
padding: 16px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
border: 1px solid #f0f0f0;
}
.search-item {
display: flex;
align-items: center;
margin-bottom: 12px;
gap: 8px;
.date-picker {
flex: 1;
min-width: 120px;
}
.picker-text {
background-color: #f8f9fa;
border: 1px solid #e9ecef;
border-radius: 8px;
padding: 8px 12px;
font-size: 14px;
color: #495057;
text-align: center;
}
.date-separator {
font-size: 16px;
color: #6c757d;
margin: 0 8px;
font-weight: bold;
}
}
.search-actions {
display: flex;
gap: 12px;
justify-content: center;
.search-btn, .reset-btn {
flex: 1;
max-width: 120px;
}
}
// 列表组件
.list-component {
flex: 1;
overflow-y: auto;
padding: 12px;
padding-bottom: 80px; // 为底部按钮留出空间
box-sizing: border-box;
background-color: #f5f7fa;
}
.list-scroll-view {
height: 100%;
}
.loading-indicator,
.empty-state {
text-align: center;
color: #999;
padding: 30px 15px;
font-size: 14px;
}
.qd-card {
background-color: #ffffff;
border-radius: 12px;
padding: 16px;
margin-bottom: 12px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
border: 1px solid #f0f0f0;
transition: all 0.3s ease;
position: relative;
overflow: hidden;
&:active {
transform: translateY(1px);
box-shadow: 0 1px 6px rgba(0, 0, 0, 0.12);
}
&::before {
content: '';
position: absolute;
left: 0;
top: 0;
bottom: 0;
width: 4px;
background: linear-gradient(135deg, #007aff 0%, #0056cc 100%);
border-radius: 2px;
}
}
.card-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 12px;
gap: 12px;
.qd-title {
font-size: 16px;
font-weight: 600;
color: #2c3e50;
flex: 1;
line-height: 1.4;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
word-break: break-word;
}
.qd-status {
padding: 4px 8px;
border-radius: 12px;
font-size: 12px;
font-weight: 500;
white-space: nowrap;
flex-shrink: 0;
}
}
.status-pending {
background: #fff3cd;
color: #856404;
}
.status-draft {
background: #d1ecf1;
color: #0c5460;
}
.status-pushed {
background: #d4edda;
color: #155724;
}
.status-default {
background: #f8f9fa;
color: #6c757d;
}
.card-body {
margin-bottom: 12px;
.qd-info {
display: flex;
flex-direction: column;
gap: 8px;
}
.info-item {
display: flex;
align-items: center;
.info-label {
font-size: 14px;
color: #666;
min-width: 80px;
}
.info-value {
font-size: 14px;
color: #333;
flex: 1;
}
}
}
.card-footer {
display: flex;
justify-content: flex-end;
align-items: center;
padding-top: 12px;
border-top: 1px solid #f0f0f0;
.footer-actions {
display: flex;
gap: 10px;
.footer-action-icon {
padding: 5px;
}
.qr-icon {
width: 22px;
height: 22px;
cursor: pointer;
}
.details-icon {
width: 22px;
height: 22px;
cursor: pointer;
}
.push-icon {
width: 22px;
height: 22px;
cursor: pointer;
}
}
}
// 新增按钮 - 固定在底部
.add-button-fixed {
position: fixed;
bottom: 0;
left: 0;
right: 0;
padding: 15px;
background-color: #fff;
box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.1);
z-index: 10;
border-top: 1px solid #eee;
.add-btn {
width: 100%;
background: linear-gradient(135deg, #007aff 0%, #0056cc 100%);
border-radius: 12px;
padding: 12px 24px;
box-shadow: 0 4px 16px rgba(0, 122, 255, 0.3);
font-weight: 600;
font-size: 16px;
&:active {
transform: translateY(1px);
}
}
}
// 响应式优化
@media (max-width: 375px) {
.query-component {
padding: 8px;
}
.qd-card {
padding: 12px;
margin-bottom: 8px;
}
.card-header .qd-title {
font-size: 15px;
}
.card-footer .footer-item {
max-width: 150px;
font-size: 12px;
}
}
// 加载动画
.qd-card {
animation: fadeInUp 0.3s ease-out;
}
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
</style>