Files
intc-vue-h5/src/pages/banknote/index.vue
2026-02-23 22:29:36 +08:00

782 lines
19 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="container">
<!-- 查询条件区域 -->
<u-sticky offsetTop="0" customNavHeight="0">
<view class="search-view">
<view class="search-input-wrapper">
<u--input
v-model="queryParams.issueYear"
border="surround"
readonly
placeholder="请选择发行年份"
placeholderStyle="color: #909399"
color="#333333"
customStyle="background: rgba(102, 126, 234, 0.08); border: 2rpx solid rgba(102, 126, 234, 0.3); border-radius: 24rpx; height: 66rpx"
class="search-input"
@click="showYearPicker = true">
</u--input>
<uni-icons
type="calendar"
size="18"
color="#667eea"
class="search-icon"
@click="showYearPicker = true">
</uni-icons>
</view>
<view class="reset-btn" @click="handleResetAll()">
<uni-icons type="reload" size="18" color="#667eea"></uni-icons>
<text>重置</text>
</view>
<view class="filter-btn" @click="filterPanel = !filterPanel">
<uni-icons type="list" size="18" color="#667eea"></uni-icons>
<text>筛选</text>
</view>
</view>
</u-sticky>
<!-- 筛选面板 -->
<u-transition :show="filterPanel" mode="slide-down">
<view class="filter-panel">
<view class="filter-panel-content">
<view class="filter-title">材质选择</view>
<view class="state-list">
<view
v-for="item in compositionOptions"
:key="item.dictValue"
class="state-item"
:class="queryParams.composition === item.dictValue ? 'active' : ''"
@click="selectComposition(item)">
{{ item.dictLabel }}
</view>
</view>
</view>
<view class="btn-box">
<view class="btn-reset" @click="handleReset()">
<uni-icons type="reload" size="16" color="#909399"></uni-icons>
<text>重置</text>
</view>
<view class="btn-confirm" @click="searchSubmit()">
<uni-icons type="checkmarkempty" size="16" color="#ffffff"></uni-icons>
<text>确定</text>
</view>
</view>
</view>
</u-transition>
<!-- 数据列表 -->
<u-list @scrolltolower="loadmore" lowerThreshold="100">
<u-list-item v-for="(item, index) in dataList" :key="index">
<view class="list-item" >
<view class="item-header">
<view class="card-name-section">
<view class="card-icon">
<uni-icons type="star" size="20" color="#ffffff"></uni-icons>
</view>
<view class="card-info">
<text class="card-name">{{ item.fullName }}</text>
</view>
</view>
</view>
<view class="card-body">
<view class="info-row">
<view class="info-item">
<text class="info-label">名称</text>
<text class="info-value">{{ item.shortName || '--' }}</text>
</view>
<view class="info-item">
<text class="info-label">发行时间</text>
<text class="info-value">{{ item.issueDate || '--' }}</text>
</view>
<view class="info-item">
<text class="info-label">面值</text>
<text class="info-value">{{ item.faceValue }}</text>
</view>
<view class="info-item">
<text class="info-label">发行量</text>
<text class="info-value">{{ item.mintage }}</text>
</view>
<view class="info-item">
<text class="info-label">材质</text>
<text class="info-value">{{ getCompositionLabel(item.composition) }}</text>
</view>
<view class="info-item">
<text class="info-label">发行单位</text>
<text class="info-value">{{ item.coinageUnit || '--' }}</text>
</view>
<view class="info-item info-item-full" >
<text class="info-label">发行公告</text>
<text class="info-value">{{ item.remark || '--' }}</text>
</view>
<!-- 图片展示区域 -->
<view class="info-item info-item-full" v-if="item.collectFileList && item.collectFileList.length > 0">
<view class="image-list">
<view
v-for="(file, idx) in item.collectFileList.slice(0, 4)"
:key="idx"
class="image-wrapper"
@click.stop="handlePreviewImages(item.collectFileList, idx)">
<image
:src="getFullImageUrl(file.url)"
mode="aspectFit"
class="item-image"
/>
</view>
<view v-if="item.collectFileList.length > 4" class="image-more" @click.stop="handlePreviewImages(item.collectFileList, 0)">
<text>+{{ item.collectFileList.length - 4 }}</text>
</view>
</view>
</view>
</view>
</view>
</view>
</u-list-item>
<view></view>
<u-loadmore :status="status" loadingIcon="semicircle" height="88" fontSize="32rpx" @loadmore="loadmore" />
</u-list>
<!-- 年份选择器 -->
<u-picker
:show="showYearPicker"
:columns="yearColumns"
@confirm="confirmYear"
@cancel="showYearPicker = false"
@close="showYearPicker = false"
></u-picker>
</view>
</template>
<script setup>
import { ref, computed, onMounted } from 'vue'
import { listCommemorativeBanknote } from '@/api/collect/CommemorativeBanknote'
import request from '@/utils/request'
import config from '@/config'
import dayjs from 'dayjs'
// 查询参数
const queryParams = ref({
pageNum: 1,
pageSize: 10,
issueYear: '',
composition: ''
})
// 数据列表
const dataList = ref([])
const total = ref(0)
const status = ref('loadmore')
const showYearPicker = ref(false)
const filterPanel = ref(false)
// 生成年份选项最近100年
const yearColumns = ref([
Array.from({ length: 100 }, (_, i) => {
const year = new Date().getFullYear() - i
return { text: `${year}`, value: year }
})
])
// 系列选项(从字典获取)
const seriesOptions = ref([])
// 材质选项
const compositionOptions = ref([])
// 形状选项
const shapeOptions = ref([])
// 币齿选项
const coinTeethOptions = ref([])
// 计算窗口高度
const windowHeight = computed(() => {
return uni.getSystemInfoSync().windowHeight - 50
})
// 获取系列标签
const getSeriesLabel = (seriesValue) => {
const found = seriesOptions.value.find(item => item.dictValue === seriesValue)
return found ? found.dictLabel : '未知系列'
}
// 获取材质标签
const getCompositionLabel = (compositionValue) => {
const found = compositionOptions.value.find(item => item.dictValue === compositionValue)
return found ? found.dictLabel : '--'
}
// 获取形状标签
const getShapeLabel = (shapeValue) => {
const found = shapeOptions.value.find(item => item.dictValue === shapeValue)
return found ? found.dictLabel : '--'
}
// 获取币齿标签
const getCoinTeethLabel = (coinTeethValue) => {
const found = coinTeethOptions.value.find(item => item.dictValue === coinTeethValue)
return found ? found.dictLabel : '--'
}
// 获取完整图片URL
const getFullImageUrl = (url) => {
if (!url) return ''
// 如果是完整URL直接返回
if (url.startsWith('http://') || url.startsWith('https://')) {
return url
}
// 拼接基础URL
return 'https://www.qdintc.com' + url
}
// 选择材质
const selectComposition = (item) => {
queryParams.value.composition = item.dictValue
}
// 加载字典数据无需token
const loadDictData = async () => {
try {
// 加载材质字典
const compositionRes = await request({
url: '/system/dict/data/type/commemorative_banknote_composition',
method: 'get',
headers: { isToken: false }
})
compositionOptions.value = compositionRes.data || []
} catch (error) {
console.error('加载字典失败:', error)
}
}
// 查询数据列表
const getList = async () => {
status.value = 'loading'
try {
const response = await listCommemorativeBanknote({
pageSize: 10,
pageNum: queryParams.value.pageNum,
...queryParams.value
})
dataList.value = dataList.value.concat(response.rows || [])
total.value = response.total || 0
if (dataList.value.length < total.value) {
status.value = 'loadmore'
} else {
status.value = 'nomore'
}
} catch (error) {
uni.showToast({
title: '查询失败',
icon: 'none'
})
status.value = 'nomore'
}
}
// 确定查询
const searchSubmit = () => {
queryParams.value.pageNum = 1
dataList.value = []
getList()
filterPanel.value = false
}
// 重置按钮
const handleReset = () => {
queryParams.value = {
pageNum: 1,
pageSize: 10,
issueYear: '',
composition: ''
}
dataList.value = []
total.value = 0
}
// 重置所有查询条件
const handleResetAll = () => {
queryParams.value = {
pageNum: 1,
pageSize: 10,
issueYear: '',
composition: ''
}
dataList.value = []
total.value = 0
// 重置后自动查询
getList()
}
// 确认年份
const confirmYear = (e) => {
// e.value 是数组,包含选中的值
const selectedValue = e.value[0]
// 如果是对象格式 { text: '2024年', value: 2024 },取 value 属性
const year = typeof selectedValue === 'object' ? selectedValue.value : selectedValue
queryParams.value.issueYear = String(year)
showYearPicker.value = false
// 选择年份后自动查询
queryParams.value.pageNum = 1
dataList.value = []
getList()
}
// 加载更多
const loadmore = () => {
if (status.value === 'loadmore') {
queryParams.value.pageNum++
getList()
}
}
// 查看详情
const handleViewDetail = (item) => {
uni.showToast({
title: `查看${item.fullName}`,
icon: 'none'
})
}
// 预览图片
const handlePreviewImages = (fileList, current = 0) => {
if (!fileList || fileList.length === 0) return
// 拼接完整URL
const urls = fileList.map(file => getFullImageUrl(file.url))
uni.previewImage({
current: current,
urls: urls
})
}
// 页面加载时查询
onMounted(() => {
loadDictData()
getList()
})
</script>
<style lang="scss" scoped>
page {
height: 100%;
background: #f5f7fa;
}
.container {
min-height: 100vh;
background: #f5f7fa;
}
// 查询区域
.search-view {
padding: 12rpx 32rpx;
background-color: #ffffff;
display: flex;
justify-content: space-between;
align-items: center;
position: relative;
z-index: 100;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05);
gap: 10rpx;
.search-input-wrapper {
flex: 1;
display: flex;
align-items: center;
position: relative;
.search-icon {
position: absolute;
right: 20rpx;
z-index: 10;
cursor: pointer;
}
}
.search-input {
background: rgba(102, 126, 234, 0.08);
color: #333333;
width: 100%;
margin-right: 0;
border-radius: 24rpx;
border: 2rpx solid rgba(102, 126, 234, 0.3);
height: 66rpx !important;
display: flex;
align-items: center;
:deep(input::placeholder) {
color: #909399 !important;
font-size: 14px !important;
}
:deep(.u-input__content__field-wrapper__field) {
color: #333333 !important;
}
:deep(input) {
color: #333333 !important;
}
}
.reset-btn {
display: flex;
align-items: center;
gap: 6rpx;
padding: 12rpx 24rpx;
background: rgba(102, 126, 234, 0.08);
border-radius: 24rpx;
border: 2rpx solid rgba(102, 126, 234, 0.3);
transition: all 0.3s ease;
flex-shrink: 0;
&:active {
transform: scale(0.95);
background: rgba(102, 126, 234, 0.12);
}
text {
color: #667eea;
font-size: 28rpx;
font-weight: 600;
}
}
.filter-btn {
display: flex;
align-items: center;
gap: 6rpx;
padding: 12rpx 24rpx;
background: rgba(102, 126, 234, 0.08);
border-radius: 24rpx;
border: 2rpx solid rgba(102, 126, 234, 0.3);
transition: all 0.3s ease;
flex-shrink: 0;
&:active {
transform: scale(0.95);
background: rgba(102, 126, 234, 0.12);
}
text {
color: #667eea;
font-size: 28rpx;
font-weight: 600;
}
}
}
.filter-panel {
width: 100%;
position: fixed;
left: 0;
top: 90rpx;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5);
z-index: 10000;
display: flex;
flex-direction: column;
justify-content: flex-start;
.filter-panel-content {
background-color: #ffffff;
padding: 0 30rpx 30rpx;
border-radius: 0 0 16rpx 16rpx;
.filter-title {
color: #2c3e50;
font-size: 32rpx;
font-weight: 600;
padding: 32rpx 0 24rpx 20rpx;
position: relative;
display: flex;
align-items: center;
&::before {
content: '';
width: 6rpx;
height: 32rpx;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border-radius: 3rpx;
margin-right: 12rpx;
flex-shrink: 0;
}
}
.state-list {
display: flex;
flex-wrap: wrap;
justify-content: flex-start;
gap: 16rpx;
margin-bottom: 24rpx;
.state-item {
padding: 0 32rpx;
height: 68rpx;
border: 2rpx solid #e8edf3;
border-radius: 34rpx;
text-align: center;
line-height: 68rpx;
font-size: 28rpx;
color: #666666;
transition: all 0.3s ease;
background: #ffffff;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.04);
&:active {
transform: scale(0.95);
}
}
.active {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border: 2rpx solid transparent;
color: #ffffff;
box-shadow: 0 4rpx 12rpx rgba(102, 126, 234, 0.3);
font-weight: 600;
}
}
}
.btn-box {
display: flex;
gap: 20rpx;
padding: 24rpx 30rpx;
background-color: #fff;
box-shadow: 0rpx -10rpx 20rpx #EEEEEE;
.btn-reset,
.btn-confirm {
display: flex;
align-items: center;
justify-content: center;
gap: 8rpx;
height: 88rpx;
border-radius: 12rpx;
font-size: 30rpx;
font-weight: 600;
transition: all 0.3s ease;
&:active {
transform: scale(0.98);
opacity: 0.9;
}
text {
line-height: 1;
}
}
.btn-reset {
flex: 1;
background: #f5f7fa;
border: 2rpx solid #dcdfe6;
text {
color: #606266;
}
}
.btn-confirm {
flex: 1;
background: #667eea;
box-shadow: 0 2rpx 8rpx rgba(102, 126, 234, 0.2);
border: none;
text {
color: #ffffff;
}
}
}
}
// 列表项
.list-item {
margin: 10rpx 24rpx;
background-color: #fff;
border-radius: 16rpx;
overflow: hidden;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.08);
transition: all 0.2s ease;
&:active {
transform: scale(0.98);
box-shadow: 0 1rpx 6rpx rgba(0, 0, 0, 0.06);
}
.item-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 16rpx 24rpx;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
.card-name-section {
display: flex;
align-items: center;
flex: 1;
min-width: 0;
}
.card-icon {
width: 40rpx;
height: 40rpx;
background: rgba(255, 255, 255, 0.25);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin-right: 12rpx;
flex-shrink: 0;
}
.card-info {
flex: 1;
min-width: 0;
display: flex;
flex-direction: row;
align-items: center;
gap: 12rpx;
.card-name {
color: #ffffff;
font-size: 30rpx;
font-weight: 700;
line-height: 1.5;
word-wrap: break-word;
word-break: break-all;
flex: 1;
letter-spacing: 0.5rpx;
}
.card-code {
color: rgba(255, 255, 255, 0.9);
font-size: 24rpx;
font-weight: 500;
line-height: 1.3;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
flex: 1;
min-width: 0;
letter-spacing: 0.3rpx;
}
}
}
.card-body {
padding: 24rpx;
background: #fafbfc;
border: 2rpx solid #f0f2f5;
margin: 15rpx 16rpx 16rpx;
border-radius: 12rpx;
}
.info-row {
display: flex;
flex-wrap: wrap;
margin: 0 -12rpx;
.info-item {
width: 50%;
padding: 0 12rpx;
box-sizing: border-box;
display: flex;
flex-direction: column;
margin-bottom: 20rpx;
&:last-child,
&:nth-last-child(2) {
margin-bottom: 0;
}
.info-label {
font-size: 24rpx;
color: #667eea;
font-weight: 500;
background: rgba(102, 126, 234, 0.08);
padding: 6rpx 12rpx;
border-radius: 8rpx;
align-self: flex-start;
margin-bottom: 8rpx;
}
.info-value {
font-size: 26rpx;
color: #333;
font-weight: 500;
flex: 1;
line-height: 1.5;
word-wrap: break-word;
word-break: break-all;
}
}
// 图片占据整行
.info-item-full {
width: 100%;
.image-list {
display: flex;
flex-direction: column;
align-items: center;
gap: 12rpx;
margin-top: 8rpx;
.image-wrapper {
width: 100%;
height: 400rpx;
border-radius: 12rpx;
background: #ffffff;
overflow: hidden;
transition: all 0.3s ease;
display: flex;
align-items: center;
justify-content: center;
&:active {
transform: scale(0.98);
opacity: 0.8;
}
}
.item-image {
width: 100%;
height: 100%;
display: block;
}
.image-more {
width: 100%;
height: 180rpx;
border-radius: 12rpx;
background: rgba(102, 126, 234, 0.1);
border: 2rpx dashed rgba(102, 126, 234, 0.4);
display: flex;
align-items: center;
justify-content: center;
transition: all 0.3s ease;
&:active {
transform: scale(0.98);
background: rgba(102, 126, 234, 0.15);
}
text {
font-size: 40rpx;
color: #667eea;
font-weight: 600;
}
}
}
}
}
}
</style>