Files
tuanwei-uniapp/pages/mine/declare.vue
2026-07-20 11:56:02 +08:00

356 lines
8.3 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.

<template>
<view class="page">
<scroll-view scroll-y class="page-scroll">
<view class="hero-card">
<view class="hero-title">我的申报</view>
<view class="hero-desc">汇总本地草稿已提交记录和当前开放的申报项目</view>
<view class="hero-stats">
<view class="stat-item">
<view class="stat-value">{{ localRecords.length }}</view>
<view class="stat-label">本地记录</view>
</view>
<view class="stat-item">
<view class="stat-value">{{ submittedCount }}</view>
<view class="stat-label">已提交</view>
</view>
<view class="stat-item">
<view class="stat-value">{{ openDeclareList.length }}</view>
<view class="stat-label">开放项目</view>
</view>
</view>
</view>
<view class="section-card">
<view class="section-head">
<view class="section-title">本地申报记录</view>
<view class="section-action" @tap="loadData()">刷新</view>
</view>
<view v-if="!localRecords.length" class="empty-block">
当前还没有本地草稿或提交记录可先前往申报页填写
</view>
<view v-else class="card-list">
<view v-for="item in localRecords" :key="item.key" class="data-card">
<view class="card-top">
<view class="card-main">
<view class="card-title">{{ item.title }}</view>
<view class="card-subtitle">{{ item.year || '未设置年度' }} · {{ item.group }}</view>
</view>
<view class="status-tag" :class="item.status === '已提交' ? 'status-tag--success' : 'status-tag--warning'">
{{ item.status }}
</view>
</view>
<view class="meta-row">保存时间{{ item.savedAt || '-' }}</view>
<view class="card-actions">
<view class="card-action" @tap="continueEdit(item)">继续编辑</view>
<view class="card-action" @tap="copyText(item.title)">复制标题</view>
</view>
</view>
</view>
</view>
<view class="section-card">
<view class="section-head">
<view class="section-title">当前开放项目</view>
<view class="section-action" @tap="goDeclareList">全部查看</view>
</view>
<view v-if="loading" class="empty-block">正在加载开放项目...</view>
<view v-else-if="errorText" class="empty-block empty-block--error">{{ errorText }}</view>
<view v-else-if="!openDeclareList.length" class="empty-block">当前暂无开放的申报项目</view>
<view v-else class="card-list">
<view v-for="item in openDeclareList" :key="item.id || `${item.module}-${item.year}`" class="data-card">
<view class="card-top">
<view class="card-main">
<view class="card-title">{{ getDeclareTitle(item) }}</view>
<view class="card-subtitle">{{ getDeclareGroup(item.module) }} · {{ item.year || '未设置年度' }}</view>
</view>
<view class="status-tag status-tag--default">可申报</view>
</view>
<view class="meta-row">时间范围{{ formatDateTime(item.startTime) }} {{ formatDateTime(item.endTime) }}</view>
<view class="card-actions">
<view class="card-action" @tap="goApply(item)">进入申报</view>
</view>
</view>
</view>
</view>
</scroll-view>
</view>
</template>
<script>
import {
formatDateTime,
getLocalDeclareRecords,
listAvailableDeclare
} from './service'
import { getFormConfig, getModuleMeta } from '../../utils/gxmu/config'
export default {
data() {
return {
loading: false,
errorText: '',
localRecords: [],
openDeclareList: []
}
},
computed: {
submittedCount() {
return this.localRecords.filter((item) => item.status === '已提交').length
}
},
onShow() {
this.loadData()
},
onPullDownRefresh() {
this.loadData(true)
},
methods: {
formatDateTime,
getDeclareTitle(item) {
return String(item.title || '').trim() || this.getDeclareGroup(item.module)
},
getDeclareGroup(module) {
const meta = getModuleMeta(module)
return (meta && meta.title) || module || '申报项目'
},
async loadData(fromPullDown = false) {
this.localRecords = getLocalDeclareRecords()
this.loading = true
this.errorText = ''
try {
const result = await listAvailableDeclare({ usable: true })
const list = Array.isArray(result) ? result : []
this.openDeclareList = list.sort((left, right) => Number(right.year || 0) - Number(left.year || 0))
} catch (error) {
this.errorText = (error && error.message) || '开放项目加载失败'
} finally {
this.loading = false
if (fromPullDown) {
uni.stopPullDownRefresh()
}
}
},
continueEdit(item) {
uni.navigateTo({
url: item.route
})
},
copyText(value) {
uni.setClipboardData({
data: String(value || ''),
success: () => {
uni.showToast({
title: '已复制',
icon: 'none'
})
}
})
},
goDeclareList() {
uni.navigateTo({
url: '/pages/gxmu/index'
})
},
goApply(item) {
if (!item.module || !getFormConfig(item.module)) {
uni.showToast({
title: '当前申报项目在小程序端暂未配置',
icon: 'none'
})
return
}
const moduleMeta = getModuleMeta(item.module)
const title = String(item.title || '').trim() || ((moduleMeta && moduleMeta.title) || '申报表')
uni.navigateTo({
url: `/pages/gxmu/form?code=${encodeURIComponent(item.module)}&year=${encodeURIComponent(item.year || '')}&declareTitle=${encodeURIComponent(title)}`
})
}
}
}
</script>
<style lang="scss" scoped>
.page {
min-height: 100vh;
background:
radial-gradient(circle at top center, rgba(20, 150, 242, 0.1), transparent 30%),
linear-gradient(180deg, #f5fbff 0%, #eef7ff 100%);
}
.page-scroll {
height: 100vh;
padding: 24rpx 24rpx 40rpx;
}
.hero-card,
.section-card,
.data-card {
border-radius: 30rpx;
background: rgba(255, 255, 255, 0.94);
border: 1rpx solid rgba(20, 150, 242, 0.08);
box-shadow: 0 10rpx 28rpx rgba(34, 94, 142, 0.08);
}
.hero-card {
padding: 32rpx 28rpx;
background: linear-gradient(145deg, #0d6fba 0%, #1496f2 58%, #5bc2ff 100%);
color: #fff;
}
.hero-title {
font-size: 40rpx;
font-weight: 700;
}
.hero-desc {
margin-top: 14rpx;
font-size: 24rpx;
line-height: 1.7;
color: rgba(255, 255, 255, 0.86);
}
.hero-stats {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 16rpx;
margin-top: 24rpx;
}
.stat-item {
padding: 18rpx 16rpx;
border-radius: 22rpx;
background: rgba(255, 255, 255, 0.12);
text-align: center;
}
.stat-value {
font-size: 32rpx;
font-weight: 700;
}
.stat-label {
margin-top: 6rpx;
font-size: 22rpx;
color: rgba(255, 255, 255, 0.8);
}
.section-card {
margin-top: 24rpx;
padding: 26rpx;
}
.section-head {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16rpx;
}
.section-title {
font-size: 30rpx;
font-weight: 700;
color: #1f2329;
}
.section-action {
font-size: 22rpx;
color: #1496f2;
font-weight: 700;
}
.empty-block {
margin-top: 18rpx;
padding: 26rpx 22rpx;
border-radius: 24rpx;
background: #faf5f2;
font-size: 24rpx;
line-height: 1.75;
color: #7b726d;
}
.empty-block--error {
color: #0f7dd1;
}
.card-list {
display: flex;
flex-direction: column;
gap: 18rpx;
margin-top: 18rpx;
}
.data-card {
padding: 24rpx;
}
.card-top {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 16rpx;
}
.card-main {
flex: 1;
min-width: 0;
}
.card-title {
font-size: 30rpx;
font-weight: 700;
color: #22272f;
}
.card-subtitle {
margin-top: 8rpx;
font-size: 22rpx;
color: #8b817c;
}
.meta-row {
margin-top: 18rpx;
padding: 18rpx;
border-radius: 22rpx;
background: #faf7f2;
font-size: 24rpx;
line-height: 1.75;
color: #5f564f;
}
.status-tag {
padding: 10rpx 16rpx;
border-radius: 999rpx;
font-size: 20rpx;
font-weight: 700;
}
.status-tag--success {
background: #eef9f1;
color: #1f8f49;
}
.status-tag--warning {
background: #fff8e6;
color: #ad7b00;
}
.status-tag--default {
background: #f5f5f5;
color: #8b817c;
}
.card-actions {
display: flex;
gap: 14rpx;
margin-top: 18rpx;
}
.card-action {
padding: 10rpx 18rpx;
border-radius: 999rpx;
background: rgba(20, 150, 242, 0.08);
color: #1496f2;
font-size: 22rpx;
}
</style>