xinli/xinlidsj/pages/report/index.vue
2026-02-24 16:49:05 +08:00

234 lines
8.5 KiB
Vue

<template>
<view class="page" :class="{ big: isH5 }">
<view class="panel">
<view class="filters">
<view class="chip" :class="{ active: query.sourceType === '' }" @click="setSource('')">全部</view>
<view class="chip" :class="{ active: query.sourceType === 'assessment' }" @click="setSource('assessment')">测评</view>
<view class="chip" :class="{ active: query.sourceType === 'questionnaire' }" @click="setSource('questionnaire')">问卷</view>
</view>
<view v-if="errorMsg" class="error">{{ errorMsg }}</view>
</view>
<view v-if="loading" class="state">
<uni-icons type="spinner-cycle" size="28" color="#94A3B8"></uni-icons>
<view class="state-text">加载中...</view>
</view>
<view v-else class="list">
<view v-if="rows.length === 0" class="state">
<uni-icons type="file-text" size="34" color="#CBD5E1"></uni-icons>
<view class="state-text">暂无数据</view>
</view>
<view v-else>
<view class="item" v-for="row in rows" :key="row.reportId" @click="openDetail(row)">
<view class="row">
<view class="badge" :class="row.sourceType === 'questionnaire' ? 'warn' : 'info'">
{{ row.sourceType === 'questionnaire' ? '问卷' : '测评' }}
</view>
<view class="item-title">{{ row.reportTitle || ('报告#' + row.reportId) }}</view>
</view>
<view class="item-desc">类型:{{ row.reportType || '-' }} · 信息编号:{{ row.infoNumber || '-' }}</view>
<view class="item-foot">生成:{{ formatTime(row.generateTime) }} · 创建:{{ formatTime(row.createTime) }}</view>
</view>
<view v-if="hasMore" class="load-more">上拉加载更多…</view>
<view v-else class="load-more">没有更多了</view>
</view>
</view>
</view>
</template>
<script>
import { request } from '../../utils/request'
import { listReport } from '../../api/psychology/report'
import UniIcons from '@/uni_modules/uni-icons/components/uni-icons/uni-icons.vue'
export default {
components: {
UniIcons
},
data() {
return {
isH5: false,
loading: false,
errorMsg: '',
rows: [],
total: 0,
query: { pageNum: 1, pageSize: 10, sourceType: '' },
keyword: '',
userId: null
}
},
onLoad(options) {
try {
const info = uni.getSystemInfoSync()
const p = info ? (info.uniPlatform || info.platform) : ''
this.isH5 = p === 'web' || p === 'h5'
} catch (e) {
this.isH5 = false
}
this.keyword = (options && options.keyword) ? String(options.keyword) : ''
this.userId = null
this.refresh()
},
onPullDownRefresh() {
this.refresh().finally(() => uni.stopPullDownRefresh())
},
onReachBottom() {
if (this.loading || !this.hasMore) return
this.query.pageNum += 1
this.fetchList(true)
},
computed: {
hasMore() {
return this.rows.length < (this.total || 0)
}
},
methods: {
setSource(sourceType) {
if (this.loading) return
this.query.sourceType = sourceType
this.refresh()
},
refresh() {
this.query.pageNum = 1
this.rows = []
this.total = 0
return this.fetchList(false)
},
fetchList(append) {
this.errorMsg = ''
this.loading = true
const params = { pageNum: this.query.pageNum, pageSize: this.query.pageSize }
if (this.query.sourceType) params.sourceType = this.query.sourceType
const resolveUserId = () => {
if (this.userId) return Promise.resolve(this.userId)
const kw = (this.keyword || '').trim()
if (!kw) return Promise.resolve(null)
if (/^\d{1,18}$/.test(kw)) {
return request({
url: '/app/profile/options?keyword=' + encodeURIComponent(kw) + '&limit=1',
method: 'GET'
}).then((r) => {
const data = r && r.data ? r.data : null
if (!data || data.code !== 200) return null
const list = data.data || []
if (!list.length) return null
return list[0].userId || null
}).catch(() => null)
}
return request({
url: '/psychology/assessment/student/options?keyword=' + encodeURIComponent(kw) + '&limit=1',
method: 'GET'
}).then((r) => {
const data = r && r.data ? r.data : null
if (!data || data.code !== 200) return null
const list = data.data || []
if (!list.length) return null
return list[0].userId || null
}).catch(() => null)
}
return resolveUserId().then((uid) => {
if (uid) {
this.userId = uid
params.userId = uid
}
return listReport(params)
})
.then((res) => {
this.loading = false
const data = res && res.data ? res.data : null
if (!data || data.code !== 200) {
this.errorMsg = (data && data.msg) ? data.msg : '加载失败'
return
}
const list = data.rows || []
this.total = data.total || 0
this.rows = append ? this.rows.concat(list) : list
})
.catch((e) => {
this.loading = false
this.errorMsg = e && e.message ? e.message : '网络错误'
})
},
openDetail(row) {
if (!row || !row.reportId) return
const url = `/pages/report/detail?reportId=${encodeURIComponent(row.reportId)}&sourceType=${encodeURIComponent(row.sourceType || '')}`
uni.navigateTo({ url })
},
formatTime(val) {
if (!val) return '—'
try {
const d = new Date(val)
if (isNaN(d.getTime())) return '—'
const y = d.getFullYear()
const m = String(d.getMonth() + 1).padStart(2, '0')
const day = String(d.getDate()).padStart(2, '0')
const hh = String(d.getHours()).padStart(2, '0')
const mm = String(d.getMinutes()).padStart(2, '0')
return `${y}-${m}-${day} ${hh}:${mm}`
} catch (e) {
return '—'
}
}
}
}
</script>
<style>
.page {
min-height: 100vh;
padding: 24rpx 24rpx 120rpx;
box-sizing: border-box;
background: #F4F6FB;
}
.page.big {
padding: 14rpx 14rpx 120rpx;
background-image:
radial-gradient(1100rpx 520rpx at 50% 14%, rgba(43, 107, 255, 0.30) 0%, rgba(6, 16, 40, 0.0) 65%),
linear-gradient(180deg, rgba(5, 11, 24, 0.90) 0%, rgba(8, 20, 45, 0.85) 42%, rgba(6, 16, 40, 0.92) 100%),
url('/static/bg.png');
background-size: auto, auto, cover;
background-position: center, center, center;
background-repeat: no-repeat, no-repeat, no-repeat;
}
.panel {
background: rgba(255, 255, 255, 0.95);
border-radius: 18rpx;
padding: 24rpx;
border: 1px solid rgba(15, 23, 42, 0.06);
box-shadow: 0 10rpx 22rpx rgba(15, 23, 42, 0.05);
margin-bottom: 18rpx;
}
.page.big .panel,
.page.big .item,
.page.big .state {
border: 1px solid rgba(116, 216, 255, 0.22);
background: linear-gradient(180deg, rgba(10, 18, 38, 0.75) 0%, rgba(5, 10, 22, 0.55) 100%);
box-shadow: 0 12rpx 24rpx rgba(0, 0, 0, 0.35);
}
.filters { display: flex; flex-wrap: wrap; }
.chip { padding: 14rpx 22rpx; border-radius: 999rpx; font-size: 26rpx; color: #64748B; background: rgba(15, 23, 42, 0.04); margin-right: 16rpx; margin-bottom: 16rpx; min-height: 60rpx; display: flex; align-items: center; justify-content: center; }
.chip.active { color: #1677ff; background: rgba(22,119,255,0.12); }
.page.big .chip { color: rgba(201, 242, 255, 0.70); background: rgba(10, 18, 38, 0.65); border: 1px solid rgba(116, 216, 255, 0.20); }
.page.big .chip.active { color: #0b1226; background: linear-gradient(90deg, rgba(116, 216, 255, 0.95) 0%, rgba(43, 107, 255, 0.90) 100%); border-color: rgba(116, 216, 255, 0.5); }
.list { margin-top: 8rpx; }
.item { background: rgba(255, 255, 255, 0.95); border-radius: 18rpx; padding: 18rpx; border: 1px solid rgba(15, 23, 42, 0.06); margin-bottom: 14rpx; }
.row { display: flex; align-items: center; }
.badge { font-size: 22rpx; padding: 6rpx 14rpx; border-radius: 999rpx; margin-right: 14rpx; color: #fff; background: #8f959e; }
.badge.warn { background: #faad14; }
.badge.info { background: #1677ff; }
.item-title { font-size: 28rpx; font-weight: 700; color: #111827; flex: 1; }
.item-desc { margin-top: 10rpx; font-size: 24rpx; color: #64748B; }
.item-foot { margin-top: 10rpx; font-size: 22rpx; color: #94A3B8; }
.state { height: 360rpx; border-radius: 20rpx; background: rgba(255, 255, 255, 0.85); border: 1px solid rgba(15, 23, 42, 0.06); display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 12rpx; }
.state-text { font-size: 24rpx; color: #94A3B8; font-weight: 700; }
.load-more { text-align: center; color: #94A3B8; font-size: 22rpx; padding: 20rpx 0 6rpx; }
.error { margin-top: 14rpx; font-size: 24rpx; color: #ff4d4f; line-height: 36rpx; }
.page.big .item-title { color: rgba(235, 248, 255, 0.92); }
.page.big .item-desc,
.page.big .item-foot,
.page.big .state-text,
.page.big .load-more { color: rgba(201, 242, 255, 0.65); }
</style>