| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261 |
- <!DOCTYPE html>
- <html lang="zh-CN">
- <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
- <title>列表</title>
- <!-- 引入基础依赖(统一由 base.js 动态注入其他依赖) -->
- <script src="/js/mp_base/base.js"></script>
- <style>
- #app {
- background: #f5f5f5;
- min-height: 100vh;
- }
- /* 防止Vue模板闪烁 */
- [v-cloak] {
- display: none !important;
- }
- /* 搜索筛选区域 */
- .search-filter-container {
- background: #f5f5f5;
- padding: 15px;
- position: sticky;
- top: 0;
- z-index: 100;
- display: flex;
- justify-content: flex-end;
- flex-wrap: wrap;
- gap: 10px;
- }
- .search-filter-container .ss-select-container{
- border: 1px solid #ccc;
- border-radius: 4px;
- padding: 0 10px;
- height: 34px;
- box-sizing: border-box;
- }
-
- /* 加载状态 */
- .loading-container {
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
- height: 200px;
- color: #666;
- }
- .loading-spinner {
- width: 40px;
- height: 40px;
- border: 4px solid #f3f3f3;
- border-top: 4px solid #40ac6d;
- border-radius: 50%;
- animation: spin 1s linear infinite;
- margin-bottom: 15px;
- }
- @keyframes spin {
- 0% { transform: rotate(0deg); }
- 100% { transform: rotate(360deg); }
- }
- .loading-text {
- font-size: 14px;
- }
- /* 列表容器 */
- .list-container {
- padding:0 15px;
- }
- /* 空状态 */
- .empty-state {
- text-align: center;
- padding: 60px 20px;
- color: #999;
- }
- .empty-icon {
- font-size: 48px;
- margin-bottom: 15px;
- }
- .empty-text {
- font-size: 16px;
- }
- /* 卡片内容样式 - 按照小程序list.vue转换 */
- .card-content .card-header {
- margin-bottom: 10px; /* 20rpx -> 10px */
- }
- .card-content .card-title {
- font-size: 16px; /* 32rpx -> 16px */
- font-weight: bold;
- color: #333;
- }
- .card-content .card-description {
- font-size: 14px; /* 28rpx -> 14px */
- color: #666;
- margin-bottom: 8px; /* 15rpx -> 8px */
- }
- .card-content .attribute-group {
- display: flex;
- flex-wrap: wrap;
- column-gap: 10px; /* 20rpx -> 10px */
- }
- .card-content .attribute-item {
- display: flex;
- margin-bottom: 5px; /* 10rpx -> 5px */
- }
- .card-content .attr-label {
- font-size: 13px; /* 26rpx -> 13px */
- color: #999;
- }
- .card-content .attr-value {
- font-size: 13px; /* 26rpx -> 13px */
- color: #333;
- flex: 1;
- }
- /* 状态文本样式 */
- .status-text {
- font-weight: bold;
- }
- /* 加载更多提示样式 */
- .load-more-container {
- text-align: center;
- padding: 20px;
- color: #999;
- font-size: 14px;
- }
- .load-more-loading {
- color: #007aff;
- }
- .load-more-end {
- color: #999;
- }
- .load-more-tip {
- color: #ccc;
- }
- /* 回到顶部按钮 */
- .back-to-top-btn {
- width: 50px;
- height: 50px;
- border-radius: 50%;
- background: rgba(87, 93, 109, 0.5);
- display: flex;
- justify-content: center;
- align-items: center;
- position: fixed;
- bottom: 200px;
- right: 15px;
- cursor: pointer;
- transition: all 0.3s ease;
- z-index: 999;
- }
- .back-to-top-btn:active {
- transform: scale(0.9);
- }
- .back-to-top-inner {
- width: 42px;
- height: 42px;
- border-radius: 50%;
- background: rgba(87, 93, 109, 0.5);
- display: flex;
- justify-content: center;
- align-items: center;
- }
- /* 搜索弹窗 */
- .search-modal-mask {
- position: fixed;
- top: 0;
- left: 0;
- right: 0;
- bottom: 0;
- background: rgba(0, 0, 0, 0.5);
- z-index: 1000;
- display: flex;
- align-items: flex-end;
- }
- .search-modal-content {
- width: 100%;
- background: white;
- animation: slideUp 0.3s ease-out;
- /* 让键盘弹起时自动上移 */
- position: relative;
- }
- @keyframes slideUp {
- from {
- transform: translateY(100%);
- }
- to {
- transform: translateY(0);
- }
- }
- .search-input-container {
- display: flex;
- align-items: center;
- height: 50px;
- background: white;
- border-top: 1px solid #e5e5e5;
- /* 适配iPhone底部安全区域 */
- /* padding-bottom: env(safe-area-inset-bottom); */
- }
- .search-input {
- flex: 1;
- height: 100%;
- border: none;
- padding: 0 15px;
- font-size: 16px;
- outline: none;
- background: transparent;
- }
- .search-divider {
- width: 1px;
- height: 30px;
- background: #d5d8dc;
- }
- .search-icon-btn {
- width: 60px;
- height: 100%;
- display: flex;
- align-items: center;
- justify-content: center;
- cursor: pointer;
- background: transparent;
- border: none;
- padding: 0;
- }
- .search-icon-btn:active {
- background: #f5f5f5;
- }
- </style>
- </head>
- <body>
- <div id="app" v-cloak>
- <!-- 搜索和筛选区域 -->
- <div class="search-filter-container">
- <!-- 动态下拉选择器 -->
- <template v-for="(options, fieldName) in filterSelectOptions" :key="fieldName">
- <ss-select
- v-model="selectedFilters[fieldName]"
- :placeholder="`选择${getFieldDesc(fieldName)}`"
- :options="options"
- @change="handleFilterChange"
- >
- </ss-select>
- </template>
- <!-- 完全由buttonList决定的动态按钮组 -->
- <ss-search-button
- v-for="(button, index) in buttonList"
- :key="index"
- :text="getButtonText(button)"
-
- @click="handleButtonClick(button)"
- >
- </ss-search-button>
- </div>
- <!-- 加载状态 -->
- <div v-if="loading" class="loading-container">
- <div class="loading-spinner"></div>
- <div class="loading-text">加载中...</div>
- </div>
- <!-- 列表区域 -->
- <div v-else class="list-container">
- <!-- 空状态 -->
- <div v-if="list.length === 0" class="empty-state">
- <div class="empty-icon">📋</div>
- <div class="empty-text">暂无数据</div>
- </div>
- <!-- 数据列表 -->
- <div v-else>
- <ss-card
- v-for="(item, index) in list"
- :key="index"
- :item="item"
- @click="handleCardClick(item)"
- @button-click="handleCardAction"
- >
- <!-- 卡片内容 - 按照小程序list.vue的结构,使用API数据 -->
- <div class="card-content">
- <!-- 主标题 (first) -->
- <div class="card-header" v-if="item.firstDisplay">
- <div class="card-title">{{ item.firstDisplay }}</div>
- </div>
- <!-- 描述 (second) -->
- <div class="card-description" v-if="item.secondDisplay">
- {{ item.secondDisplay }}
- </div>
- <!-- 属性列表 (third) -->
- <div class="card-attributes" v-if="item.thirdDisplay && item.thirdDisplay.length > 0">
- <div
- v-for="(group, groupIndex) in item.thirdDisplay"
- :key="groupIndex"
- class="attribute-group"
- >
- <div
- v-for="(attr, attrIndex) in group"
- :key="attrIndex"
- class="attribute-item"
- >
- <span class="attr-label">{{ attr.field.desc }}:</span>
- <span class="attr-value">{{ attr.displayValue }}</span>
- </div>
- </div>
- </div>
- </div>
- </ss-card>
- </div>
- </div>
- <!-- 加载更多提示 -->
- <div class="load-more-container" v-if="list.length > 0">
- <div v-if="isLoadingMore" class="load-more-loading">
- <span>正在加载更多...</span>
- </div>
- <div v-else-if="!hasMore" class="load-more-end">
- <span>没有更多数据了</span>
- </div>
- <div v-else class="load-more-tip">
- <span>滚动到底部加载更多</span>
- </div>
- </div>
- <!-- 回到顶部按钮 -->
- <div
- v-if="showBackToTop"
- class="back-to-top-btn"
- @touchstart="handleLongPressStart"
- @touchend="handleLongPressEnd"
- @touchcancel="handleLongPressCancel"
- @click.prevent="handleBackToTopClick"
- >
- <div class="back-to-top-inner">
- <Icon name="icon-huidaodingbu" size="40" color="#fff"></Icon>
- </div>
- </div>
- <!-- 搜索弹窗 -->
- <div v-if="showSearchModal" class="search-modal-mask" @click="closeSearchModal">
- <div class="search-modal-content" @click.stop>
- <div class="search-input-container">
- <input
- ref="searchInput"
- v-model="searchKeyword"
- type="search"
- inputmode="search"
- class="search-input"
- placeholder="请输入关键词"
- @keyup.enter="performSearch"
- autocomplete="off"
- />
- <div class="search-divider"></div>
- <button class="search-icon-btn" @click="performSearch">
- <Icon name="icon-chazhao" size="24" color="#575d6d"></Icon>
- </button>
- </div>
- </div>
- </div>
- </div>
- <script>
- // 等待SS框架加载完成
- window.SS.ready(function () {
- // 使用SS框架的方式创建Vue实例
- window.SS.dom.initializeFormApp({
- el: '#app',
- data() {
- return {
- // 加载状态
- loading: true,
- // 列表数据
- list: [],
- originalList: [], // 原始数据,用于筛选
- // 分页相关
- currentPage: 1,
- pageSize: 10,
- hasMore: true,
- isLoadingMore: false,
- // 页面参数
- pageParams: {},
- service: '', // init服务名称
- // 功能说明:对接PC两段式接口(init 返回 home/list 服务名) by xu 2026-02-28
- ssSearchPobjHomeServName: '',
- ssSearchPobjListServName: '',
- // API返回的动态配置
- buttonList: [],
- fieldsList: [],
- ssPaging: null,
- // 动态搜索筛选选项
- filterOptions: [],
- sortOptions: [],
- // 下拉选择器数据
- selectedFilters: {}, // 动态筛选条件
- filterSelectOptions: {}, // 各个筛选字段的选项
- // 字典缓存
- dictCache: new Map(),
- // 显示字段配置 - 根据service动态设置
- displayFields: [],
- // 卡片操作按钮
- cardActions: [
- { text: '查看', name: 'view' },
- { text: '编辑', name: 'edit' }
- ],
- // 回到顶部按钮
- showBackToTop: false,
- // 是否存在关键词搜索
- hasKeyWord:false,
- // 搜索相关
- showSearchModal: false,
- searchKeyword: '',
- // 长按相关
- longPressTimer: null,
- isLongPress: false,
- }
- },
- mounted() {
- // 页面加载时初始化
- this.initPage();
- // 监听页面刷新通知
- this.setupRefreshListener();
- // 监听滚动事件,实现滚动加载
- this.setupScrollListener();
- // 开发模式:加载Mock数据(测试用)
- // this.loadMockData();
- },
- beforeUnmount() {
- // 清理刷新监听器
- if (this.refreshCleanup) {
- this.refreshCleanup();
- }
- // 清理滚动监听器
- if (this.scrollCleanup) {
- this.scrollCleanup();
- }
- },
- methods: {
- // 加载Mock数据(用于开发测试)
- loadMockData() {
- console.log('🎭 加载Mock数据...');
- const mockData = [
- {
- firstDisplay: '张三 - 2024级计算机科学与技术1班',
- secondDisplay: '学号:2024001 | 手机:138****1234',
- thirdDisplay: [
- [
- { field: { desc: '性别' }, displayValue: '男' },
- { field: { desc: '年龄' }, displayValue: '20' },
- { field: { desc: '状态' }, displayValue: '在读' }
- ],
- [
- { field: { desc: '入学时间' }, displayValue: '2024-09-01' },
- { field: { desc: '辅导员' }, displayValue: '王老师' }
- ]
- ]
- },
- {
- firstDisplay: '李四 - 2024级软件工程2班',
- secondDisplay: '学号:2024002 | 手机:139****5678',
- thirdDisplay: [
- [
- { field: { desc: '性别' }, displayValue: '女' },
- { field: { desc: '年龄' }, displayValue: '19' },
- { field: { desc: '状态' }, displayValue: '在读' }
- ],
- [
- { field: { desc: '入学时间' }, displayValue: '2024-09-01' },
- { field: { desc: '辅导员' }, displayValue: '李老师' }
- ]
- ]
- },
- {
- firstDisplay: '王五 - 2023级人工智能1班',
- secondDisplay: '学号:2023003 | 手机:136****9012',
- thirdDisplay: [
- [
- { field: { desc: '性别' }, displayValue: '男' },
- { field: { desc: '年龄' }, displayValue: '21' },
- { field: { desc: '状态' }, displayValue: '在读' }
- ],
- [
- { field: { desc: '入学时间' }, displayValue: '2023-09-01' },
- { field: { desc: '辅导员' }, displayValue: '赵老师' }
- ]
- ]
- },
- {
- firstDisplay: '赵六 - 2024级数据科学与大数据技术1班',
- secondDisplay: '学号:2024004 | 手机:137****3456',
- thirdDisplay: [
- [
- { field: { desc: '性别' }, displayValue: '女' },
- { field: { desc: '年龄' }, displayValue: '20' },
- { field: { desc: '状态' }, displayValue: '在读' }
- ],
- [
- { field: { desc: '入学时间' }, displayValue: '2024-09-01' },
- { field: { desc: '辅导员' }, displayValue: '刘老师' }
- ]
- ]
- },
- {
- firstDisplay: '钱七 - 2023级网络工程1班',
- secondDisplay: '学号:2023005 | 手机:135****7890',
- thirdDisplay: [
- [
- { field: { desc: '性别' }, displayValue: '男' },
- { field: { desc: '年龄' }, displayValue: '21' },
- { field: { desc: '状态' }, displayValue: '休学' }
- ],
- [
- { field: { desc: '入学时间' }, displayValue: '2023-09-01' },
- { field: { desc: '辅导员' }, displayValue: '周老师' }
- ]
- ]
- },
- {
- firstDisplay: '孙八 - 2024级信息安全1班',
- secondDisplay: '学号:2024006 | 手机:133****2468',
- thirdDisplay: [
- [
- { field: { desc: '性别' }, displayValue: '女' },
- { field: { desc: '年龄' }, displayValue: '19' },
- { field: { desc: '状态' }, displayValue: '在读' }
- ],
- [
- { field: { desc: '入学时间' }, displayValue: '2024-09-01' },
- { field: { desc: '辅导员' }, displayValue: '吴老师' }
- ]
- ]
- },
- {
- firstDisplay: '周九 - 2023级物联网工程1班',
- secondDisplay: '学号:2023007 | 手机:188****1357',
- thirdDisplay: [
- [
- { field: { desc: '性别' }, displayValue: '男' },
- { field: { desc: '年龄' }, displayValue: '22' },
- { field: { desc: '状态' }, displayValue: '在读' }
- ],
- [
- { field: { desc: '入学时间' }, displayValue: '2023-09-01' },
- { field: { desc: '辅导员' }, displayValue: '郑老师' }
- ]
- ]
- },
- {
- firstDisplay: '吴十 - 2024级云计算1班',
- secondDisplay: '学号:2024008 | 手机:189****2468',
- thirdDisplay: [
- [
- { field: { desc: '性别' }, displayValue: '女' },
- { field: { desc: '年龄' }, displayValue: '20' },
- { field: { desc: '状态' }, displayValue: '在读' }
- ],
- [
- { field: { desc: '入学时间' }, displayValue: '2024-09-01' },
- { field: { desc: '辅导员' }, displayValue: '冯老师' }
- ]
- ]
- },
- {
- firstDisplay: '郑十一 - 2023级区块链工程1班',
- secondDisplay: '学号:2023009 | 手机:180****3691',
- thirdDisplay: [
- [
- { field: { desc: '性别' }, displayValue: '男' },
- { field: { desc: '年龄' }, displayValue: '21' },
- { field: { desc: '状态' }, displayValue: '在读' }
- ],
- [
- { field: { desc: '入学时间' }, displayValue: '2023-09-01' },
- { field: { desc: '辅导员' }, displayValue: '陈老师' }
- ]
- ]
- },
- {
- firstDisplay: '王十二 - 2024级电子商务1班',
- secondDisplay: '学号:2024010 | 手机:181****4802',
- thirdDisplay: [
- [
- { field: { desc: '性别' }, displayValue: '女' },
- { field: { desc: '年龄' }, displayValue: '19' },
- { field: { desc: '状态' }, displayValue: '在读' }
- ],
- [
- { field: { desc: '入学时间' }, displayValue: '2024-09-01' },
- { field: { desc: '辅导员' }, displayValue: '褚老师' }
- ]
- ]
- },
- {
- firstDisplay: '陈十三 - 2023级金融科技1班',
- secondDisplay: '学号:2023011 | 手机:182****5913',
- thirdDisplay: [
- [
- { field: { desc: '性别' }, displayValue: '男' },
- { field: { desc: '年龄' }, displayValue: '22' },
- { field: { desc: '状态' }, displayValue: '毕业' }
- ],
- [
- { field: { desc: '入学时间' }, displayValue: '2023-09-01' },
- { field: { desc: '辅导员' }, displayValue: '卫老师' }
- ]
- ]
- },
- {
- firstDisplay: '刘十四 - 2024级数字媒体技术1班',
- secondDisplay: '学号:2024012 | 手机:183****6024',
- thirdDisplay: [
- [
- { field: { desc: '性别' }, displayValue: '女' },
- { field: { desc: '年龄' }, displayValue: '20' },
- { field: { desc: '状态' }, displayValue: '在读' }
- ],
- [
- { field: { desc: '入学时间' }, displayValue: '2024-09-01' },
- { field: { desc: '辅导员' }, displayValue: '蒋老师' }
- ]
- ]
- }
- ];
- // 设置数据
- this.list = mockData;
- this.originalList = [...mockData];
- this.loading = false;
- this.hasMore = false;
- console.log('✅ Mock数据加载完成,共', mockData.length, '条');
- },
- // 初始化页面
- async initPage() {
- try {
- console.log('🔄 初始化列表页面...');
- // 获取URL参数
- this.pageParams = this.getUrlParams();
- this.service = this.pageParams.service || 'default';
- console.log('📋 页面参数:', this.pageParams);
- // 功能说明:先调 init,再按 init 返回的 home/list 服务继续拉取数据 by xu 2026-02-28
- await this.loadInitAndHomeData();
- } catch (error) {
- console.log('❌ 页面初始化失败:', error);
- // this.showToast('页面初始化失败', 'error');
- } finally {
- this.loading = false;
- }
- },
- // 功能说明:调用 init 接口并解析 ssSearchPobjHomeServName/ssSearchPobjListServName by xu 2026-02-28
- async loadInitAndHomeData() {
- const initService = (this.service || '').trim();
- if (!initService) {
- await this.loadData(1, false);
- return;
- }
- const initParams = {
- pageNo: 1,
- rowNumPer: this.pageSize,
- management: '1',
- isReady: '1'
- };
- const initResult = await request.post(
- `/service?ssServ=${initService}&management=1&isReady=1`,
- initParams,
- {
- loading: false,
- formData: true
- }
- );
- const initPayload = this.unwrapResponseData(initResult?.data);
- this.ssSearchPobjHomeServName = String(initPayload?.ssSearchPobjHomeServName || '').trim();
- this.ssSearchPobjListServName = String(initPayload?.ssSearchPobjListServName || '').trim();
- console.log('✅ init返回服务名:', {
- initService,
- ssSearchPobjHomeServName: this.ssSearchPobjHomeServName,
- ssSearchPobjListServName: this.ssSearchPobjListServName
- });
- const homeService = this.ssSearchPobjHomeServName || initService;
- await this.loadDataByService(homeService, 1, false);
- },
- // 功能说明:统一处理 /service 返回结构(兼容 {ssData} 与平铺结构) by xu 2026-02-28
- unwrapResponseData(data) {
- if (!data || typeof data !== 'object') {
- return {};
- }
- if (data.ssData && typeof data.ssData === 'object') {
- return data.ssData;
- }
- return data;
- },
- // 功能说明:封装按指定 ssServ 拉取列表数据(home/list 共用) by xu 2026-02-28
- async loadDataByService(ssServ, pageNo = 1, isLoadMore = false) {
- const serviceName = String(ssServ || '').trim();
- if (!serviceName) {
- console.warn('⚠️ 缺少服务名,跳过请求');
- return;
- }
- // 防止重复加载:只有在非首次加载且正在加载时才阻止
- if (this.loading && !isLoadMore && this.list.length > 0) return;
- if (this.isLoadingMore && isLoadMore) return;
- try {
- console.log(`🔄 加载列表数据... 服务: ${serviceName}, 页码: ${pageNo}, 加载更多: ${isLoadMore}`);
- if (isLoadMore) {
- this.isLoadingMore = true;
- } else {
- this.loading = true;
- }
- const requestParams = {
- pageNo: pageNo,
- rowNumPer: this.pageSize,
- management: '1',
- isReady: '1',
- // 功能说明:请求参数只保留有效筛选项(避免空值/旧值污染查询) by xu 2026-02-28
- ...this.getActiveFilterParams(this.selectedFilters)
- };
- const result = await request.post(
- `/service?ssServ=${serviceName}&management=1&isReady=1`,
- requestParams,
- {
- loading: false,
- formData: true
- }
- );
- console.log('✅ API响应数据:', result);
- if (result && result.data) {
- await this.processApiData(result.data, isLoadMore);
- } else {
- console.warn('⚠️ API返回数据格式异常:', result);
- }
- } catch (error) {
- console.error('❌ 数据加载失败:', error);
- } finally {
- if (isLoadMore) {
- this.isLoadingMore = false;
- } else {
- this.loading = false;
- }
- }
- },
- // 加载数据
- async loadData(pageNo = 1, isLoadMore = false) {
- // 功能说明:翻页/筛选/搜索统一走 list 服务;未返回时回退 home/init by xu 2026-02-28
- const listService = this.ssSearchPobjListServName || this.ssSearchPobjHomeServName || this.service;
- await this.loadDataByService(listService, pageNo, isLoadMore);
- },
- // 处理API返回的数据
- async processApiData(data, isLoadMore = false) {
- try {
- const payload = this.unwrapResponseData(data);
- const objectList = Array.isArray(payload.objectList)
- ? payload.objectList
- : Array.isArray(payload.objList)
- ? payload.objList
- : [];
- const draftList = Array.isArray(payload.draftList) ? payload.draftList : [];
- const combinedObjectList = draftList.concat(objectList);
- console.log('🔄 处理API数据...', {
- isLoadMore,
- objectListLength: combinedObjectList.length,
- fromObjList: Array.isArray(payload.objList)
- });
- // 保存API返回的配置信息
- if (!isLoadMore) {
- // 功能说明:列表接口通常不返回按钮/搜索字段,缺省时保留首屏(home)已加载配置,避免筛选后按钮消失 by xu 2026-02-28
- if (Array.isArray(payload.buttonList) || Array.isArray(payload.rootFuncList)) {
- this.buttonList = payload.buttonList || payload.rootFuncList || [];
- }
- if (Array.isArray(payload.fieldsList) || Array.isArray(payload.searchFieldList)) {
- this.fieldsList = payload.fieldsList || payload.searchFieldList || [];
- }
- }
- // ssPaging信息每次都要更新,因为包含当前页信息
- this.ssPaging = payload.ssPaging || this.ssPaging || null;
- // 功能说明:list接口未返回 hasKeyword 时沿用首屏值,避免长按搜索入口被错误隐藏 by xu 2026-02-28
- if (Object.prototype.hasOwnProperty.call(payload, 'hasKeyWord') || Object.prototype.hasOwnProperty.call(payload, 'hasKeyword')) {
- this.hasKeyWord = payload.hasKeyWord || payload.hasKeyword || false;
- }
- // 处理objectList数据
- if (combinedObjectList.length > 0) {
- // 使用field-formatter.js格式化列表数据
- const formattedList = await window.formatObjectList(combinedObjectList, this.dictCache);
- if (isLoadMore) {
- // 加载更多:追加到现有列表
- this.list = [...this.list, ...formattedList];
- this.originalList = [...this.originalList, ...formattedList];
- } else {
- // 首次加载或刷新:替换列表
- this.originalList = formattedList;
- this.list = [...this.originalList];
- }
- console.log('✅ 列表数据处理完成:', this.list.length, '条');
- // 更新分页状态
- this.currentPage = isLoadMore ? this.currentPage + 1 : 1;
- // 根据ssPaging信息判断是否还有更多数据
- if (this.ssPaging && this.ssPaging.rowNum !== undefined) {
- const totalRecords = this.ssPaging.rowNum;
- const currentRecords = this.list.length;
- this.hasMore = currentRecords < totalRecords;
- console.log('📊 分页信息:', {
- totalRecords,
- currentRecords,
- hasMore: this.hasMore,
- currentPage: this.currentPage
- });
- } else {
- // 降级处理:根据当前页数据量判断
- this.hasMore = formattedList.length >= this.pageSize;
- console.log('⚠️ 使用降级分页判断:', {
- returnedCount: formattedList.length,
- pageSize: this.pageSize,
- hasMore: this.hasMore
- });
- }
- } else {
- // 没有更多数据
- this.hasMore = false;
- console.log('❌ 没有返回数据,设置hasMore为false');
- }
- // 根据fieldsList生成筛选选项(只在首次加载时生成)
- if (!isLoadMore) {
- await this.generateFilterOptions();
- }
- } catch (error) {
- console.error('❌ 数据处理失败:', error);
- throw error;
- }
- },
- // 获取URL参数
- getUrlParams() {
- const params = {};
- const urlSearchParams = new URLSearchParams(window.location.search);
- for (const [key, value] of urlSearchParams) {
- params[key] = decodeURIComponent(value);
- }
- return params;
- },
- // 处理buttonList按钮点击
- handleButtonClick(button) {
- console.log('🔘 按钮点击:', button);
- // 直接跳转到目标页面
- const destPage = button.function?.dest || button.dest;
- NavigationManager.goToFromButton(button);
- },
- // 跳转到目标页面
- navigateToPage(button, destPage) {
- const urlParams = new URLSearchParams(window.location.search);
- // 添加按钮相关参数
- if (button.function) {
- urlParams.set('dest', destPage);
- urlParams.set('title', encodeURIComponent(button.function.desc || button.buttonName));
- urlParams.set('service', button.function.servName || button.service || '');
- } else {
- urlParams.set('dest', destPage);
- urlParams.set('title', encodeURIComponent(button.buttonName));
- urlParams.set('service', button.service || '');
- }
- const newUrl = `${destPage}.html?${urlParams.toString()}`;
- console.log('� 跳转到:', newUrl);
- window.location.href = newUrl;
- },
- // 根据fieldsList生成筛选选项
- async generateFilterOptions() {
- if (!this.fieldsList || this.fieldsList.length === 0) {
- return;
- }
- for (const field of this.fieldsList) {
- // 如果字段有cbName,生成下拉选项
- if (field.cbName) {
- try {
- const options = await window.getDictOptions(field.cbName, this.dictCache);
- this.filterSelectOptions[field.name] = [
- { n: `全部${field.desc}`, v: '' },
- ...options
- ];
- } catch (error) {
- console.error('获取筛选选项失败:', field.cbName, error);
- }
- }
- }
- console.log('🔽 生成筛选选项:', this.filterSelectOptions);
- },
- // 卡片点击 - SsCard组件会自动传递item数据
- handleCardClick(item) {
- console.log('📄 卡片点击事件触发',item);
-
- },
- // 卡片操作 - SsCard组件的按钮点击事件
- handleCardAction({ button, item, index }) {
- console.log('⚡ 卡片操作:', button, item);
- if (button && button.onclick) {
- // 执行按钮的onclick回调
- button.onclick();
- }
- this.showToast(`执行操作: ${button.title}`, 'info');
- },
- // 加载更多数据
- async loadMore() {
- if (!this.hasMore || this.isLoadingMore) {
- console.log('🚫 无法加载更多:', { hasMore: this.hasMore, isLoadingMore: this.isLoadingMore });
- return;
- }
- console.log('📄 加载更多数据...');
- await this.loadData(this.currentPage + 1, true);
- },
- // 刷新数据
- async refreshData() {
- console.log('🔄 刷新数据...');
- // 重置分页状态
- this.currentPage = 1;
- this.hasMore = true;
- this.list = [];
- this.originalList = [];
- try {
- await this.loadData(1, false);
- this.showToast('刷新成功', 'success');
- } catch (error) {
- this.showToast('刷新失败', 'error');
- }
- },
- // 获取字段描述
- getFieldDesc(fieldName) {
- const field = this.fieldsList.find(f => f.name === fieldName);
- return field ? field.desc : fieldName;
- },
- // 功能说明:统一按钮文案映射(兼容 rootFuncList/buttonList 不同字段) by xu 2026-02-28
- getButtonText(button) {
- if (!button || typeof button !== 'object') return '';
- return button.desc || button.title || button.buttonName || button.name || '';
- },
- // 功能说明:提取有效筛选参数(过滤空串/null/undefined) by xu 2026-02-28
- getActiveFilterParams(source) {
- const params = {};
- const obj = (source && typeof source === 'object') ? source : {};
- Object.entries(obj).forEach(([key, value]) => {
- if (value === '' || value === null || value === undefined) return;
- params[key] = value;
- });
- return params;
- },
- // 筛选选择器变化(立即搜索)
- handleFilterChange(value) {
- console.log('🔽 筛选条件变化,立即搜索:', value);
- // 立即执行搜索
- this.applyDynamicFilters();
- },
- // 应用动态筛选(重新调用API)
- async applyDynamicFilters() {
- try {
- console.log('🔍 应用筛选条件:', this.selectedFilters);
- // 功能说明:筛选参数先归一化,清理已取消的筛选条件 by xu 2026-02-28
- const filterParams = this.getActiveFilterParams(this.selectedFilters);
- // 功能说明:筛选后重置分页并走 list 服务 by xu 2026-02-28
- this.currentPage = 1;
- this.hasMore = true;
- this.list = [];
- this.originalList = [];
- this.selectedFilters = { ...filterParams };
- await this.loadData(1, false);
- } catch (error) {
- console.error('❌ 筛选失败:', error);
- this.showToast('筛选失败', 'error');
- }
- },
- // 生成项目按钮
- generateItemButtons(service) {
- if (!service || !service.play) return [];
- return [{
- title: service.play.name || '查看',
- icon: 'icon-chakan',
- onclick: () => {
- console.log('点击查看按钮:', service.play);
- // 这里可以跳转到详情页面
- this.showToast(`查看: ${service.play.title}`, 'info');
- }
- }];
- },
- // 设置页面刷新监听
- setupRefreshListener() {
- // 监听子页面返回时的刷新通知
- this.refreshCleanup = NavigationManager.onRefreshNotify((refreshData) => {
- console.log('📢 收到刷新通知,重新加载数据');
- this.refreshData();
- });
- },
- // 设置滚动监听
- setupScrollListener() {
- let isThrottled = false;
- const handleScroll = () => {
- if (isThrottled) return;
- isThrottled = true;
- setTimeout(() => {
- isThrottled = false;
- }, 200); // 节流200ms
- // 检查是否滚动到底部
- const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
- const windowHeight = window.innerHeight;
- const documentHeight = document.documentElement.scrollHeight;
- // 控制回到顶部按钮显示:滚动超过300px时显示
- this.showBackToTop = scrollTop > 300;
- // 距离底部50px时触发加载更多
- if (scrollTop + windowHeight >= documentHeight - 50) {
- console.log('📄 滚动到底部,尝试加载更多...');
- this.loadMore();
- }
- };
- // 添加滚动监听
- window.addEventListener('scroll', handleScroll);
- // 保存清理函数
- this.scrollCleanup = () => {
- window.removeEventListener('scroll', handleScroll);
- };
- },
- // 返回顶部
- scrollToTop() {
- window.scrollTo({
- top: 0,
- behavior: 'smooth'
- });
- },
- // 处理回到顶部按钮点击
- handleBackToTopClick() {
- // 如果不是长按触发的搜索,则执行返回顶部
- if (!this.isLongPress) {
- this.scrollToTop();
- }
- // 重置长按标志
- this.isLongPress = false;
- },
- // 长按开始
- handleLongPressStart(event) {
- if(this.hasKeyWord){ // 有输入关键字才显示,否则不处理长按
- this.isLongPress = false;
- // 设置长按定时器(500ms)
- this.longPressTimer = setTimeout(() => {
- this.isLongPress = true;
- this.openSearchModal();
- // 震动反馈(如果支持)
- if (navigator.vibrate) {
- navigator.vibrate(50);
- }
- }, 500);
- }
- },
- // 长按结束
- handleLongPressEnd(event) {
- // 清除长按定时器
- if (this.longPressTimer) {
- clearTimeout(this.longPressTimer);
- this.longPressTimer = null;
- }
- },
- // 长按取消(手指移出按钮区域)
- handleLongPressCancel(event) {
- // 清除长按定时器
- if (this.longPressTimer) {
- clearTimeout(this.longPressTimer);
- this.longPressTimer = null;
- }
- this.isLongPress = false;
- },
- // 打开搜索弹窗
- openSearchModal() {
- this.showSearchModal = true;
- this.searchKeyword = '';
- // 延迟聚焦输入框,确保DOM已渲染和动画完成
- this.$nextTick(() => {
- // 使用 setTimeout 确保在移动端也能正常触发键盘
- setTimeout(() => {
- if (this.$refs.searchInput) {
- // 先点击再聚焦,确保移动端键盘弹出
- this.$refs.searchInput.click();
- this.$refs.searchInput.focus();
- console.log('✅ 输入框已聚焦');
- }
- }, 100); // 等待动画完成后再聚焦
- });
- },
- // 关闭搜索弹窗
- closeSearchModal() {
- this.showSearchModal = false;
- this.searchKeyword = '';
- },
- // 执行搜索
- async performSearch() {
- const keyword = this.searchKeyword.trim();
- // 无关键词时,直接关闭弹窗,什么都不做
- if (!keyword) {
- // this.closeSearchModal();
- return;
- }
- console.log('🔍 执行关键词搜索:', keyword);
- // 关闭搜索弹窗
- this.closeSearchModal();
- // 设置 keyword 到筛选条件中
- this.selectedFilters.ssKeyword = keyword;
- // 调用 API 搜索
- await this.applyDynamicFilters();
- },
- // 显示提示
- showToast(message, type = 'info') {
- console.log(`${type.toUpperCase()}: ${message}`);
- // 使用浏览器原生alert,后续可以替换为更好的提示组件
- alert(message);
- }
- }
- })
- })
- </script>
- </body>
- </html>
|