| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222 |
- <%@ taglib prefix="ss" uri="/ssTag" %>
- <%@ page language="java" pageEncoding="UTF-8" isELIgnored="false" %>
- <%@ taglib uri="/ssTag" prefix="ss" %>
- <%pageContext.setAttribute("wdpageinformation", "{'hastab':'0'}");%>
- <!DOCTYPE html>
- <html>
- <head>
- <meta http-equiv="pragma" content="no-cache">
- <meta http-equiv="cache-control" content="no-cache">
- <meta http-equiv="expires" content="0">
- <script>window.loginStatus = "${empty sessionScope['ssUser']?'0':'1'}"</script>
- <ss:skin file='main.css'/>
- <script type="text/javascript" src="/ss/jquery/jquery.js"></script>
- <script type="text/javascript" src="/ss/artdialog/artDialogUtil.js"></script>
- <script type="text/javascript" src="/ss/js/base.js"></script>
- <script> if (!window.wd) var wd = {};
- if (!wd.display) wd.display = {};
- wd.display.wdDialogId = "objList";
- if (!wd.app) wd.app = {};
- wd.app.name = 'pms';</script>
- <script type="text/javascript" src="/ss/js/masklayer.js"></script>
- <script type="text/javascript" src="/ss/js/wdDialogInit.js"></script>
- <script type="text/javascript" src="/ss/js/common.js"></script>
- <script type="text/javascript" src="/ss/js/display.js"></script>
- <script type="text/javascript" src="/ss/js/edit.js"></script>
- <script type="text/javascript" src="/ss/nicescroll/jquery.nicescroll.js"></script>
- <script type="text/javascript" src="/ss/nicescroll/jquery.nicescroll.iframehelper.min.js"></script>
- <link rel="stylesheet" type="text/css" href="/ss/window/theme/dhtmlxwindows.css">
- <link rel="stylesheet" type="text/css" href="/ss/window/theme/dhx_blue/dhtmlxwindows_dhx_blue.css">
- <script type="text/javascript" src="/ss/window/dhtmlxcommon.js"></script>
- <script type="text/javascript" src="/ss/window/dhtmlxwindows.js"></script>
- <script type="text/javascript" src="/ss/window/dhtmlxcontainer.js"></script>
- <script type="text/javascript" src="/ss/js/common.js"></script>
- <script type="text/javascript" src="/ss/js/display.js"></script>
- <script type="text/javascript" src="/ss/js/edit.js"></script>
- <ssDlg setPval="true"/>
- <%-- setValue="true" close="true"。Lin --%>
- <script type="text/javascript" src="/ss/jquery/jquery.ellipsis.js"></script>
- <%-- 新UI引入的JS --%>
- <script src="/js/load.js"></script>
- <%-- ="/newUI/ss/js/base.js"。Lin(新UI) --%>
- <script src="/js/pageC.js"></script>
- <%-- 对象变动、修改、查看页,左则选项卡宽度 在里面定义 Ben(20251213) --%>
- <%-- 改为 <data@ss name="prt"/>。Lin
- <tab@ss name="print" enable="viewobject.funcMap.play.print"/> --%>
- <ss:data name='prt'/><%-- 初始化弹出窗口的打印按钮的后台数据 Ben--%>
- <%@include file="/ss/clip/prtIcon.jsp" %>
- <%-- 引入初始化弹出窗口的打印按钮的JS Ben--%>
- <script>
- window.ss.dom.formElemConfig = window.ss.dom.formElemConfig || {};
- </script>
- <style>
- /* 首页 loading:没加载完不显示页面内容 */
- .pobj-loading-home {
- position: fixed;
- left: 50%;
- top: 50%;
- transform: translate(-50%, -50%);
- z-index: 9999;
- pointer-events: none;
- }
- /* 列表 loading:区域性(搜索条件和分页之间) */
- .pobj-list-loading {
- width: 100%;
- min-height: 260px;
- display: flex;
- align-items: center;
- justify-content: center;
- }
- .pobj-spinner {
- width: 18px;
- height: 18px;
- border-radius: 50%;
- border: 2px solid rgba(0, 0, 0, 0.2);
- border-top-color: rgba(0, 0, 0, 0.65);
- animation: pobjSpin 0.8s linear infinite;
- }
- @keyframes pobjSpin {
- from {
- transform: rotate(0deg);
- }
- to {
- transform: rotate(360deg);
- }
- }
- </style>
- </head>
- <body>
- <div id="app">
- <div v-show="loadingHome" class="pobj-loading-home" style="display:none;">
- <div class="pobj-spinner"></div>
- </div>
- <%-- 功能说明:移除 thnType 直出调试内容,避免进入页面即输出 by xu 20260123 --%>
- <%-- 功能说明:主布局容器(左侧表单 + 右侧 ss-sidebar);为首屏 rowNumPer 自适应计算需要,避免用 homeReady 把容器 display:none(否则无法测量宽高) by xu 20260123 --%>
- <div style="display: flex; height: 100%; ">
- <%-- 功能说明:左侧表单区域在 flex 中占满剩余宽度(避免挤掉右侧 ss-sidebar) by xu 20260113 --%>
- <form class="page-container" id="myForm" style="flex: 1; min-width: 0; width: auto;"
- action="<ss:serv name='${currentService.servName}' dest='${currentService.dest}' parm='${currentService.parm}'/>"
- method="post">
- <input type="hidden" name="isAnd" v-model="form.isAnd"/>
- <input name="management" type="hidden" v-model="form.management"/>
- <input name="isFulltext" type="hidden" v-model="form.isFulltext"/>
- <%-- 功能说明:分页已完全走 Vue+Ajax(ssPaging),不再依赖旧表单提交隐藏域 by xu 20260123 --%>
- <%--搜索条件start--%>
- <div class="search-bar">
- <ss-breadcrumb></ss-breadcrumb>
- <div class="search-bar-contaienr">
- <template v-if="homeReady">
- <template v-for="field in searchFieldList" :key="field.name">
- <ss-objp
- v-if="field.cbName"
- v-model="form[field.name]"
- :name="field.name"
- :placeholder="field.desc"
- width="150px"
- inp="true"
- url="/service?ssServ=loadObjpOpt&objectpickerdropdown1=1"
- :cb="field.cbName"
- ></ss-objp>
- <ss-search-date-picker
- v-else-if="field.type == 11"
- v-model="form[field.name]"
- :name="field.name"
- type="date"
- :placeholder="field.desc"
- width="140px"
- :fmt="field.fmt"
- ></ss-search-date-picker>
- <ss-search-date-picker
- v-else-if="field.type == 3"
- v-model="form[field.name]"
- :name="field.name"
- type="datetime"
- :placeholder="field.desc"
- width="200px"
- :fmt="field.fmt"
- ></ss-search-date-picker>
- <ss-search-input
- v-else
- :name="field.name"
- :placeholder="field.desc"
- v-model="form[field.name]"
- width="120px"
- @search="search"
- ></ss-search-input>
- </template>
- <ss-search-input
- v-if="hasKeyword"
- name="ssKeyword"
- placeholder="关键词"
- v-model="form.ssKeyword"
- width="120px"
- @search="search"
- ></ss-search-input>
- <ss-search-button
- v-if="hasScope"
- :key="'scope-' + String(searchButtonConfigCheckId)"
- text="所有"
- icon-class="nav-icon-search"
- :opt="searchButtonConfig"
- :check-id="searchButtonConfigCheckId"
- ></ss-search-button>
- <template v-for="func in rootFuncList" :key="func.id || func.servName">
- <ss-search-button
- :text="func.desc || func.title || ''"
- icon-class="nav-icon-add"
- :opt="[]"
- :check-id="0"
- @click="handleRootFuncClick(func)"
- ></ss-search-button>
- </template>
- </template>
- <template v-else>
- <%--新UI 单对象查询翻页end Ben --%>
- <%--关键词--%>
- <%-- 再去掉 -- 只支持一行。Lin
- <ss:rpt name='searchableFields' id='fieldsList'>
- --%>
- <ss:rpt name='fieldsList' id='searchItem'>
- <%
- ss.obj.ObjFieldB f = (ss.obj.ObjFieldB) pageContext.getAttribute("searchItem");
- pageContext.setAttribute("searchItemValue", request.getParameter(f.name));
- %>
- <%--codebook 使用select下拉--%>
- <ss:equal val='${empty searchItem.cbName}' val2='false'>
- <%-- 旧的objecPicker
- <input type="text" name="${searchItem.name}" value="${searchItemValue}"/>
- <input type="text" name="${searchItem.name}Name" placeholder="${searchItem.desc}" style="width:${searchItem.width};min-width: 108px;"/>
- <ss:equal val='${empty cadcadingInputs[searchItem.name]}' val2='true'>
- <ss:objp name='${searchItem.name}' cb='${searchItem.cbName}' inp='true' filterField='${searchItem.filterFieldStr}'/>
- </ss:equal> --%>
- <%-- 新UI --%>
- <!-- <ss-objp
- v-model="${searchItem.name}"
- name="${searchItem.name}"
- :options="${searchItem.name}Option"
- placeholder="${searchItem.desc}"
- width="120"
- input="true"
- ></ss-objp> -->
- <ss-objp
- v-model="${searchItem.name}"
- name="${searchItem.name}"
- :opt="${searchItem.name}Option"
- placeholder="${searchItem.desc}"
- width="150px"
- inp="true"
- url="/service?ssServ=loadObjpOpt&objectpickerdropdown1=1"
- cb="${searchItem.cbName}"
- ></ss-objp>
- <script>
- /**
- * objectPicker(如:性别码)
- * optUrl:加载下拉菜单option选项的url
- */
- window.ss.dom.formElemConfig.${searchItem.name} = {
- desc: '${searchItem.desc}',
- value: '${searchItemValue}',
- optUrl: '123456',
- type: 2
- };//放当前页面表单元素配置的变量
- </script>
- </ss:equal>
- <%--codebook 使用input输入框--%>
- <ss:equal val='${empty searchItem.cbName}' val2='true'>
- <%--日期--%>
- <ss:equal val='${searchItem.type}'
- valList='3,11'> <%-- 改 equal:val1='true' val2='${searchItem.type == "time"}'。Lin --%>
- <!-- <div class="input-inside"> -->
- <%--年度--%>
- <%--
- <ss:equal val='${searchItem.enrolDate}' val2='true'>
- <input type='hidden' placeholder="${searchItem.desc}" name='${searchItem.name}'
- value="<ss:txt val='${searchItemValue}'/>"/>
- <div><input type="text" name="${searchItem.name}_year" autocomplete="off" />
- <input name="${searchItem.name}_month" type="button" value="春季" ssVal="3"/>
- <input name="${searchItem.name}_month" type="button" value="秋季" ssVal="9"/>
- </div><script>(function(){wd.edit.onoffInit('radio','${searchItem.name}_month','',false,null,null,null,'edit');})();</script>
- <script>(function(){
- wd.display.initEnrolDate('${searchItem.name}','edit');
- })()
- </script>
- </ss:equal>
- --%>
- <%--日期--%>
- <ss:equal val='${searchItem.type==11}' val2='true'>
- <%--<input type='text' autocomplete="off" placeholder="${searchItem.desc}" name='${searchItem.name}'
- value='<ss:txt val='${searchItemValue}'/>' format="${searchItem.fmt}"/>
- <input type="button" ssType="date" ssName="${searchItem.name}"/>--%>
- <!-- <ss-date-picker
- v-model="${searchItem.name}"
- name="${searchItem.name}"
- type="date"
- placeholder="${searchItem.desc}"
- width="120px"
- ></ss-date-picker> -->
- <ss-search-date-picker
- v-model="${searchItem.name}"
- name="${searchItem.name}"
- type="date"
- placeholder="${searchItem.desc}"
- width="100px"
- fmt="${searchItem.fmt}"
- ></ss-search-date-picker>
- <script>
- //日期类型(出生日期)
- window.ss.dom.formElemConfig.${searchItem.name} = {
- desc: '${searchItem.desc}',
- value: '${searchItemValue}',
- type: 3,
- name: '${searchItem.name}'
- };//放当前页面表单元素配置的变量
- </script>
- </ss:equal>
- <!-- </div> -->
- </ss:equal>
- <%--文本--%>
- <%-- 改 equal:val1='false' val2='${searchItem.type == "time"}'。Lin --%>
- <ss:notEqual val='${searchItem.type}' valList='3,11'>
- <%--<input name='${searchItem.name}' placeholder="${searchItem.desc}" type='text'
- value='<ss:txt val='${searchItemValue}'/>'/>
- <input type="hidden" ssType="and" ssName="${searchItem.name}"/>--%>
- <%--<script>wd.edit.addClearTextButton("${searchItem.name}");</script>--%>
- <ss-search-input
- name="${searchItem.name}"
- placeholder="${searchItem.desc}"
- v-model="${searchItem.name}"
- width="100px"
- >
- </ss-search-input>
- </ss:notEqual>
- </ss:equal>
- </ss:rpt>
- <%-- 再去掉 -- 只支持一行。Lin
- </ss:rpt>
- --%>
- <%--
- <ss:rpt name='cadcadingName' id='item'>
- <ss:ccp name='${item}'/>
- </ss:rpt>
- --%>
- <ss:equal val='${hasKeyWord}' val2='true'>
- <%--
- <input name="ssKeyword" value="${ssKeyword}" type="text" placeholder="关键词"/>
- <script>wd.edit.addClearTextButton("ssKeyword");</script>
- --%>
- <!-- <ss-search-input
- name="ssKeyword"
- placeholder="关键词"
- v-model="form.keyword"
- width="120"
- >
- </ss-search-input> -->
- <ss-search-input
- name="ssKeyword"
- placeholder="关键词"
- v-model="ssKeyword"
- width="100px"
- @search="search"
- >
- </ss-search-input>
- <script>
- //关键词
- window.ss.dom.formElemConfig.ssKeyword = {
- value: '${ssKeyword}',
- name: "ssKeyword",
- desc: "关键词",
- type: 31
- };//放当前页面表单元素配置的变量
- </script>
- </ss:equal>
- <ss-search-button
- text="所有"
- icon-class="nav-icon-search"
- :opt="searchButtonConfig"
- :check-id="searchButtonConfigCheckId"
- ></ss-search-button>
- <%-- 新UI 单对象查询 选项卡、翻页start Ben --%>
- <ss:equal val='${"1"==isReady && !isMultipleObject}' val2='true'>
- <%--
- <ss:equal val='${hasScope}' val2='true'>
- <ul style="list-style: none;display: inline-block;">
- <li ssType="searchScope" ssVal=99>所有</li>
- <li ssType="searchScope" ssVal=2>管理</li>
- <li ssType="searchScope" ssVal=1>创建</li>
- <li ssType="searchScope" ssVal=3>已办</li>
- <li ssType="searchScope" ssVal=55>停用</li>
- </ul>
- </ss:equal>
- --%>
- </ss:equal>
- <%--
- <input type="submit" name="ssSearch" value="搜索" class="content-invertButton"/>
- --%>
- <!-- <ss-search-button text="搜索" @click="console.log('这里改成提交表单')">
- </ss-search-button> -->
- <script>
- <%-- 根按钮(管理按钮) --%>
- window.ss.dom.btnElemConfig = window.ss.dom.btnElemConfig || {};
- </script>
- <%--多对像搜索隐藏全文按钮--%>
- <%--
- <ss:equal val='${isMultipleObject}' val2='false'>
- --%><%--
- <input type="hidden" ssType="fts" ssName="fts" value="全文"/>--%> <%-- wdType="isFulltext" wdName="isFulltext"。Lin --%>
- <%--</ss:equal>--%>
- <%--根按钮(管理按钮)start--%>
- <ss:rpt name='buttonList' id='button'>
- <ss:auth serv='${button.servName}'>
- <ss:equal val='${empty button.pluginList}' val2='true'>
- <%--
- <input type="button" name="${button.name}" value="${button.buttonName}" class="content-button"
- onclick='wd.display.showComponent({show:["wdDialog"],url:"<ss:serv name='${button.servName}' dest='${button.dest}' parm='${button.parm}'/>",title:"${button.title}",width:"${button.width}",height:"${button.height}",minHeight:"${button.minHeight}",maxHeight:"${button.maxHeight+100}",showTitle:"${button.showTitle}"});'/>
- --%>
- <!-- <ss-search-button
- text="${button.name}"
- icon-class="nav-icon-add"
- ></ss-search-button> -->
- <script>
- function ${button.id}handleClick() {
- wd.display.showComponent({
- show: ["wdDialog"],
- url: "<ss:serv name='${button.servName}' dest='${button.dest}' parm='${button.parm}'/>",
- title: "${button.title}",
- width: "${button.width}",
- height: "${button.height}",
- minHeight: "${button.minHeight}",
- maxHeight: "${button.maxHeight+100}",
- showTitle: "${button.showTitle}"
- });
- }
- <%-- 实际没用上,注释掉 Ben(20251225)
- window.ss.dom.btnElemConfig.${button.id}={
- desc:"${button.buttonName}",
- id:"${button.id}",
- dropOptions:[],
- onclick: () => {
- wd.display.showComponent({show:["wdDialog"],url:"<ss:serv name='${button.servName}' dest='${button.dest}' parm='${button.parm}'/>",title:"${button.title}",width:"${button.width}",height:"${button.height}",minHeight:"${button.minHeight}",maxHeight:"${button.maxHeight+100}",showTitle:"${button.showTitle}"});
- }
- };
- --%>
- </script>
- <ss-search-button
- text="${button.buttonName}"
- icon-class="nav-icon-add"
- :opt="[]"
- :check-id="0"
- onclick="${button.id}handleClick()"
- ></ss-search-button>
- </ss:equal>
- <ss:equal val='${empty button.pluginList}' val2='false'>
- <ss:rpt name='${button.pluginList}' id='plugin'>
- <span style="display:none;" class="${button.id}children" value="${plugin.plugin.desc}"
- onclick='wd.display.showComponent({show:["wdDialog"],url:"<ss:serv
- name='${plugin.servName}' dest='${plugin.dest}'
- parm='${plugin.parm}'/>",title:"${plugin.title}",width:"${plugin.width}",height:"${plugin.height}",minHeight:"${plugin.minHeight}",maxHeight:"${plugin.maxHeight}",showTitle:"${plugin.showTitle}"});'>
- </span>
- </ss:rpt>
- <%-- <input type="button" id="${button.id}" name="${button.name}" value="${button.buttonName}" class="content-button" onclick='void(0)'/>
- <span style=" display:inline-table; width: 60px; ">
- <input type="button" id="${button.id}" name="${button.name}" value="${button.buttonName}" class="content-button" onclick='void(0)'/>
- <span class="icon-dimPoint" style="margin-right: 7px;margin-top: -25px;position: relative;">
- </span>
- </span>
- <script>wd.display.attachButton("${button.id}","${button.id}children",null,false,true)</script>--%>
- <%-- 新UI start Ben --%>
- <!-- <ss-search-button
- text="${plugin.plugin.desc}"
- icon-class="nav-icon-add"
- :options="${button.id}DropOptions"
- ></ss-search-button> -->
- <%-- :opt改为取vue.data中的变量,而不取window.ss.dom.btnElemConfig(因为取不到),onclick改为:onclick xu(20251209) --%>
- <ss-search-button
- text="${button.buttonName}"
- icon-class="nav-icon-add"
- :opt="btnElemConfig.${button.id}.dropOptions"
- check-id="${management}"
- onclick="${button.id}handleClick()"
- ></ss-search-button>
- <script>
- function ${button.id}handleClick() {
- wd.display.showComponent({
- show: ["wdDialog"],
- url: "<ss:serv name='${plugin.servName}' dest='${plugin.dest}' parm='${plugin.parm}'/>",
- title: "${plugin.title}",
- width: "${plugin.width}",
- height: "${plugin.height}",
- minHeight: "${plugin.minHeight}",
- maxHeight: "${plugin.maxHeight}",
- showTitle: "${plugin.showTitle}"
- });
- }
- <%-- 实际没用上,注释掉 Ben(20251225)
- window.ss.dom.btnElemConfig.${button.id}={id:"${button.id}",dropOptions:[],
- onclick: () => {
- wd.display.showComponent({show:["wdDialog"],url:"<ss:serv name='${plugin.servName}' dest='${plugin.dest}' parm='${plugin.parm}'/>",title:"${plugin.title}",width:"${plugin.width}",height:"${plugin.height}",minHeight:"${plugin.minHeight}",maxHeight:"${plugin.maxHeight}",showTitle:"${plugin.showTitle}"});
- }
- };
- --%>
- <%-- 循环生成按钮数组 --%>
- <ss:rpt name='${button.pluginList}' id='plugin'>
- window.ss.dom.btnElemConfig
- .${button.id}.
- dropOptions.push(
- {
- desc: '${plugin.plugin.desc}', <%-- 按钮名 --%>
- callback: function () {
- wd.display.showComponent({
- show: ["wdDialog"],
- url: "<ss:serv name='${plugin.servName}' dest='${plugin.dest}' parm='${plugin.parm}'/>",
- title: "${plugin.title}",
- width: "${plugin.width}",
- height: "${plugin.height}",
- minHeight: "${plugin.minHeight}",
- maxHeight: "${plugin.maxHeight}",
- showTitle: "${plugin.showTitle}"
- });
- }
- }
- );
- </ss:rpt>
- </script>
- <%-- 新UI end Ben --%>
- </ss:equal>
- </ss:auth>
- </ss:rpt>
- <%--管理按钮end--%>
- </template>
- </div>
- </div>
- <%--搜索条件end--%>
- <script type="text/javascript" src="/ss/env/env_search.js"></script>
- <%-- 旧UI初始化查询列表的js,新UI做好后,要被删掉的 --%>
- <%-- 新UI初始化查询列表数据的JS --%>
- <script>
- window.ss.dom.listConfig = window.ss.dom.listConfig || {};
- //二级对象 草稿箱
- window.ss.dom.listConfig.draftbox = [];
- <%-- 草稿箱start --%>
- <ss:rpt name='cgxList' id='item'>
- {
- let item = {};
- window.ss.dom.listConfig.draftbox.push(item);
- <%-- 下面的titlexxx是原来td的一个属性,不知道有啥用 --%>
- <ss:equal val='${not empty item.service.play && empty item.service.update && empty item.service.change}' val2='true'>
- item.titlexxx = "${item.service.play.title}";
- item.onclick = function () {
- wd.display.showComponent({
- show: ["wdDialog"],
- url: "<ss:serv name='${item.service.play.servName}' dest='${item.service.play.dest}' parm='${item.service.play.parm}'/>",
- title: "${item.service.play.title}",
- width: "${item.service.play.width}",
- height: "${item.service.play.height}",
- minHeight: "${item.service.play.minHeight}",
- maxHeight: "${item.service.play.maxHeight}"
- });
- };
- </ss:equal>
- <ss:equal val='${item.service.lbm}' val2='1'>
- <ss:equal val='${empty item.service.update}' val2='false'>
- item.titlexxx = "${item.service.update.title}";
- item.onclick = function () {
- wd.display.showComponent({
- show: ["wdDialog"],
- url: "<ss:serv name='${item.service.update.servName}' dest='${item.service.update.dest}' parm='${item.service.update.parm}'/>",
- title: "${item.service.update.title}",
- width: "${item.service.update.width}",
- height: "${item.service.update.height}",
- minHeight: "${item.service.update.minHeight}",
- maxHeight: "${item.service.update.maxHeight}"
- });
- };
- </ss:equal>
- </ss:equal>
- <ss:equal val='${item.service.lbm}' val2='11'>
- <ss:equal val='${empty item.service.change}' val2='false'>
- item.titlexxx = "${item.service.change.title}";
- item.onclick = function () {
- wd.display.showComponent({
- show: ["wdDialog"],
- url: "<ss:serv name='${item.service.change.servName}' dest='${item.service.change.dest}' parm='${item.service.change.parm}'/>",
- title: "${item.service.change.title}",
- width: "${item.service.change.width}",
- height: "${item.service.change.height}",
- minHeight: "${item.service.change.minHeight}",
- maxHeight: "${item.service.change.maxHeight}"
- });
- };
- </ss:equal>
- </ss:equal>
- item.buttons = [];
- <ss:equal val='${item.service.lbm}' val2='1'>
- item.buttons.push(
- {
- class: "cart-list-setting",
- title: "增加",
- onclick: () => {
- console.log("点击了增加");
- }
- }
- );
- </ss:equal>
- <ss:equal val='${item.service.lbm}' val2='11'>
- item.buttons.push(
- {
- class: "cart-list-setting",
- title: "变动",
- onclick: () => {
- console.log("点击了变动");
- }
- }
- );
- </ss:equal>
- <ss:equal val='${item.service.lbm}' val2='51'>
- item.buttons.push(
- {
- class: "cart-list-setting",
- title: "停用",
- onclick: () => {
- console.log("点击了停用");
- }
- }
- );
- </ss:equal>
- <ss:equal val='${item.service.lbm}' val2='55'>
- item.buttons.push(
- {
- class: "cart-list-setting",
- title: "启用",
- onclick: () => {
- console.log("点击了启用");
- }
- }
- </ss:equal>
- <ss:equal val='${item.service.lbm}' valList='51,55'>
- item.buttons.push(
- {
- class: "cart-list-setting",
- title: "还原",
- onclick: () => {
- wd.display.showComponent({
- show: ["wdDialog"],
- url: "<ss:serv name='deleteSq' parm='{"wdConfirmationCaptchaService":"0","sqid":"${item.sqid}"}' dest='info'/>",
- title: "提示信息",
- width: 715,
- height: 483
- });
- }
- }
- </ss:equal>
- console.log('###item.thumbnail:${item.thumbnail}');
- <%--缩略图--%>
- <ss:equal val='${empty item.thumbnail}' val2='false'>
- <ss:equal val='${item.service.state}' val2='0'>
- item.thumb = "${sessionScope['ssUser'].skinDir}image/object/default-${item.ssObjName}.png"
- onerror = "javascript:this.src='${sessionScope['ssUser'].skinDir}image/default-photo.png';this.onerror=null;";
- </ss:equal>
- <ss:notEqual val='${item.service.state}' val2='0'>
- item.thumb = "<ss:serv name='dlByHttp' parm='{"wdConfirmationCaptchaService":"0","path":"${item.thumbnail.value}","type":"img"}'/>";
- </ss:notEqual>
- </ss:equal>
- <%-- 列标题 --%>
- <ss:equal val='${empty item.first}' val2='false'> <%-- 改 equal:val1="true" val2="${not empty item.first}"。Lin --%>
- <%--不带codebook--%>
- <ss:equal val='${empty item.first.field.cbName}' val2='true'>
- item.title = "<ss:txt val='${item.first.value}' miniDate='false' fmt='${item.first.field.fmt}'/>";
- </ss:equal>
- <%--带codebook--%>
- <ss:equal val='${empty item.first.field.cbName}' val2='false'>
- item.title = "<ss:cbTrans cb='${item.first.field.cbName}' val='${item.first.value}'/>";
- </ss:equal>
- </ss:equal>
- <%--列标题:缺标题显示属性start--%>
- <ss:equal val='${empty item.first && not empty item.third}' val2='true'>
- item.title = '';
- <ss:rpt name='${item.third}' id='itemList'>
- <ss:rpt name='${itemList}' id='item2'>
- item.title += '${item2.field.desc}:';
- <%--不带codebook--%>
- <ss:equal val='${empty item2.field.cbName}' val2='true'>
- item.title += '<ss:txt val='${item2.value}' fmt='${item2.field.fmt}' miniDate='false'/> ';
- </ss:equal>
- <%--带codebook--%>
- <ss:equal val='${empty item2.field.cbName}' val2='false'>
- item.title += '<ss:cbTrans cb='${item2.field.cbName}' val='${item2.value}'/> ';
- </ss:equal>
- </ss:rpt>
- </ss:rpt>
- </ss:equal>
- <%--缺标题显示属性end--%>
- <%--正文或摘要--%>
- <ss:equal val='${empty item.second}' val2='false'>
- // 摘要字段统一映射为 desc(用于 ss-list-card 右侧文字区规则) by xu 20260113
- item.desc = '${item.second.value}';
- item.summary = '${item.second.value}'; // legacy 兜底 by xu 20260113
- </ss:equal>
- // 对象号字段映射(用于 ss-list-card 第三部分回显) by xu 20260113
- item.objNum = '${item.ssObjId}';
- <%-- 列表底部的对象属性(tags) --%>
- <ss:equal val='${empty item.first || empty item.third}' val2='false'>
- item.tags = [];
- <ss:rpt name='${item.third}' id='itemList'>
- <ss:rpt name='${itemList}' id='item2'>
- {
- let v;
- <%--不带codebook--%>
- <ss:equal val='${empty item2.field.cbName}' val2='true'>
- v = '<ss:txt val='${item2.value}' fmt='${item2.field.fmt}' miniDate='false'/>';
- </ss:equal>
- <%--带codebook--%>
- <ss:equal val='${empty item2.field.cbName}' val2='false'>
- v = '<ss:cbTrans cb='${item2.field.cbName}' val='${item2.value}'/>';
- </ss:equal>
- item.tags.push({'${item2.field.desc}': v});
- }
- </ss:rpt>
- </ss:rpt>
- </ss:equal>
- <%-- 对象变动前后属性列表 --%>
- item.changeItems = [];
- <ss:rpt name='${item.forth}' id='item3'>
- item.changeItems.push({
- name: '${item3.name}',
- oldValue: '${item3.oldValue}',
- newValue: '${item3.newValue}'
- });
- </ss:rpt>
- }
- </ss:rpt>
- <%-- 草稿箱end --%>
- //在用或停用的对象列表
- window.ss.dom.listConfig.list = [];
- <ss:rpt name='objectList' id='item'><%-- 循环一次生成一行列表 start --%>
- {
- let item = {};//列表的其中一行的属性
- window.ss.dom.listConfig.list.push(item);
- <ss:equal val='${item.service.state}' val2='0'><%-- 借阅 --%>
- item.onclick = function () {
- wd.display.showComponent({
- show: ["wdDialog"],
- url: "<ss:serv name='ydsq_tj' parm='{"wdConfirmationCaptchaService":"0","ssObjId":"${item.ssObjId}","ssObjName":"${item.ssObjName}"}' dest='ydsq_tj'/>",
- title: "借阅",
- width: 613,
- height: 387
- });
- }
- </ss:equal>
- <ss:equal val='${empty item.service.play}' val2='false'><%-- 有权查看,不需要借阅的情况 --%>
- item.titlexxx = "${item.service.play.title}";
- item.onclick = function () {
- wd.display.showComponent({
- getSize: 1,
- width: ((${item.service.play.width}+DOM_SIZE_objInfoTabWidth) + ""),
- show: ["wdDialog"],
- url: "<ss:serv name='${item.service.play.servName}' dest='${item.service.play.dest}' parm='${item.service.play.parm}'/>",
- title: "${item.service.play.title}",
- height: "${item.service.play.height}",
- minHeight: "${item.service.play.minHeight}",
- maxHeight: "${item.service.play.maxHeight}"
- });
- }
- </ss:equal>
- <ss:equal val='${empty item.thumbnail}' val2='false'><%-- 缩略图 --%>
- <ss:equal val='${item.service.state}' val2='0'><%-- 借阅 --%>
- item.thumb = "${sessionScope['ssUser'].skinDir}image/object/default-${item.ssObjName}.png";
- </ss:equal>
- <ss:notEqual val='${item.service.state}' val2='0'>
- console.log('@@@图片:${item.thumbnail.value}');
- item.thumb = "<ss:serv name='dlByHttp' parm='{"wdConfirmationCaptchaService":"0","path":"${item.thumbnail.value}","type":"img"}'/>";
- </ss:notEqual>
- </ss:equal>
- <%-- 标题 --%>
- <ss:equal val='${empty item.first}' val2='false'> <%-- 改 equal:val1="true" val2="${not empty item.first}"。Lin --%>
- <%--不带codebook--%>
- <ss:equal val='${empty item.first.field.cbName}' val2='true'>
- item.title = "<ss:txt val='${item.first.value}' miniDate='false' fmt='${item.first.field.fmt}'/>";
- </ss:equal>
- <%--带codebook--%>
- <ss:equal val='${empty item.first.field.cbName}' val2='false'>
- item.title = "<ss:cbTrans cb='${item.first.field.cbName}' val='${item.first.value}'/>";
- </ss:equal>
- </ss:equal>
- <%--缺标题显示属性当标题start--%>
- <ss:equal val='${empty item.first && not empty item.third}' val2='true'>
- item.title = '';
- <ss:rpt name='${item.third}' id='itemList'>
- <ss:rpt name='${itemList}' id='item2'>
- item.title += '${item2.field.desc}:';
- <%--属性名--%>
- <%--不带codebook--%><%--属性值--%>
- <ss:equal val='${empty item2.field.cbName}' val2='true'>
- item.title += "<ss:txt val='${item2.value}' fmt='${item2.field.fmt}' miniDate='false'/> ";
- </ss:equal>
- <%--带codebook--%><%--属性值--%>
- <ss:equal val='${empty item2.field.cbName}' val2='false'>
- item.title += "<ss:cbTrans cb='${item2.field.cbName}' val='${item2.value}'/> ";
- </ss:equal>
- </ss:rpt>
- </ss:rpt>
- </ss:equal>
- <%--缺标题显示属性当标题end--%>
- <%-- 摘要 --%>
- <ss:equal val='${empty item.second}' val2='false'>
- // 摘要字段统一映射为 desc(用于 ss-list-card 右侧文字区规则) by xu 20260113
- item.desc = '${item.second.value}';
- item.summary = '${item.second.value}'; // legacy 兜底 by xu 20260113
- </ss:equal>
- // 对象号字段映射(用于 ss-list-card 第三部分回显) by xu 20260113
- item.objNum = '${item.ssObjId}';
- <%-- 列表底部的对象标签组 --%>
- item.tags = [];
- <%-- 提到条件外面 Ben(20251230) --%>
- <ss:equal val='${empty item.first || empty item.third}' val2='false'>
- <%--item.tags=[]; 提到条件外面,不然列表报 Mount failed:TypeError: Cannot read properties of undefined (reading 'map') Ben(20251230) --%>
- <ss:rpt name='${item.third}' id='itemList'>
- <ss:rpt name='${itemList}' id='item2'>
- {
- let v;
- <%--不带codebook--%>
- <ss:equal val='${empty item2.field.cbName}' val2='true'>
- v = '<ss:txt val='${item2.value}' miniDate='false' fmt='${item2.field.fmt}'/>';
- </ss:equal>
- <%--带codebook--%>
- <ss:equal val='${empty item2.field.cbName}' val2='false'>
- v = '<ss:cbTrans cb='${item2.field.cbName}' val='${item2.value}'/>';
- </ss:equal>
- item.tags.push({
- ${item2.field.desc}:
- v
- })
- ;
- }
- </ss:rpt>
- </ss:rpt>
- </ss:equal>
- <%--按钮--%>
- <ss:equal val='${empty item.service.btnList}' val2='false'>
- item.buttons = [];
- <ss:rpt name='${item.service.btnList}' id='btn'>
- item.buttons.push(
- {
- id: "${btn.btnID}",
- titlexxx: "${btn.title}",
- class: "cart-list-setting",
- title: "${btn.name}",
- onclick: () => {
- <%--列表的变动按钮就在这里,下面的width会根据不同业务对象而不同
- 弹窗参数加上getSize=1,使showComponent弹窗方法不再通过ajax从后台取宽高 --%>
- wd.display.showComponent({
- getSize: 1,
- width: ((${btn.width}+DOM_SIZE_objInfoTabWidth) + ""),
- show: ["wdDialog"],
- url: "<ss:serv name='${btn.servName}' dest='${btn.dest}' parm='${btn.parm}'/>",
- title: "${btn.title}",
- height: "${btn.height}",
- minHeight: "${btn.minHeight}",
- maxHeight: "${btn.maxHeight}"
- });
- }
- }
- );
- <%--
- <ss:equal val='${index}' val2='0'>
- </ss:equal>
- <ss:notEqual val='${index}' val2='0'>
- </ss:notEqual>
- 改为不区分是否第1个变动按钮,都输出相同json数据 --%>
- </ss:rpt>
- </ss:equal>
- }
- </ss:rpt><%-- 循环一次生成一行列表 end --%>
- </script>
- <%--搜索结果start--%>
- <%-- 功能说明:卡片区域使用 grid 自动分列,剩余宽度平分(不锁死 max-width) by xu 20260109 --%>
- <div v-show="userInteracted && loadingList" class="pobj-list-loading" style="display:none;">
- <div class="pobj-spinner"></div>
- </div>
- <div v-show="!(userInteracted && loadingList)"
- :class="['content-area','item-content-area', cardGridKind ? ('ss-card-grid--' + cardGridKind) : '']">
- <template v-for="(item, i) in listConfig.draftbox" :key="i">
- <ss-folder-card v-if="item.children" :item="item"></ss-folder-card>
- <!-- 功能说明:传入 ssObjName + cardClickAction(卡片主体点击 view/single;角标仍多选) by xu 20260109 -->
- <ss-list-card v-else :item="item" :ss-obj-name="ssObjName" :card-click-action="cardClickAction"
- @toggle-select="handleToggleSelect"></ss-list-card>
- </template>
- <template v-for="(item, i) in listConfig.list" :key="i">
- <ss-folder-card v-if="item.children" :item="item"></ss-folder-card>
- <!-- 功能说明:传入 ssObjName + cardClickAction(卡片主体点击 view/single;角标仍多选) by xu 20260109 -->
- <ss-list-card v-else :item="item" :ss-obj-name="ssObjName" :card-click-action="cardClickAction"
- @toggle-select="handleToggleSelect"></ss-list-card>
- </template>
- <ss-page
- :key="String(ssPaging?.pageNo || 1) + '-' + String(ssPaging?.rowNumPer || 0) + '-' + String(ssPaging?.rowNum || 0)" <%-- // 功能说明:分页 key 不再硬编码 12,仅用于触发分页组件重渲染 by xu 20260123 --%>
- :total="Number(ssPaging?.rowNum || 0)"
- :size="Number(ssPaging?.rowNumPer || 0)" <%-- // 功能说明:分页大小仅使用 ssPaging.rowNumPer(默认值由 data 初始化兜底一次) by xu 20260123 --%>
- :page="Number(ssPaging?.pageNo || 1)"
- @change="handlePageChange">
- </ss-page>
- </div>
- </form>
- <!-- 功能说明:右侧业务面板(ss-sidebar),用于已选/服务/预订/图表等;当无 rbarTabNameList/rbarFuncList/pstatList 时临时隐藏 by xu 20260122 -->
- <ss-sidebar v-if="showRightSidebar"
- :buttons="sidebarButtons"
- :panels="sidebarPanels"
- @remove="handleSidebarRemove"
- ></ss-sidebar>
- <!-- 功能说明:业务页编辑态右下角新增入口(+ hover 上拉:增加图/增加表) by xu 20260123 -->
- <div v-if="isSysEditMode && sysModeSource === 'biz'" class="ss-objlist-fab" @click.stop>
- <button type="button" class="ss-objlist-fab__btn" title="新增">
- <ss-icon class="menu-base-icon icon-add"></ss-icon> <%-- // 功能说明:新增按钮图标样式参考左侧菜单 by xu 20260123 --%>
- </button>
- <div class="ss-objlist-fab__menu">
- <div class="ss-objlist-fab__item" @click="openAddPcht">增加图</div>
- <div class="ss-objlist-fab__item" @click="openAddPform">增加表</div>
- </div>
- </div>
- </div>
- </div>
- </body>
- </html>
- <script type="module">
- <%-- 打印新UI相关json对象 --%>
- const data = {
- ssObjName: "${ssObjName}", // 对象名
- // 后端注入的 ssServ 接口名(用于 ajax 拉取查询首页/列表数据)
- ssSearchPobjHomeServName: "${ssSearchPobjHomeServName}", // <对象名>_cxsy
- ssSearchPobjListServName: "${ssSearchPobjListServName}", // <对象名>_cxlb
- // 接口返回(调试用,可删)
- ssHomeResp: null,
- ssListResp: null,
- // 页面状态
- homeReady: false,
- userInteracted: false, // 用户触发(搜索/翻页/切换范围)
- loadingHome: false,
- loadingList: false,
- isSysEditMode: false, // 功能说明:系统编辑态(业务页)控制右下角“新增”入口显隐 by xu 20260123
- sysModeSource: "unknown", // 功能说明:编辑态来源(desktop/biz/menu/unknown) by xu 20260123
- // 首页接口返回字段(按 page/objlist修改.txt)
- searchFieldList: [], // 查询条件字段
- rootFuncList: [], // 根功能
- hasKeyword: false, // 是否有关键词
- hasScope: false, // 是否带范围
- thnType: (() => { // 功能说明:首屏 thnType 优先从 JSP 注入(用于第一个接口前正确计算 rowNumPer);空/缺省回退 0 by xu 20260123
- try {
- const s = String("${thnType}" || "").trim();
- const n = parseInt(s, 10);
- return isNaN(n) ? 0 : n;
- } catch (_) {
- return 0;
- }
- })(), // 缩略图类型:1横向 2竖向 3方形 4圆形(目前仅用 1/2,其他先不处理)
- ssPaging: {pageNo: 1, rowNumPer: 12, rowNum: 0}, // 翻页信息 // 功能说明:分页默认每页12条(全文件仅此处兜底) by xu 20260123
- rbarFuncList: [], // 右侧栏功能
- rbarTabNameList: [], // 右侧栏选项卡
- pstatList: [], // 功能说明:个人统计图表配置(右侧图表区) by xu 20260114
- // 查询表单参数(用于接口入参)
- form: {management: "99", isFulltext: "0", isAnd: "", ssKeyword: ""},
- systemType: window.ss.dom.TYPE,
- listConfig: window.ss.dom.listConfig || {draftbox: [], list: []},
- formElemConfig: window.ss.dom.formElemConfig || {},
- // 卡片区域:自动分列/宽度平分 by xu 20260109
- cardGridKind: (() => { // 功能说明:首屏按 thnType 预设网格类型(决定 --ss-card-min),避免缩略图业务第一个接口仍按 300px 计算 by xu 20260123
- try {
- const t = (() => { const s = String("${thnType}" || "").trim(); const n = parseInt(s, 10); return isNaN(n) ? 0 : n; })();
- if (t === 1) return "thumbnail";
- if (t === 2) return "photo";
- return "none";
- } catch (_) {
- return "none";
- }
- })(), // none/photo/thumbnail by xu 20260109
- // 卡片主体点击动作:view=查看(调用 item.onclick);single=单选互斥;角标仍多选 by xu 20260109
- cardClickAction: "view", // 功能说明:默认先用 view,首页接口若返回预订面板则切到 single by xu 20260114
- hasBookPanel: false, // 功能说明:是否存在预订面板(驱动 cardClickAction + 选中后联动 cxycl) by xu 20260116
- hasServPanel: false, // 功能说明:是否存在服务面板(严格依赖首页 rbarTabNameList 是否返回 rbarServ) by xu 20260116
- hasObjPanel: false, // 功能说明:是否存在对象面板(严格依赖首页 rbarTabNameList 是否返回 rbarObj;无则不显示“已选”且禁止选中) by xu 20260122
- rbar: (String("${rbar}" || "").trim().toLowerCase() === "true") ? true : null, // 功能说明:JSP 注入 rbar:有字段且为 "true" 才展示;缺省/空串为 null,首页接口返回后会覆盖 by xu 20260123
- showRightSidebar: (String("${rbar}" || "").trim().toLowerCase() === "true"), // 功能说明:首屏右侧栏显隐以 JSP rbar 为准(用于首屏测量口径) by xu 20260123
- autoRowNumPer: 0, // 功能说明:动态计算的 rowNumPer(只记录,下次接口请求时生效;resize 不立即重拉) by xu 20260123
- yclLoading: false, // 功能说明:已选对象联动(cxycl)加载状态 by xu 20260114
- yclResp: null, // 功能说明:已选对象联动接口返回(调试/兜底) by xu 20260114
- // 右侧已选(用于未来接入 ss-sidebar 的已选面板) by xu 20260109
- selectedItems: [],
- // 右侧边栏:顶部按钮(由首页接口 rbarFuncList 初始化)
- sidebarButtons: [],
- // 右侧边栏:分区配置(由 initSidebarPanels 初始化) by xu 20260113
- sidebarPanels: [],
- <%-- 实际没用上,注释掉 Ben(20251225) btnElemConfig:window.ss.dom.btnElemConfig, --%>
- searchButtonConfigCheckId: "99",
- searchButtonConfig: [
- {
- id: "99",
- desc: "所有",
- callback: () => window.__objListVm && window.__objListVm.switchScope(99),
- },
- {
- id: "2",
- desc: "管理",
- callback: () => window.__objListVm && window.__objListVm.switchScope(2),
- },
- {
- id: "1",
- desc: "创建",
- callback: () => window.__objListVm && window.__objListVm.switchScope(1),
- },
- {
- id: "3",
- desc: "已办",
- callback: () => window.__objListVm && window.__objListVm.switchScope(3),
- },
- {
- id: "55",
- desc: "停用",
- callback: () => window.__objListVm && window.__objListVm.switchScope(55),
- }
- ],
- }
- if (window.ss.dom.formElemConfig) {
- Object.entries(window.ss.dom.formElemConfig).forEach(([key, config]) => {
- data[key] = config.value;
- // 处理 objPicker
- if (config.type === window.ss.dom.TYPE.OBJPICKER) {
- data[key + "ObjPicker"] = true;
- data[key + "Option"] = [];
- data[key + "Url"] = config.optUrl;
- }
- })
- }
- SS.ready(function () {
- // 功能说明:右侧 ss-sidebar__inner 统一增加 padding-top=10px(撤回外层 wrapper 方案) by xu 20260114
- try {
- if (!document.getElementById("ss-objlist-sidebar-inner-padding")) {
- var st = document.createElement("style");
- st.id = "ss-objlist-sidebar-inner-padding";
- st.type = "text/css";
- st.appendChild(document.createTextNode("#app .ss-sidebar__inner{padding-top:10px;}"));
- document.head.appendChild(st);
- }
- } catch (e) {
- }
- window.ss.dom.initializeFormApp({
- el: "#app",
- data() {
- return data;
- },
- methods: {
- openAddPform() {
- // 功能说明:编辑态新增统计表(pformSet) by xu 20260123
- try {
- wd.display.showComponent({
- show: ["wdDialog"],
- url: "<ss:serv name='addPform' parm='{\"wdConfirmationCaptchaService\":\"0\",\"dxm\":\"${ssObjName}\"}' dest='pformSet'/>",
- title: "设置统计表",
- width: 800,
- height: 580
- });
- } catch (e) {
- console.error("openAddPform failed", e);
- }
- },
- openAddPcht() {
- // 功能说明:编辑态新增统计图(pchtSet) by xu 20260123
- try {
- wd.display.showComponent({
- show: ["wdDialog"],
- url: "<ss:serv name='addPcht' parm='{\"wdConfirmationCaptchaService\":\"0\",\"dxm\":\"${ssObjName}\",\"use_grtjmbid\":\"${use_grtjmbid}\"}' dest='pchtSet'/>",
- title: "设置统计图",
- width: 800,
- height: 580
- });
- } catch (e) {
- console.error("openAddPcht failed", e);
- }
- },
- openServiceDialog(srv) {
- // 功能说明:使用 tools.js 统一封装的 openServiceDialog,支持透传搜索条件 by xu 20260122
- if (!srv) return;
- const extraParams = window.ssTools.pickSearchParams(this.searchFieldList, this.form);
- window.ssTools.openServiceDialog(srv, { extraParams });
- },
- normalizeObjToCard(row) {
- const card = {
- _raw: row,
- ssObjName: row?.ssObjName,
- objNum: row?.ssObjId,
- title: "",
- desc: row?.abs || "",
- tags: [],
- buttons: [],
- stateIconList: row?.stateIconList || [],
- statusIcons: [],
- bigState: row?.bigState,
- bookState: row?.bookState,
- };
- // 功能说明:将后端 stateIconList 映射为卡片右上角 statusIcons(按 icon-state/icon-servType 规则转为 biz 图标组;无匹配则不展示;不使用 menu-*) by xu 20260123
- try {
- const rawList = Array.isArray(row?.stateIconList) ? row.stateIconList : [];
- const icons = [];
- const seen = new Set();
- rawList.forEach((s) => {
- const cls = String(s || "").trim();
- if (!cls) return;
- const m = cls.match(/-(\d+)$/);
- const code = m ? Number(m[1]) : NaN;
- if (code === 1) return; // 约定:后缀为 1 的不处理 by xu 20260116
- // 功能说明:icon-state-<对象名>-<状态码>:15=清洁中(icon-obj-qjz),275=维修中(icon-obj-wxz) by xu 20260123
- const stateMatch = cls.match(/^icon-state-[^-]+-(\d+)$/);
- if (stateMatch) {
- const stateCode = Number(stateMatch[1]);
- if (stateCode === 15) {
- const k = "ss-card-status-biz-icon icon-obj-qjz";
- if (!seen.has(k)) { seen.add(k); icons.push({ class: k, title: "清洁中" }); }
- return;
- }
- if (stateCode === 275) {
- const k = "ss-card-status-biz-icon icon-obj-wxz";
- if (!seen.has(k)) { seen.add(k); icons.push({ class: k, title: "维修中" }); }
- return;
- }
- return;
- }
- // 功能说明:icon-servType-<对象名>-<服务类别码>:只要类别码!=1 就显示服务图标 icon-obj-fw by xu 20260123
- const servTypeMatch = cls.match(/^icon-servType-[^-]+-(\d+)$/);
- if (servTypeMatch) {
- const servTypeCode = Number(servTypeMatch[1]);
- if (servTypeCode !== 1) {
- const k = "ss-card-status-biz-icon icon-obj-fw";
- if (!seen.has(k)) { seen.add(k); icons.push({ class: k, title: "服务中" }); }
- }
- return;
- }
- // 功能说明:兜底支持后端直接下发 icon-base 字形类(例如 icon-fauth),不再拼 menu-base-icon;不再用 icon-sauth 兜底 by xu 20260123
- try {
- const tokens = cls.match(/\bicon-[a-zA-Z0-9_-]+\b/g) || [];
- const glyph = tokens.find((t) => t && t.indexOf("icon-state-") !== 0 && t.indexOf("icon-servType-") !== 0);
- if (glyph) {
- if (glyph === "icon-sauth") return; // 功能说明:不再使用 icon-sauth 作为兜底/透传展示 by xu 20260123
- const k = "ss-card-status-base-icon " + glyph;
- if (!seen.has(k)) { seen.add(k); icons.push({ class: k, title: "使用中" }); }
- }
- } catch (_) {}
- });
- card.statusIcons = icons;
- } catch (e) {
- }
- // 功能说明:title 可能是 {val, fmt}(日期/时间),此处按 fmt 做格式化(避免显示英文时间串) by xu 20260114
- try {
- const t = row?.title;
- if (t && typeof t === "object" && t.val !== undefined) {
- card.title = String(window.ssTools.formatValByFmt(t.val, t.fmt) ?? "");
- } else {
- card.title = String(row?.title?.val || row?.title || "");
- }
- } catch (_) {
- card.title = String(row?.title?.val || row?.title || "");
- }
- // 功能说明:bookState 优先于 bigState(0绿空闲/1黄预订中/2红),映射到卡片 status by xu 20260116
- const hasBookState = row?.bookState !== undefined && row?.bookState !== null && row?.bookState !== "";
- const hasBigState = row?.bigState !== undefined && row?.bigState !== null && row?.bigState !== "";
- if (hasBookState || hasBigState) {
- const stateVal = hasBookState ? Number(row.bookState) : Number(row.bigState);
- card.status = stateVal === 2 ? "disabled" : stateVal === 1 ? "unavailable" : stateVal === 0 ? "available" : "";
- }
- // thnType:缩略图类型(目前仅处理 1=横向、2=竖向;其余先按无缩略图处理)
- // 注意:即使 thn 为空,只要 thnType 存在,也要保留缩略图区域,以显示“对象号_icon”占位
- const thnType = Number(this.thnType || 0);
- if (thnType === 1) card.thumbType = "thumbnail"; // 横向:180×100
- else if (thnType === 2) card.thumbType = "photo"; // 竖向:73×100(默认)
- // thn:缩略图不为空 => 有图片(否则 ss-list-card 会按 thumbType 显示占位 icon)
- if (row?.thn) card.thumb = window.ssTools.buildThumbUrl(row.thn);
- if (Array.isArray(row?.catList)) {
- row.catList.forEach((c) => {
- if (!c || !c.desc) return;
- // 功能说明:catList 存在 fmt 时格式化时间(yyyy-MM-dd / yyyy-MM-dd HH:mm:ss),避免直接显示英文时间串 by xu 20260114
- const val = window.ssTools.formatValByFmt(c.val, c.fmt);
- card.tags.push({[c.desc]: val});
- });
- }
- if (Array.isArray(row?.chgRootFuncList)) {
- row.chgRootFuncList.forEach((btn) => {
- card.buttons.push({
- id: btn.id,
- titlexxx: btn.title,
- class: "cart-list-setting",
- title: btn.desc || btn.title || "",
- onclick: () => this.openServiceDialog(btn),
- });
- });
- }
- if (row?.play) {
- card.onclick = () => this.openServiceDialog(row.play);
- }
- return card;
- },
- buildPstatChartPanels(bizIcon) {
- // 功能说明:根据 pstatList 构建右侧图表面板(type=chart),用于 ss-sidebar 底部图表区 by xu 20260114
- const list = Array.isArray(this.pstatList) ? this.pstatList : [];
- return list
- .filter((it) => Number(it?.grtjlbm) === 1)
- .map((it) => ({
- type: "chart",
- _grtjid: it?.grtjid,
- title: it?.mc || "",
- iconClass: bizIcon,
- // 功能说明:右侧面板图表高度做上限(避免后端 gd 过大导致 sidebar 竖向撑爆) by xu 20260114
- height: (() => {
- const gd = Number(it?.gd);
- const h = (isFinite(gd) && gd > 0) ? Math.max(160, Math.min(260, gd)) : 220;
- return String(h) + "px";
- })(),
- // 功能说明:统计图默认先用柱状图(后续可按后端类型再扩展) by xu 20260115
- options: {
- xAxis: {type: "category", data: []},
- yAxis: {type: "value"},
- series: [{type: "bar", data: []}]
- },
- }));
- },
- loadPstatChartData(panel) {
- // 功能说明:根据 grtjid 调 wrPchtPlay 获取真实数据,并写回 panel.options 触发渲染 by xu 20260114
- const grtjid = panel?._grtjid;
- if (!grtjid) return Promise.resolve(null);
- return this.callSsService("wrPchtPlay", {grtjid})
- .then((res) => {
- const payload = res && typeof res === "object" ? (res.ssData || res) : null;
- // 功能说明:兼容 wrPchtPlay 返回 dataArray(name=mc,value=sl) by xu 20260114
- let list =
- (payload && (payload.dataArray || payload.dataList || payload.list || payload.data)) ||
- payload;
- if (!Array.isArray(list)) list = [];
- const data = list
- .map((it) => {
- if (!it || typeof it !== "object") return null;
- const name = it.name ?? it.mc ?? it.desc ?? it.title;
- const value = it.value ?? it.sl ?? it.zrs ?? it.count ?? it.num;
- if (name === undefined || name === null) return null;
- const v = Number(value);
- return {name: String(name), value: isNaN(v) ? 0 : v};
- })
- .filter(Boolean);
- // 功能说明:统计图默认先用柱状图(tooltip/label 显示数值;全 0 也正常展示) by xu 20260115
- const xData = data.map((it) => it.name);
- const yData = data.map((it) => it.value);
- panel.options = {
- color: ["#5470C6", "#91CC75", "#FAC858", "#EE6666", "#73C0DE", "#3BA272", "#FC8452", "#9A60B4", "#EA7CCC"],
- tooltip: {trigger: "axis", axisPointer: {type: "shadow"}},
- grid: {left: "3%", right: "3%", top: 12, bottom: 24, containLabel: true},
- xAxis: {
- type: "category",
- data: xData,
- axisLabel: {interval: 0, rotate: xData.length > 6 ? 30 : 0}
- },
- yAxis: {type: "value"},
- series: [{
- type: "bar",
- barMaxWidth: 36,
- data: yData,
- label: {show: true, position: "top"},
- }],
- };
- // 功能说明:强制触发 sidebarPanels 更新(避免仅在选中卡片后才刷新图表) by xu 20260114
- try {
- this.sidebarPanels = (this.sidebarPanels || []).slice();
- } catch (e) {
- }
- // 功能说明:图表初次渲染时容器尺寸可能未稳定,nextTick 后触发 resize,避免“未选中=灰色圆” by xu 20260114
- try {
- const self = this;
- if (self && self.$nextTick) {
- self.$nextTick(function () {
- try {
- window.dispatchEvent(new Event("resize"));
- } catch (e) {
- }
- });
- }
- setTimeout(function () {
- try {
- window.dispatchEvent(new Event("resize"));
- } catch (e) {
- }
- }, 120);
- } catch (e) {
- }
- return res;
- })
- .catch((e) => {
- console.error("loadPstatChartData failed", grtjid, e);
- return null;
- });
- },
- applyPobjHomeData(ssData) {
- if (!ssData || typeof ssData !== "object") return;
- this.ssHomeResp = ssData;
- this.homeReady = true;
- this.userInteracted = false;
- this.ssObjName = ssData.ssObjName || this.ssObjName; // 对象名
- this.searchFieldList = Array.isArray(ssData.searchFieldList) ? ssData.searchFieldList : []; // 查询条件字段
- this.rootFuncList = Array.isArray(ssData.rootFuncList) ? ssData.rootFuncList : []; // 根功能
- this.hasKeyword = !!ssData.hasKeyword; // 是否有关键词
- this.hasScope = !!ssData.hasScope; // 是否带范围
- this.thnType = Number(ssData.thnType || 0); // 缩略图类型
- this.ssPaging = ssData.ssPaging || this.ssPaging; // 翻页信息
- this.rbarFuncList = Array.isArray(ssData.rbarFuncList) ? ssData.rbarFuncList : []; // 右侧栏功能
- this.rbarTabNameList = Array.isArray(ssData.rbarTabNameList) ? ssData.rbarTabNameList : []; // 右侧栏选项卡
-
-
- this.pstatList = Array.isArray(ssData.pstatList) ? ssData.pstatList : []; // 功能说明:个人统计图表配置 by xu 20260114
- // 功能说明:优先使用后端 0 服务字段 rbar 控制右侧栏显隐;若后端缺省未返回 rbar,则临时回退为 3 字段判空规则 by xu 20260123
- if (Object.prototype.hasOwnProperty.call(ssData, "rbar")) {
- this.rbar = !!ssData.rbar;
- this.showRightSidebar = !!ssData.rbar;
- } else {
- this.rbar = null;
- // 功能说明:临时规则——当 rbarTabNameList/rbarFuncList/pstatList 全为空时隐藏右侧栏,把宽度让给左侧列表;后续改为“无 rbarTabNameList && 无图表标记”再隐藏(等后端给图表标记) by xu 20260122
- this.showRightSidebar = (this.rbarTabNameList?.length > 0) || (this.rbarFuncList?.length > 0) || (this.pstatList?.length > 0);
- }
- // 功能说明:如果首页返回预订/服务面板(rbarBook/rbarbook/rbarServ),则卡片主体点击切到 single(单选互斥),否则保持 view(查看) by xu 20260116
- {
- const tabs = Array.isArray(this.rbarTabNameList) ? this.rbarTabNameList : [];
- const norm = (s) => String(s || "").trim().toLowerCase();
- this.hasBookPanel = tabs.some((t) => norm(t) === "rbarbook"); // 功能说明:预订tab存在才回显bookList by xu 20260116
- this.hasServPanel = tabs.some((t) => norm(t) === "rbarserv"); // 功能说明:服务tab存在才回显servList by xu 20260116
- this.hasObjPanel = tabs.some((t) => norm(t) === "rbarobj"); // 功能说明:无 rbarObj 时禁用选中能力 by xu 20260122
- this.cardClickAction = (this.hasObjPanel && (this.hasBookPanel || this.hasServPanel)) ? "single" : "view"; // 功能说明:仅允许选中时才启用单选联动 by xu 20260122
- }
- this.searchButtonConfigCheckId = String(this.form?.management || "99");
- // 用当前表单参数初始化 form(确保 v-model 有值)
- const curParams = this.getSearchFormParams();
- const nextForm = {...this.form, ...curParams};
- this.searchFieldList.forEach((f) => {
- if (f && f.name && !(f.name in nextForm)) nextForm[f.name] = "";
- });
- this.form = nextForm;
- this.searchButtonConfigCheckId = String(this.form?.management || "99");
- // 初始化列表(objList/draftList)
- const nextDraft = Array.isArray(ssData.draftList) ? ssData.draftList.map((r) => this.normalizeObjToCard(r)) : [];
- const nextList = Array.isArray(ssData.objList) ? ssData.objList.map((r) => this.normalizeObjToCard(r)) : [];
- if (this.listConfig) {
- this.listConfig.draftbox = nextDraft;
- this.listConfig.list = nextList;
- }
- // 列表数据更新后,重新探测卡片网格类型
- this.cardGridKind = this.detectCardGridKind();
- // 功能说明:首页列表/右侧栏落地后,按当前容器尺寸更新 autoRowNumPer(不立即重拉;下次列表请求时生效) by xu 20260123
- if (this.$nextTick) this.$nextTick(() => this.scheduleAutoRowNumPer("home"));
- else this.scheduleAutoRowNumPer("home");
- // 初始化右侧栏(选项卡 + 功能)
- const bizIcon = this.ssObjName ? ("ss-sidebar-biz-icon icon-obj-" + this.ssObjName) : "";
- // 功能说明:右侧各 tab header 图标:预订/服务用固定业务图标,其余沿用当前对象图标 by xu 20260123
- const tabIconClassOf = (tabKeyLower) => {
- if (tabKeyLower === "rbarbook") return "ss-sidebar-biz-icon icon-obj-yd";
- if (tabKeyLower === "rbarserv") return "ss-sidebar-biz-icon icon-obj-fw";
- return bizIcon;
- };
- const pickedPanel = this.hasObjPanel ? { // 功能说明:仅 rbarObj 存在才显示“已选”tab by xu 20260122
- type: "list",
- title: "已选",
- count: this.selectedItems.length,
- iconClass: bizIcon,
- closable: true,
- itemLayout: "person",
- items: this.selectedItems,
- mode: "selected",
- itemAction: true,
- onClear: () => this.clearSelectedItems(),
- } : null;
- // 功能说明:面板 key 兼容后端大小写(rbarBook/rbarbook/rbarServ),并移除“对象”tab by xu 20260116
- const titleMap = {rbarry: "人员", rbarbook: "预订", rbarserv: "服务"};
- const tabs = Array.isArray(this.rbarTabNameList) ? this.rbarTabNameList : [];
- const normKey = (s) => String(s || "").trim().toLowerCase();
- const tabPanels = tabs.map((k) => {
- const nk = normKey(k);
- // 功能说明:右侧栏去掉“对象”tab(即使后端返回 rbarObj 也忽略) by xu 20260116
- if (nk === "rbarobj") return null;
- const p = {
- type: "list",
- _tabKey: nk, // 功能说明:保存原始 tab key(用于后续写回 items) by xu 20260114
- title: titleMap[nk] || k,
- iconClass: tabIconClassOf(nk),
- items: [],
- itemAction: false,
- };
- // 功能说明:右侧“预订/服务”面板复用“已选”tab 的 person 布局(title+meta),避免渲染 ss-sidebar-tag by xu 20260116
- if (nk === "rbarbook") {
- p.itemLayout = "person";
- }
- if (nk === "rbarserv") {
- p.itemLayout = "simple"; // 功能说明:服务面板只回显 mc(名称),不需要 meta 槽位 by xu 20260116
- }
- // 功能说明:右侧“人员”面板先做前端 mock 搜索头(仅展示,不接后端) by xu 20260114
- if (nk === "rbarry") {
- p.iconClass = "ss-sidebar-biz-icon icon-obj-ry"; // 功能说明:右侧“人员”tab 使用右侧栏 biz 图标基类 by xu 20260123
- p.count = 0;
- p.headerFilters = [
- {
- key: "keyword",
- component: "ss-search-input",
- props: {name: "keyword", placeholder: "人员", width: "140px"},
- value: "",
- },
- ];
- p.headerSearchButton = true;
- p.onSearch = function (payload) {
- try {
- console.log("人员搜索", payload);
- } catch (_) {
- }
- };
- p.mode = "selected";
- p.items = [];
- p.itemAction = false;
- }
- return p;
- }).filter(Boolean);
- this.sidebarPanels = pickedPanel ? [pickedPanel, ...tabPanels] : tabPanels; // 功能说明:无 rbarObj 时不注入“已选”tab by xu 20260122
- try {
- console.log("[pobj] sidebarPanels(init)", (this.sidebarPanels || []).map((p) => ({
- title: p?.title,
- type: p?.type,
- tab: p?._tabKey
- })));
- } catch (_) {
- } // 功能说明:打印初始化后的面板列表,排查缺少“对象/预订”tab by xu 20260116
- // 功能说明:将 pstatList=51 聚拢为一个“报表”面板(type=report-table),用于右侧报表区 by xu 20260115
- try {
- const reportList = (Array.isArray(this.pstatList) ? this.pstatList : []).filter((it) => Number(it?.grtjlbm) === 51);
- if (reportList.length) {
- this.sidebarPanels = this.sidebarPanels.concat([{
- type: "report-table",
- title: "报表",
- iconClass: bizIcon,
- items: reportList,
- // 功能说明:点击统计表单元格,优先按 fwm/bjm 组装后端 service 并弹窗 by xu 20260115
- onOpen: (srv) => this.openServiceDialog(srv),
- }]);
- }
- } catch (e) {
- }
- // 功能说明:将 pstatList=1 映射为右侧图表区(type=chart),并调 wrPchtPlay 拉取真实数据 by xu 20260114
- const chartPanels = this.buildPstatChartPanels(bizIcon);
- if (chartPanels.length) {
- this.sidebarPanels = this.sidebarPanels.concat(chartPanels);
- chartPanels.forEach((p) => this.loadPstatChartData(p));
- // 功能说明:追加 chartPanels 后触发一次 resize,避免首次进入图表为灰色圆(布局未完成) by xu 20260114
- try {
- setTimeout(function () {
- window.dispatchEvent(new Event("resize"));
- }, 0);
- } catch (_) {
- }
- }
- this.sidebarButtons = this.rbarFuncList.map((f) => ({
- id: f.id || f.servName,
- text: f.desc || f.title || "",
- onClick: () => this.openServiceDialog(f),
- }));
- if (!this.showRightSidebar) { // 功能说明:右侧栏隐藏时清空面板/按钮,避免残留旧数据误导(临时规则) by xu 20260122
- this.sidebarPanels = [];
- this.sidebarButtons = [];
- }
- },
- handleRootFuncClick(func) {
- this.openServiceDialog(func);
- },
- // 切换范围(所有/管理/创建/已办/停用)=> 刷新列表(不刷新右侧栏)
- switchScope(management) {
- if (this.loadingList) return;
- this.form.management = String(management);
- this.searchButtonConfigCheckId = String(management);
- this.ssPaging.pageNo = 1;
- this.userInteracted = true;
- this.loadPobjList();
- },
- // 提取当前表单参数(用于 ajax 调用 /service?ssServ=...)
- getSearchFormParams() {
- const params = {};
- try {
- const arr = $("#myForm").serializeArray();
- arr.forEach(({name, value}) => {
- if (!name) return;
- if (name === "pageNo" || name === "rowNumPer" || name === "rowNum") return; // 功能说明:分页参数不再从表单序列化读取(完全由 ssPaging 驱动) by xu 20260123
- if (Object.prototype.hasOwnProperty.call(params, name)) {
- const cur = params[name];
- params[name] = Array.isArray(cur) ? cur.concat([value]) : [cur, value];
- } else {
- params[name] = value;
- }
- });
- } catch (e) {
- console.error("getSearchFormParams failed", e);
- }
- return params;
- },
- // 以 Vue data 为准,组装列表/搜索请求参数(避免依赖组件内部 input 的实现)
- getPobjParams() {
- const params = {};
- const form = this.form || {};
- Object.keys(form).forEach((k) => {
- const v = form[k];
- if (v === undefined || v === null) return;
- if (typeof v === "object") return;
- params[k] = v;
- });
- if (this.ssPaging) {
- params.pageNo = this.ssPaging.pageNo;
- params.rowNumPer = this.ssPaging.rowNumPer;
- params.rowNum = this.ssPaging.rowNum;
- }
- return params;
- },
- // 通用:调用后端 service(统一返回原生 Promise,避免 jqXHR 不支持 finally/catch)
- callSsService(ssServ, extraParams = {}) {
- if (!ssServ) return Promise.resolve(null);
- return new Promise((resolve) => {
-
- $.ajax({
- type: "get",
- url: "/service",
- data: {ssServ, ...extraParams},
- dataType: "text",
- })
- .done((text) => {
- if (typeof text !== "string") return resolve(text);
- const t = text.trim();
- if (!t) return resolve(null);
- try {
- return resolve(JSON.parse(t));
- } catch (_) {
- return resolve(text);
- }
- })
- .fail((xhr, status, err) => {
- console.error("callSsService failed", ssServ, status, err);
- resolve(null);
- });
- });
- },
- // 初始化:拉取查询首页数据(搜索条件/初始列表/右侧面板等)
- loadPobjHome() {
- const ssServ = this.ssSearchPobjHomeServName;
- if (!ssServ) return Promise.resolve(null);
- if (this.loadingHome) return Promise.resolve(null);
- const startedAt = Date.now();
- this.loadingHome = true;
- this.homeReady = false;
- this.userInteracted = false;
- const params = this.getPobjParams();
- // 功能说明:打印首屏请求参数,确认 rowNumPer 是否在第一个接口前写入 by xu 20260123
- try { console.log("[objList][自适应分页] 首页请求参数", { ssServ, pageNo: params?.pageNo, rowNumPer: params?.rowNumPer, rowNum: params?.rowNum, autoRowNumPer: this.autoRowNumPer }); } catch (_) {}
- return this.callSsService(ssServ, params)
- .then((res) => {
- const payload = res && typeof res === "object" ? (res.ssData || res) : null;
- this.applyPobjHomeData(payload);
- console.log("[pobj] home", ssServ, res);
- return res;
- })
- .finally(() => {
- const delay = Math.max(0, 200 - (Date.now() - startedAt));
- setTimeout(() => {
- this.loadingHome = false;
- }, delay);
- });
- },
- applyPobjListData(ssData) {
- if (!ssData || typeof ssData !== "object") return;
- // thnType(若列表接口也返回,则更新;一般由首页接口返回)
- if (ssData.thnType != null) this.thnType = Number(ssData.thnType || 0);
- if (ssData.ssPaging) {
- this.ssPaging = {
- ...this.ssPaging,
- ...ssData.ssPaging,
- pageNo: Number(ssData.ssPaging.pageNo || 1),
- rowNumPer: Number(this.ssPaging?.rowNumPer || ssData.ssPaging.rowNumPer || 0), // 功能说明:rowNumPer 由 loadPobjList 按 autoRowNumPer 统一生效;这里只在当前为空时回退后端值 by xu 20260123
- rowNum: Number(ssData.ssPaging.rowNum || 0),
- };
- }
- const nextList = Array.isArray(ssData.objList) ? ssData.objList.map((r) => this.normalizeObjToCard(r)) : [];
- if (this.listConfig) this.listConfig.list = nextList;
- this.cardGridKind = this.detectCardGridKind();
- // 功能说明:列表渲染数据更新后,按当前容器尺寸更新 autoRowNumPer(不立即重拉;下次列表请求时生效) by xu 20260123
- if (this.$nextTick) this.$nextTick(() => this.scheduleAutoRowNumPer("list"));
- else this.scheduleAutoRowNumPer("list");
- },
- // 功能说明:将 <对象名>_cxycl 返回的 bookList 写回右侧预订面板 by xu 20260114
- applyPobjYclData(ssData) {
- // 功能说明:兼容返回结构(ssData 可能是整包 {ssCode,ssData} 或直接 ssData),避免“预订tab不回显” by xu 20260116
- if (!ssData) return;
- const data = (ssData && typeof ssData === "object" && ssData.ssData && typeof ssData.ssData === "object") ? ssData.ssData : ssData;
- if (!data || typeof data !== "object") return;
- const ensurePanel = (tabKeyLower, title) => {
- // 功能说明:严格依赖首页 rbarTabNameList 决定是否允许创建面板:未返回 rbarServ 时即使 cxycl 有 servList 也不处理 by xu 20260116
- if (tabKeyLower === "rbarbook" && !this.hasBookPanel) return null;
- if (tabKeyLower === "rbarserv" && !this.hasServPanel) return null;
- const panels = Array.isArray(this.sidebarPanels) ? this.sidebarPanels : [];
- let p = panels.find((it) => String(it?._tabKey || "").toLowerCase() === tabKeyLower);
- if (p) return p;
- const idx = panels.findIndex((it) => it?.type === "chart");
- const insertAt = idx >= 0 ? idx : panels.length;
- p = {
- type: "list",
- _tabKey: tabKeyLower,
- title,
- // 功能说明:预订/服务面板 iconClass 与首页初始化保持一致(避免 cxycl 兜底创建时图标错误) by xu 20260123
- iconClass: (tabKeyLower === "rbarbook")
- ? "ss-sidebar-biz-icon icon-obj-yd"
- : (tabKeyLower === "rbarserv")
- ? "ss-sidebar-biz-icon icon-obj-fw"
- : (this.ssObjName ? ("ss-sidebar-biz-icon icon-obj-" + this.ssObjName) : ""),
- items: [],
- itemAction: false,
- };
- // 功能说明:预订面板默认使用 person 布局(title+meta) by xu 20260116
- if (tabKeyLower === "rbarbook") p.itemLayout = "person";
- if (tabKeyLower === "rbarserv") p.itemLayout = "simple"; // 功能说明:服务面板只回显 mc(名称) by xu 20260116
- panels.splice(insertAt, 0, p);
- this.sidebarPanels = panels;
- return p;
- };
- const fmtDt = (v) => { // 功能说明:JSP 页面避免使用 JS 模板字符串插值(可能被 JSP EL 干扰),统一用字符串拼接 by xu 20260114
- if (v === undefined || v === null || v === "") return "";
- const d = new Date(v);
- if (!isNaN(d.getTime())) {
- const pad2 = (n) => String(n).padStart(2, "0");
- return (
- String(d.getFullYear()) +
- "-" +
- pad2(d.getMonth() + 1) +
- "-" +
- pad2(d.getDate()) +
- " " +
- pad2(d.getHours()) +
- ":" +
- pad2(d.getMinutes())
- );
- }
- return String(v);
- };
- const bookList = Array.isArray(data.bookList) ? data.bookList : [];
- const servList = Array.isArray(data.servList) ? data.servList : []; // 功能说明:将 <对象名>_cxycl 返回的 servList 写回右侧服务面板 by xu 20260116
- try {
- console.log("[pobj] cxycl bookList", {len: bookList.length, bookList: bookList});
- } catch (_) {
- } // 功能说明:打印 cxycl 返回,排查“预订tab不回显” by xu 20260116
- // 功能说明:无论 bookList 是否为空,都要写回面板(为空则清空旧数据),避免“切换对象但仍显示上一次预订人” by xu 20260114
- const bookPanel = ensurePanel("rbarbook", "预订");
- if (bookPanel) {
- bookPanel.itemLayout = "person"; // 功能说明:预订面板复用“已选”tab 的 person 布局(title+中间 meta 槽位),不走 tags by xu 20260114
- bookPanel.items = bookList.map((b) => ({
- _raw: b,
- id: b?.id,
- title: String(b?.ydr ?? ""), // 功能说明:预订面板 title=预订人 by xu 20260114
- meta: String(b?.ydrdh ?? ""), // 功能说明:预订面板中间槽位 meta=电话(同“已选”tab) by xu 20260114
- tags: [], // 功能说明:预订面板不使用 tags(避免自定义标签样式) by xu 20260114
- }));
- bookPanel.count = bookPanel.items.length;
- }
- // 功能说明:服务面板按预订(book)回显逻辑写回(title+meta),为空也要清空 by xu 20260116
- const servPanel = ensurePanel("rbarserv", "服务");
- if (servPanel) {
- servPanel.itemLayout = "simple"; // 功能说明:服务面板字段只有 id/mc,仅回显 mc(名称) by xu 20260116
- servPanel.items = servList.map((s) => ({
- _raw: s,
- id: s?.id, // 功能说明:服务面板 id=数据库ID by xu 20260116
- title: String(s?.mc ?? ""), // 功能说明:服务面板 title=mc(名称) by xu 20260116
- tags: [],
- }));
- servPanel.count = servPanel.items.length;
- }
- // 功能说明:强制触发 sidebarPanels 更新,避免仅修改子对象导致视图未刷新(预订tab回显不更新) by xu 20260116
- try {
- this.sidebarPanels = (this.sidebarPanels || []).slice();
- } catch (e) {
- }
- try {
- console.log("[pobj] cxycl bookPanel(after)", {
- title: bookPanel.title,
- tab: bookPanel._tabKey,
- count: bookPanel.count,
- first: bookPanel.items?.[0],
- });
- } catch (_) {
- } // 功能说明:打印写回后的面板状态,排查“预订tab不回显” by xu 20260116
- },
- // 功能说明:选中对象后联动调用 <对象名>_cxycl,填充右侧预订/服务面板 by xu 20260114
- loadPobjYclByItem(item) {
- if (!(this.hasBookPanel || this.hasServPanel)) return Promise.resolve(null); // 功能说明:预订/服务任一存在才调用 cxycl;否则不联动 by xu 20260116
- const objNum = item?.objNum || item?._raw?.ssObjId || item?._raw?.ssObjId;
- if (!objNum) return Promise.resolve(null);
- const ssServ = this.ssObjName ? (String(this.ssObjName) + "_cxycl") : "";
- if (!ssServ) return Promise.resolve(null);
- const reqId = (this.__yclReqId = (this.__yclReqId || 0) + 1);
- this.yclLoading = true;
- // 功能说明:cxycl 需要同时传入 ssObjIdName=<对象名>id,且把对象ID用该字段名提交(如 cdid=90052) by xu 20260114
- const ssObjIdName = String(this.ssObjName || "") + "id";
- const params = {ssObjName: this.ssObjName, ssObjIdName: ssObjIdName, ssObjId: objNum};
- params[ssObjIdName] = objNum;
- return this.callSsService(ssServ, params)
- .then((res) => {
- if (reqId !== this.__yclReqId) return null;
- // 功能说明:兼容 $.ajax/axios 等不同返回结构,避免取错层级导致 bookList 为空 by xu 20260116
- const payload = res && typeof res === "object" ? (res.ssData || res?.data?.ssData || res?.data || res) : null;
- this.yclResp = payload;
- this.applyPobjYclData(payload);
- return res;
- })
- .finally(() => {
- if (reqId !== this.__yclReqId) return;
- this.yclLoading = false;
- });
- },
- // 拉取列表数据(分页/搜索等)
- loadPobjList() {
- const ssServ = this.ssSearchPobjListServName;
- if (!ssServ) return Promise.resolve(null);
- // 只有用户操作后才允许请求列表(避免初始化阶段出现第二个 loading)
- if (!this.userInteracted) return Promise.resolve(null);
- if (this.loadingList) return Promise.resolve(null);
- // 功能说明:rowNumPer 按 autoRowNumPer 生效(resize 只记录,不立即请求;下次接口请求时才更新) by xu 20260123
- try {
- const next = Number(this.autoRowNumPer || 0);
- const cur = Number(this.ssPaging?.rowNumPer || 0);
- if (next > 0 && next !== cur) {
- try { console.log("[objList][自适应分页] 列表请求前应用 rowNumPer", { cur, next, pageNo: this.ssPaging?.pageNo }); } catch (_) {}
- this.ssPaging.rowNumPer = next;
- // 功能说明:页大小变化时回到第一页,避免页码与数据范围不一致 by xu 20260123
- this.ssPaging.pageNo = 1;
- }
- } catch (_) {
- }
- const startedAt = Date.now();
- this.loadingList = true;
- // 点击后立即清空列表区域,避免用户误以为没点到
- if (this.listConfig) {
- this.listConfig.draftbox = [];
- this.listConfig.list = [];
- }
- const params = this.getPobjParams();
- return this.callSsService(ssServ, params)
- .then((res) => {
- const payload = res && typeof res === "object" ? (res.ssData || res) : null;
- this.ssListResp = payload;
- this.applyPobjListData(payload);
- console.log("[pobj] list", ssServ, res);
- return res;
- })
- .finally(() => {
- const delay = Math.max(0, 200 - (Date.now() - startedAt));
- setTimeout(() => {
- this.loadingList = false;
- }, delay);
- });
- },
- // 初始化右侧边栏分区(已选 items 指向 selectedItems) by xu 20260113
- initSidebarPanels() {
- const bizIcon = this.ssObjName ? ('ss-sidebar-biz-icon icon-obj-' + this.ssObjName) : ''; // 功能说明:右侧栏 iconClass 使用右侧栏 biz 图标基类 by xu 20260123
- // 初始只放“已选”,其他分区由首页接口(rbarTabNameList)初始化
- this.sidebarPanels = [];
- this.sidebarButtons = [];
- },
- // 更新已选分区计数 by xu 20260113
- updatePickedPanelCount() {
- const pickedPanel = this.sidebarPanels?.find?.((p) => p?.title === '已选');
- if (pickedPanel) pickedPanel.count = this.selectedItems.length;
- },
- // 清空已选(右侧 header 清空按钮) by xu 20260113
- clearSelectedItems() {
- // 功能说明:按稳定 key 反向同步当前列表卡片选中态(避免列表刷新后对象引用变化导致取消选中失效) by xu 20260122
- const keyOf = (it) => String(it?.objNum || it?.ssObjId || it?._raw?.ssObjId || it?._raw?.objNum || "");
- const keys = new Set((this.selectedItems || []).map(keyOf).filter(Boolean));
- const all = [].concat(this.listConfig?.draftbox || []).concat(this.listConfig?.list || []);
- all.forEach((it) => {
- const k = keyOf(it);
- if (!k) return;
- if (keys.has(k)) it._ssSelected = false;
- });
- this.selectedItems.forEach((it) => {
- if (it) it._ssSelected = false;
- });
- this.selectedItems.splice(0, this.selectedItems.length);
- this.updatePickedPanelCount();
- // 功能说明:强制触发 sidebarPanels 更新,避免 count 未刷新 by xu 20260122
- try { this.sidebarPanels = (this.sidebarPanels || []).slice(); } catch (_) {}
- },
- // 右侧边栏移除 -> 反向同步左侧卡片状态 by xu 20260113
- handleSidebarRemove(item) {
- if (!item) return;
- // 功能说明:按稳定 key 移除,避免列表刷新后 selectedItems/indexOf 引用不一致导致 count 不更新/取消失败 by xu 20260122
- const keyOf = (it) => String(it?.objNum || it?.ssObjId || it?._raw?.ssObjId || it?._raw?.objNum || "");
- const key = keyOf(item);
- item._ssSelected = false;
- const idx = key ? this.selectedItems.findIndex((it) => keyOf(it) === key) : this.selectedItems.indexOf(item);
- if (idx > -1) this.selectedItems.splice(idx, 1);
- // 反向同步当前列表里同 key 的卡片状态
- if (key) {
- const all = [].concat(this.listConfig?.draftbox || []).concat(this.listConfig?.list || []);
- const cur = all.find((it) => keyOf(it) === key);
- if (cur) cur._ssSelected = false;
- }
- this.updatePickedPanelCount();
- // 功能说明:强制触发 sidebarPanels 更新,避免 count 未刷新 by xu 20260122
- try { this.sidebarPanels = (this.sidebarPanels || []).slice(); } catch (_) {}
- },
- // 功能说明:动态计算 rowNumPer(让分页视觉上在最后一行出现),以 DOM 实测为准;返回明细用于调试 by xu 20260123
- calcAutoRowNumPerDetail() {
- try {
- // 功能说明:Vue3 根节点可能是 Fragment(this.$el 不是 Element),统一从 #app 下查找内容区 by xu 20260123
- const host = (this.$el && this.$el.nodeType === 1 && this.$el.querySelector)
- ? this.$el
- : (document.getElementById && document.getElementById("app")) || document.body;
- const area = host && host.querySelector ? host.querySelector(".item-content-area") : null;
- if (!area) return { err: "no_area" };
- const cs = window.getComputedStyle ? window.getComputedStyle(area) : null;
- // 功能说明:内容区宽度以 item-content-area 为准;若右侧栏与内容区发生覆盖(非并排布局),按重叠宽度扣减 by xu 20260123
- let W = Math.max(0, area.clientWidth || 0);
- let overlap = 0;
- try {
- if (this.showRightSidebar) {
- const sidebar = host && host.querySelector ? host.querySelector(".ss-sidebar") : null;
- if (sidebar && area.getBoundingClientRect && sidebar.getBoundingClientRect) {
- const ar = area.getBoundingClientRect();
- const sr = sidebar.getBoundingClientRect();
- overlap = Math.max(0, Math.min(ar.right, sr.right) - Math.max(ar.left, sr.left));
- if (overlap > 0) W = Math.max(0, W - overlap);
- }
- }
- } catch (_) {
- }
- const H = Math.max(0, area.clientHeight || 0);
- if (!W || !H) {
- // 功能说明:首屏 homeReady=false 时容器 display:none,W/H 为 0;此处回退用 viewport 估算 by xu 20260123
- const vw = Math.max(0, document.documentElement?.clientWidth || window.innerWidth || 0);
- const vhRaw = Math.max(0, document.documentElement?.clientHeight || window.innerHeight || 0);
- // 功能说明:首屏高度按 URL 上的 removeHigh 扣减(弹窗/iframe 会预留顶部/底部区域),否则 rows 会偏大 by xu 20260123
- let removeHigh = 0;
- try {
- const qs = window.location && window.location.search ? window.location.search : "";
- const m = String(qs).match(/(?:\\?|&)removeHigh=([^&]+)/);
- if (m && m[1] != null) {
- const v = parseFloat(decodeURIComponent(m[1]));
- if (!isNaN(v) && v > 0) removeHigh = v;
- }
- } catch (_) {
- }
- const vh = Math.max(0, vhRaw - removeHigh);
- // 功能说明:右侧栏宽度用 rbar 判定(JSP 注入已写入 this.rbar;首页接口返回后会覆盖) by xu 20260123
- const rbarFlag = (this.rbar === true);
- const sidebarW = rbarFlag ? 380 : 0;
- const minW = (Number(this.thnType || 0) === 1) ? 400 : (Number(this.thnType || 0) === 2) ? 320 : 300;
- const gap = 20;
- const padLeft = 20;
- const padRight = 20;
- const padTop = 0;
- const padBottom = 100;
- const pagerH = 32; // 功能说明:分页高度以 .pager-container .pager-content 的 32px 为准(首屏未渲染时用该值预留) by xu 20260123
- let cardH = Number(this.__autoCardH || 0) || Number(this.measureAutoCardHeight?.() || 0);
- if (cardH) this.__autoCardH = cardH;
- if (!vw || !vh || !cardH) return { err: "zero_size", W, H, overlap, vw, vh, cardH, showRightSidebar: !!this.showRightSidebar };
- const Wcontent = Math.max(0, (vw - sidebarW) - padLeft - padRight);
- const cols = Math.max(1, Math.floor((Wcontent + gap) / (minW + gap)));
- const availH = Math.max(0, (vh - 75) - padTop - padBottom - pagerH); // 75=search/预留(与 CSS 对齐) by xu 20260123
- const rows = Math.max(1, Math.floor((availH + gap) / (cardH + gap)));
- const n = Math.max(1, cols * rows);
- return { n, cols, rows, W: 0, H: 0, overlap, gap, padTop, padBottom, padLeft, padRight, minW, pagerH, cardH, vw, vhRaw, vh, removeHigh, sidebarW, rbarFlag, fallback: "viewport" };
- }
- const gap = cs ? (parseFloat(cs.gap) || 0) : 0;
- const padLeft = cs ? (parseFloat(cs.paddingLeft) || 0) : 0;
- const padRight = cs ? (parseFloat(cs.paddingRight) || 0) : 0;
- const padTop = cs ? (parseFloat(cs.paddingTop) || 0) : 0;
- const padBottom = cs ? (parseFloat(cs.paddingBottom) || 0) : 0;
- const minW = cs ? (parseFloat(cs.getPropertyValue("--ss-card-min")) || 300) : 300;
- const Wcontent = Math.max(0, W - padLeft - padRight);
- const cols = Math.max(1, Math.floor((Wcontent + gap) / (minW + gap)));
- const pagerEl = area.querySelector ? (area.querySelector(".pager-bar, .pager-container") || null) : null;
- const pagerH = pagerEl && pagerEl.getBoundingClientRect ? (pagerEl.getBoundingClientRect().height || 0) : 0;
- const cardEl = area.querySelector ? (area.querySelector(".knowledge-item-container") || null) : null;
- let cardH = cardEl && cardEl.getBoundingClientRect ? (cardEl.getBoundingClientRect().height || 0) : 0;
- if (!cardH) {
- // 功能说明:首屏/空列表时通过 probe 实测卡片高度并缓存,避免 rowNumPer 只能等列表接口返回后才可计算 by xu 20260123
- cardH = Number(this.__autoCardH || 0) || Number(this.measureAutoCardHeight?.() || 0);
- if (cardH) this.__autoCardH = cardH;
- }
- if (!cardH) return { err: "no_card_height", W, H, overlap, showRightSidebar: !!this.showRightSidebar };
- const availH = Math.max(0, H - padTop - padBottom - pagerH);
- const rows = Math.max(1, Math.floor((availH + gap) / (cardH + gap)));
- const n = Math.max(1, cols * rows);
- return { n, cols, rows, W, H, overlap, gap, padTop, padBottom, padLeft, padRight, minW, pagerH, cardH, showRightSidebar: !!this.showRightSidebar };
- } catch (_) {
- return { err: "exception" };
- }
- },
- // 功能说明:仅返回 rowNumPer 数值(兼容旧调用点) by xu 20260123
- calcAutoRowNumPer() {
- const d = this.calcAutoRowNumPerDetail?.();
- return d && d.n ? d.n : 0;
- },
- measureAutoCardHeight() { // 功能说明:用 DOM probe 实测 ss-list-card 的稳定高度(不依赖接口数据) by xu 20260123
- try {
- const probe = document.createElement("div");
- const kind = Number(this.thnType || 0) === 1 ? "card-thumbnail" : "card-photo";
- probe.className = "knowledge-item-container " + kind;
- probe.style.position = "fixed";
- probe.style.left = "-99999px";
- probe.style.top = "0";
- probe.style.visibility = "hidden";
- probe.style.pointerEvents = "none";
- probe.style.zIndex = "-1";
- // 让 probe 按最小列宽测量(min-width 对高度几乎无影响,但保持一致) by xu 20260123
- try {
- // 功能说明:同 calcAutoRowNumPerDetail,优先从 #app 下取 item-content-area by xu 20260123
- const host = (this.$el && this.$el.nodeType === 1 && this.$el.querySelector)
- ? this.$el
- : (document.getElementById && document.getElementById("app")) || document.body;
- const area = host && host.querySelector ? host.querySelector(".item-content-area") : null;
- const cs = area && window.getComputedStyle ? window.getComputedStyle(area) : null;
- const minW = cs ? (parseFloat(cs.getPropertyValue("--ss-card-min")) || 300) : 300;
- probe.style.width = String(minW) + "px";
- } catch (_) {
- }
- probe.innerHTML =
- '<div class="body">' +
- ' <div class="box-header"><div>_</div></div>' +
- ' <div class="box-body">' +
- ' <div class="left ss-objlist-thumbPlaceholder"><div class="ss-objlist-thumbIcon">_</div></div>' +
- ' <div class="right"></div>' +
- " </div>" +
- "</div>";
- document.body.appendChild(probe);
- const h = probe.getBoundingClientRect ? (probe.getBoundingClientRect().height || 0) : 0;
- document.body.removeChild(probe);
- return Math.max(0, h);
- } catch (_) {
- return 0;
- }
- },
- // 功能说明:延迟/去抖触发自动 rowNumPer 计算(避免 resize 频繁触发请求) by xu 20260123
- scheduleAutoRowNumPer(reason) {
- if (this.__autoRowNumTimer) clearTimeout(this.__autoRowNumTimer);
- this.__autoRowNumTimer = setTimeout(() => {
- this.__autoRowNumTimer = null;
- this.applyAutoRowNumPer(reason || "");
- }, 150);
- },
- // 功能说明:应用自动 rowNumPer;只记录到 data(下次接口请求时生效),resize 不立即重拉 by xu 20260123
- applyAutoRowNumPer(reason) {
- const d = this.calcAutoRowNumPerDetail?.();
- const next = Number(d?.n || 0);
- if (!next) return;
- const cur = Number(this.autoRowNumPer || 0);
- if (cur === next) return;
- this.autoRowNumPer = next;
- // 功能说明:打印计算明细,便于排查“为什么还是12” by xu 20260123
- try { console.log("[objList][自适应分页] 计算更新", { reason, cur, next, detail: d }); } catch (_) {}
- },
- // 功能:探测当前业务列表卡片类型,用于网格列最小宽度 by xu 20260109
- detectCardGridKind() {
- const all = [].concat(this.listConfig?.draftbox || []).concat(this.listConfig?.list || []);
- if (all.some(it => String(it?.thumbType || '').trim() === 'thumbnail')) return 'thumbnail';
- if (all.some(it => !!it?.thumb || !!it?.thumbType)) return 'photo';
- return 'none';
- },
- // 左侧卡片角标选中(多选)+ 卡片主体单选互斥(exclusive) by xu 20260109
- handleToggleSelect({item, selected, exclusive}) {
- if (!item) return;
- if (!this.hasObjPanel) return; // 功能说明:无 rbarObj 时禁止选中(已选tab也不显示) by xu 20260122
- // 已选分区:中间槽位 meta(可按业务改为后端字段) by xu 20260113
- if (!item.meta) item.meta = String(item.objNum || '');
- // 功能说明:按稳定 key 管理 selectedItems,避免列表刷新后对象引用变化导致取消选中/计数异常 by xu 20260122
- const keyOf = (it) => String(it?.objNum || it?.ssObjId || it?._raw?.ssObjId || it?._raw?.objNum || "");
- const key = keyOf(item);
- const idx = key ? this.selectedItems.findIndex((it) => keyOf(it) === key) : this.selectedItems.indexOf(item);
- if (exclusive && selected && this.cardClickAction === 'single') {
- this.selectedItems.forEach((it) => {
- if (it && it !== item) it._ssSelected = false;
- });
- // 不要重新赋值数组,保留引用(供右侧面板使用) by xu 20260109
- this.selectedItems.splice(0, this.selectedItems.length);
- this.selectedItems.push(item);
- this.updatePickedPanelCount();
- // 功能说明:预订模式下,单选互斥选中后联动加载右侧预订/服务数据 by xu 20260114
- this.loadPobjYclByItem(item);
- // 功能说明:强制触发 sidebarPanels 更新,避免 count 未刷新 by xu 20260122
- try { this.sidebarPanels = (this.sidebarPanels || []).slice(); } catch (_) {}
- return;
- }
- if (selected) {
- if (idx === -1) {
- this.selectedItems.push(item);
- } else if (idx > -1 && this.selectedItems[idx] !== item) {
- // 功能说明:用最新的 item 对象替换旧引用,确保侧边栏操作能反向同步到当前卡片 by xu 20260122
- this.selectedItems.splice(idx, 1, item);
- }
- // 功能说明:预订模式下,多选角标选中也可触发联动(以最近选中项为准) by xu 20260114
- this.loadPobjYclByItem(item);
- } else {
- if (idx > -1) this.selectedItems.splice(idx, 1);
- }
- this.updatePickedPanelCount();
- // 功能说明:强制触发 sidebarPanels 更新,避免 count 未刷新 by xu 20260122
- try { this.sidebarPanels = (this.sidebarPanels || []).slice(); } catch (_) {}
- },
- handlePageChange({pageNo, rowNumPer, rowNum}) {
- // 翻页:调用列表接口刷新(不刷新右侧栏)
- if (this.loadingList) return;
- this.ssPaging.pageNo = pageNo;
- this.ssPaging.rowNumPer = rowNumPer;
- this.ssPaging.rowNum = rowNum;
- this.userInteracted = true;
- this.loadPobjList();
- },
- search() {
- // 搜索:回到第一页,调用列表接口刷新(不刷新右侧栏)
- if (this.loadingList) return;
- this.ssPaging.pageNo = 1;
- this.userInteracted = true;
- this.loadPobjList();
- }
- },
- mounted() {
- // 功能说明:临时将 vm 暴露到 window,供 searchButtonConfig.opt.callback 调用(后续再改为事件回传方式) by xu 20260123
- try { window.__objListVm = this; } catch (_) {}
- // 功能说明:弹窗保存后只刷新列表数据(走 Ajax),禁止走 form.submit/reload 以免跳到 /service 返回内容导致白屏 by xu 20260202
- try {
- const vm = this;
- window.wdRefresh = function () {
- try {
- if (vm.loadingList) return;
- // 保持当前筛选/页码,仅刷新列表数据
- vm.userInteracted = true;
- vm.loadPobjList();
- } catch (e) {
- console.error("[objList] wdRefresh failed", e);
- }
- };
- } catch (_) {}
- // 初始化:把 URL/form 上已有的参数合并到 form/ssPaging(支持回显 + ajax)
- const curParams = this.getSearchFormParams();
- this.form = {...this.form, ...curParams};
- // 功能说明:分页参数不再从表单读取/初始化(完全由 ssPaging 驱动,且页面不再输出分页隐藏域) by xu 20260123
- this.searchButtonConfigCheckId = String(this.form?.management || "99");
- // 功能说明:首屏在列表数据未到达前,按 thnType 预设 cardGridKind(决定 --ss-card-min),避免 detectCardGridKind 在空列表时把缩略图场景误判为 none by xu 20260123
- try {
- const t = Number(this.thnType || 0);
- if (t === 1) this.cardGridKind = "thumbnail";
- else if (t === 2) this.cardGridKind = "photo";
- else this.cardGridKind = "none";
- } catch (_) {
- this.cardGridKind = "none";
- }
- // 初始化右侧边栏分区 by xu 20260113
- this.initSidebarPanels();
- // 功能说明:订阅系统编辑模式变化(来自 home 顶部按钮/菜单切换),用于业务页右下角“新增”入口显隐 by xu 20260123
- try {
- const bus = window.parent?.sharedEventBus;
- if (bus?.subscribe) {
- const applySysMode = (payload) => {
- const mode = (payload && typeof payload === "object") ? payload.mode : payload;
- const source = (payload && typeof payload === "object") ? payload.source : "unknown";
- this.isSysEditMode = (mode === "edit");
- this.sysModeSource = String(source || "unknown");
- };
- this.__sysEditSub = bus.subscribe("systemEditModelChange", applySysMode);
- try { applySysMode(bus.getState?.("systemEditModelChange")); } catch (_) {}
- }
- } catch (_) {}
- this.userInteracted = false;
- // 功能说明:首屏在第一个接口调用前,按当前容器尺寸预先计算 rowNumPer,并写入 ssPaging(使首页接口直接返回“刚好铺满”的条数) by xu 20260123
- const startHome = () => {
- // 功能说明:首屏以 JSP 注入的 rbar 作为右侧栏存在与否的唯一依据 by xu 20260123
- try { this.showRightSidebar = (this.rbar === true); } catch (_) {}
- // 功能说明:打印首屏关键输入(thnType/cardGridKind/rbar),排查“有缩略图首屏 rowNumPer 偏大” by xu 20260123
- try { console.log("[objList][自适应分页] 首屏输入", { thnType: this.thnType, cardGridKind: this.cardGridKind, rbar: this.rbar, showRightSidebar: this.showRightSidebar }); } catch (_) {}
- // 功能说明:首屏首次渲染时容器可能尚未有尺寸(W/H=0),用 rAF 重试几帧再发第一个接口,确保 rowNumPer 能在首屏生效 by xu 20260123
- let tries = 0;
- const maxTries = 20;
- const tick = () => {
- let d = null;
- try { d = this.calcAutoRowNumPerDetail?.(); } catch (_) {}
- if (tries === 0 || tries === maxTries - 1) {
- try { console.log("[objList][自适应分页] 首屏计算", { tries, detail: d }); } catch (_) {}
- }
- const n = Number(d?.n || 0);
- if (n > 0) {
- this.autoRowNumPer = n;
- this.ssPaging.rowNumPer = n;
- this.ssPaging.pageNo = 1;
- try { console.log("[objList][自适应分页] 首屏应用 rowNumPer", { rowNumPer: n, ssPaging: this.ssPaging, fallback: d?.fallback }); } catch (_) {}
- this.loadPobjHome();
- return;
- }
- tries += 1;
- if (tries >= maxTries) {
- // 达到重试上限仍无法计算,回退按当前 ssPaging.rowNumPer 发请求 by xu 20260123
- try { console.log("[objList][自适应分页] 首屏计算放弃", { ssPaging: this.ssPaging, autoRowNumPer: this.autoRowNumPer, last: d }); } catch (_) {}
- this.loadPobjHome();
- return;
- }
- requestAnimationFrame(tick);
- };
- requestAnimationFrame(tick);
- };
- if (this.$nextTick) this.$nextTick(() => startHome());
- else startHome();
- // 功能说明:监听窗口变化,触发 rowNumPer 自动计算(resize 只记录,下次接口请求时生效) by xu 20260123
- this.__autoRowNumResizeHandler = () => this.scheduleAutoRowNumPer("resize");
- window.addEventListener && window.addEventListener("resize", this.__autoRowNumResizeHandler);
- }
- ,beforeUnmount() {
- // 功能说明:清理 resize 监听与 timer,避免页面切换残留 by xu 20260123
- // 功能说明:清理 window.__objListVm(避免切页后回调指向旧 vm) by xu 20260123
- try { if (window.__objListVm === this) window.__objListVm = null; } catch (_) {}
- if (this.__autoRowNumResizeHandler && window.removeEventListener) {
- window.removeEventListener("resize", this.__autoRowNumResizeHandler);
- this.__autoRowNumResizeHandler = null;
- }
- if (this.__autoRowNumTimer) {
- clearTimeout(this.__autoRowNumTimer);
- this.__autoRowNumTimer = null;
- }
- // 功能说明:解绑系统编辑态订阅,避免多次进入页面累积回调 by xu 20260123
- try { this.__sysEditSub?.unSubscribe?.(); } catch (_) {}
- this.__sysEditSub = null;
- }
- });
- });
- <%-- 原有的清令牌 --%>
- tokenCleanser("<ss:serv name='ss.clearPageToken'/>", {tokenList: "<%= pageContext.getAttribute(ss.page.PageC.PAGE_tokenList)%>"});
- </script>
|