| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416 |
- <!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"
- width="132px"
- inp="true"
- @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-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
- );
- // 功能说明:把后端 chg/chgRootFuncList 映射成卡片左滑操作按钮,标题取 desc,供 ss-card 左滑动作区使用 by xu 2026-03-06
- const enhancedList = formattedList.map((item, index) => {
- const rawItem = combinedObjectList[index] || {};
- const rawActions =
- Array.isArray(rawItem.chgRootFuncList) &&
- rawItem.chgRootFuncList.length > 0
- ? rawItem.chgRootFuncList
- : rawItem.chg
- ? [rawItem.chg]
- : [];
- return {
- ...item,
- ssObjId: item.ssObjId || rawItem.ssObjId || "",
- ssObjName: item.ssObjName || rawItem.ssObjName || "",
- swipeActions: rawActions.map((action) => ({
- ...action,
- title: action.desc || action.title || "操作",
- })),
- };
- });
- if (isLoadMore) {
- // 加载更多:追加到现有列表
- this.list = [...this.list, ...enhancedList];
- this.originalList = [...this.originalList, ...enhancedList];
- } else {
- // 首次加载或刷新:替换列表
- this.originalList = enhancedList;
- 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: "", v: "" },
- ...options,
- ];
- } catch (error) {
- console.error("获取筛选选项失败:", field.cbName, error);
- }
- }
- }
- console.log("🔽 生成筛选选项:", this.filterSelectOptions);
- },
- // 功能说明:统一处理卡片服务跳转,页面文件名按后端 dest 自动补 mp_,但传给业务页/后端的 dest 仍保持原始值 by xu 2026-03-06
- openServicePage(action, item = {}) {
- const target =
- action && typeof action === "object" ? action : null;
- if (!target) {
- this.showToast("当前记录缺少操作配置", "warning");
- return;
- }
- const serviceName = String(
- target.servName || target.ssServ || ""
- ).trim();
- const rawDest = String(target.dest || "").trim();
- const normalizedDest =
- rawDest && rawDest.startsWith("mp_")
- ? rawDest
- : rawDest
- ? `mp_${rawDest}`
- : "";
- const paramName = String(target.param_name || "").trim();
- const paramValue = target.param_value;
- const ssToken = String(target.ssToken || "").trim();
- const paramText =
- typeof target.parm === "string" ? target.parm : "";
- if (!serviceName) {
- this.showToast("缺少服务名", "warning");
- return;
- }
- if (!normalizedDest) {
- this.showToast("缺少目标页面", "warning");
- return;
- }
- NavigationManager.goTo(normalizedDest, {
- title: target.desc || target.title || "处理",
- service: serviceName,
- dest: rawDest,
- ssDest: rawDest,
- param: paramText,
- playParamName: paramName,
- playParamValue: paramValue,
- ssToken: ssToken,
- ssObjId: item.ssObjId || "",
- ssObjName: item.ssObjName || "",
- management: this.pageParams.management || "1",
- [paramName || "param_value"]: paramValue,
- });
- },
- // 卡片点击 - SsCard组件会自动传递item数据
- handleCardClick(item) {
- console.log("📄 卡片点击事件触发", item);
- // 功能说明:卡片点击统一按 play 参数跳转到通用查看页 mp_objplay(兼容 play / service.play 两种结构) by xu 2026-03-04
- const play =
- (item && (item.play || (item.service && item.service.play))) ||
- null;
- this.openServicePage(play, item);
- },
- // 卡片操作 - SsCard组件的按钮点击事件
- handleCardAction({ button, item, index }) {
- console.log("⚡ 卡片操作:", button, item);
- // 功能说明:左滑操作按钮按服务配置跳转(如 chg=变动),不再弹 Toast 占位 by xu 2026-03-06
- this.openServicePage(button, item);
- },
- // 加载更多数据
- 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>
|