2025-07-23 22:32:01 +08:00

558 lines
12 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!-- 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>