objList.jsp 120 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128
  1. <%@ taglib prefix="ss" uri="/ssTag" %>
  2. <%@ page language="java" pageEncoding="UTF-8" isELIgnored="false" %>
  3. <%@ taglib uri="/ssTag" prefix="ss" %>
  4. <%pageContext.setAttribute("wdpageinformation", "{'hastab':'0'}");%>
  5. <!DOCTYPE html>
  6. <html>
  7. <head>
  8. <meta http-equiv="pragma" content="no-cache">
  9. <meta http-equiv="cache-control" content="no-cache">
  10. <meta http-equiv="expires" content="0">
  11. <script>window.loginStatus = "${empty sessionScope['ssUser']?'0':'1'}"</script>
  12. <ss:skin file='main.css'/>
  13. <script type="text/javascript" src="/ss/jquery/jquery.js"></script>
  14. <script type="text/javascript" src="/ss/artdialog/artDialogUtil.js"></script>
  15. <script type="text/javascript" src="/ss/js/base.js"></script>
  16. <script> if (!window.wd) var wd = {};
  17. if (!wd.display) wd.display = {};
  18. wd.display.wdDialogId = "objList";
  19. if (!wd.app) wd.app = {};
  20. wd.app.name = 'pms';</script>
  21. <script type="text/javascript" src="/ss/js/masklayer.js"></script>
  22. <script type="text/javascript" src="/ss/js/wdDialogInit.js"></script>
  23. <script type="text/javascript" src="/ss/js/common.js"></script>
  24. <script type="text/javascript" src="/ss/js/display.js"></script>
  25. <script type="text/javascript" src="/ss/js/edit.js"></script>
  26. <script type="text/javascript" src="/ss/nicescroll/jquery.nicescroll.js"></script>
  27. <script type="text/javascript" src="/ss/nicescroll/jquery.nicescroll.iframehelper.min.js"></script>
  28. <link rel="stylesheet" type="text/css" href="/ss/window/theme/dhtmlxwindows.css">
  29. <link rel="stylesheet" type="text/css" href="/ss/window/theme/dhx_blue/dhtmlxwindows_dhx_blue.css">
  30. <script type="text/javascript" src="/ss/window/dhtmlxcommon.js"></script>
  31. <script type="text/javascript" src="/ss/window/dhtmlxwindows.js"></script>
  32. <script type="text/javascript" src="/ss/window/dhtmlxcontainer.js"></script>
  33. <script type="text/javascript" src="/ss/js/common.js"></script>
  34. <script type="text/javascript" src="/ss/js/display.js"></script>
  35. <script type="text/javascript" src="/ss/js/edit.js"></script>
  36. <ssDlg setPval="true"/>
  37. <%-- setValue="true" close="true"。Lin --%>
  38. <script type="text/javascript" src="/ss/jquery/jquery.ellipsis.js"></script>
  39. <%-- 新UI引入的JS --%>
  40. <script src="/js/load.js"></script>
  41. <%-- ="/newUI/ss/js/base.js"。Lin(新UI) --%>
  42. <script src="/js/pageC.js"></script>
  43. <%-- 对象变动、修改、查看页,左则选项卡宽度 在里面定义 Ben(20251213) --%>
  44. <%-- 改为 <data@ss name="prt"/>。Lin
  45. <tab@ss name="print" enable="viewobject.funcMap.play.print"/> --%>
  46. <ss:data name='prt'/><%-- 初始化弹出窗口的打印按钮的后台数据 Ben--%>
  47. <%@include file="/ss/clip/prtIcon.jsp" %>
  48. <%-- 引入初始化弹出窗口的打印按钮的JS Ben--%>
  49. <script>
  50. window.ss.dom.formElemConfig = window.ss.dom.formElemConfig || {};
  51. </script>
  52. <style>
  53. /* 首页 loading:没加载完不显示页面内容 */
  54. .pobj-loading-home {
  55. position: fixed;
  56. left: 50%;
  57. top: 50%;
  58. transform: translate(-50%, -50%);
  59. z-index: 9999;
  60. pointer-events: none;
  61. }
  62. /* 列表 loading:区域性(搜索条件和分页之间) */
  63. .pobj-list-loading {
  64. width: 100%;
  65. min-height: 260px;
  66. display: flex;
  67. align-items: center;
  68. justify-content: center;
  69. }
  70. .pobj-spinner {
  71. width: 18px;
  72. height: 18px;
  73. border-radius: 50%;
  74. border: 2px solid rgba(0, 0, 0, 0.2);
  75. border-top-color: rgba(0, 0, 0, 0.65);
  76. animation: pobjSpin 0.8s linear infinite;
  77. }
  78. @keyframes pobjSpin {
  79. from {
  80. transform: rotate(0deg);
  81. }
  82. to {
  83. transform: rotate(360deg);
  84. }
  85. }
  86. </style>
  87. </head>
  88. <body>
  89. <div id="app">
  90. <div v-show="loadingHome" class="pobj-loading-home" style="display:none;">
  91. <div class="pobj-spinner"></div>
  92. </div>
  93. <%-- 功能说明:主布局容器(左侧表单 + 右侧 ss-sidebar) by xu 20260113 --%>
  94. <div style="display: flex; height: 100%; " v-show="homeReady">
  95. <%-- 功能说明:左侧表单区域在 flex 中占满剩余宽度(避免挤掉右侧 ss-sidebar) by xu 20260113 --%>
  96. <form class="page-container" id="myForm" style="flex: 1; min-width: 0; width: auto;"
  97. action="<ss:serv name='${currentService.service}' dest='${currentService.dest}' parm='${currentService.param}'/>"
  98. method="post">
  99. <input type="hidden" name="isAnd" v-model="form.isAnd"/>
  100. <input name="management" type="hidden" v-model="form.management"/>
  101. <input name="isFulltext" type="hidden" v-model="form.isFulltext"/>
  102. <input type="hidden" name="pageNo" v-model="ssPaging.pageNo"/>
  103. <input type="hidden" name="rowNumPer" v-model="ssPaging.rowNumPer"/>
  104. <input type="hidden" name="rowNum" v-model="ssPaging.rowNum"/>
  105. <%--搜索条件start--%>
  106. <div class="search-bar">
  107. <ss-breadcrumb></ss-breadcrumb>
  108. <div class="search-bar-contaienr">
  109. <template v-if="homeReady">
  110. <template v-for="field in searchFieldList" :key="field.name">
  111. <ss-objp
  112. v-if="field.cbName"
  113. v-model="form[field.name]"
  114. :name="field.name"
  115. :placeholder="field.desc"
  116. width="150px"
  117. inp="true"
  118. url="/service?ssServ=loadObjpOpt&objectpickerdropdown1=1"
  119. :cb="field.cbName"
  120. ></ss-objp>
  121. <ss-search-date-picker
  122. v-else-if="field.type == 11"
  123. v-model="form[field.name]"
  124. :name="field.name"
  125. type="date"
  126. :placeholder="field.desc"
  127. width="140px"
  128. :fmt="field.fmt"
  129. ></ss-search-date-picker>
  130. <ss-search-date-picker
  131. v-else-if="field.type == 3"
  132. v-model="form[field.name]"
  133. :name="field.name"
  134. type="datetime"
  135. :placeholder="field.desc"
  136. width="200px"
  137. :fmt="field.fmt"
  138. ></ss-search-date-picker>
  139. <ss-search-input
  140. v-else
  141. :name="field.name"
  142. :placeholder="field.desc"
  143. v-model="form[field.name]"
  144. width="120px"
  145. @search="search"
  146. ></ss-search-input>
  147. </template>
  148. <ss-search-input
  149. v-if="hasKeyword"
  150. name="ssKeyword"
  151. placeholder="关键词"
  152. v-model="form.ssKeyword"
  153. width="120px"
  154. @search="search"
  155. ></ss-search-input>
  156. <ss-search-button
  157. v-if="hasScope"
  158. :key="'scope-' + String(searchButtonConfigCheckId)"
  159. text="所有"
  160. icon-class="nav-icon-search"
  161. :opt="searchButtonConfig"
  162. :check-id="searchButtonConfigCheckId"
  163. ></ss-search-button>
  164. <template v-for="func in rootFuncList" :key="func.id || func.servName">
  165. <ss-search-button
  166. :text="func.desc || func.title || ''"
  167. icon-class="nav-icon-add"
  168. :opt="[]"
  169. :check-id="0"
  170. @click="handleRootFuncClick(func)"
  171. ></ss-search-button>
  172. </template>
  173. </template>
  174. <template v-else>
  175. <%--新UI 单对象查询翻页end Ben --%>
  176. <%--关键词--%>
  177. <%-- 再去掉 -- 只支持一行。Lin
  178. <ss:rpt name='searchableFields' id='fieldsList'>
  179. --%>
  180. <ss:rpt name='fieldsList' id='searchItem'>
  181. <%
  182. ss.obj.ObjFieldB f = (ss.obj.ObjFieldB) pageContext.getAttribute("searchItem");
  183. pageContext.setAttribute("searchItemValue", request.getParameter(f.name));
  184. %>
  185. <%--codebook 使用select下拉--%>
  186. <ss:equal val='${empty searchItem.cbName}' val2='false'>
  187. <%-- 旧的objecPicker
  188. <input type="text" name="${searchItem.name}" value="${searchItemValue}"/>
  189. <input type="text" name="${searchItem.name}Name" placeholder="${searchItem.desc}" style="width:${searchItem.width};min-width: 108px;"/>
  190. <ss:equal val='${empty cadcadingInputs[searchItem.name]}' val2='true'>
  191. <ss:objp name='${searchItem.name}' cb='${searchItem.cbName}' inp='true' filterField='${searchItem.filterFieldStr}'/>
  192. </ss:equal> --%>
  193. <%-- 新UI --%>
  194. <!-- <ss-objp
  195. v-model="${searchItem.name}"
  196. name="${searchItem.name}"
  197. :options="${searchItem.name}Option"
  198. placeholder="${searchItem.desc}"
  199. width="120"
  200. input="true"
  201. ></ss-objp> -->
  202. <ss-objp
  203. v-model="${searchItem.name}"
  204. name="${searchItem.name}"
  205. :opt="${searchItem.name}Option"
  206. placeholder="${searchItem.desc}"
  207. width="150px"
  208. inp="true"
  209. url="/service?ssServ=loadObjpOpt&objectpickerdropdown1=1"
  210. cb="${searchItem.cbName}"
  211. ></ss-objp>
  212. <script>
  213. /**
  214. * objectPicker(如:性别码)
  215. * optUrl:加载下拉菜单option选项的url
  216. */
  217. window.ss.dom.formElemConfig.${searchItem.name} = {
  218. desc: '${searchItem.desc}',
  219. value: '${searchItemValue}',
  220. optUrl: '123456',
  221. type: 2
  222. };//放当前页面表单元素配置的变量
  223. </script>
  224. </ss:equal>
  225. <%--codebook 使用input输入框--%>
  226. <ss:equal val='${empty searchItem.cbName}' val2='true'>
  227. <%--日期--%>
  228. <ss:equal val='${searchItem.type}'
  229. valList='3,11'> <%-- 改 equal:val1='true' val2='${searchItem.type == "time"}'。Lin --%>
  230. <!-- <div class="input-inside"> -->
  231. <%--年度--%>
  232. <%--
  233. <ss:equal val='${searchItem.enrolDate}' val2='true'>
  234. <input type='hidden' placeholder="${searchItem.desc}" name='${searchItem.name}'
  235. value="<ss:txt val='${searchItemValue}'/>"/>
  236. <div><input type="text" name="${searchItem.name}_year" autocomplete="off" />
  237. <input name="${searchItem.name}_month" type="button" value="春季" ssVal="3"/>
  238. <input name="${searchItem.name}_month" type="button" value="秋季" ssVal="9"/>
  239. </div><script>(function(){wd.edit.onoffInit('radio','${searchItem.name}_month','',false,null,null,null,'edit');})();</script>
  240. <script>(function(){
  241. wd.display.initEnrolDate('${searchItem.name}','edit');
  242. })()
  243. </script>
  244. </ss:equal>
  245. --%>
  246. <%--日期--%>
  247. <ss:equal val='${searchItem.type==11}' val2='true'>
  248. <%--<input type='text' autocomplete="off" placeholder="${searchItem.desc}" name='${searchItem.name}'
  249. value='<ss:txt val='${searchItemValue}'/>' format="${searchItem.fmt}"/>
  250. <input type="button" ssType="date" ssName="${searchItem.name}"/>--%>
  251. <!-- <ss-date-picker
  252. v-model="${searchItem.name}"
  253. name="${searchItem.name}"
  254. type="date"
  255. placeholder="${searchItem.desc}"
  256. width="120px"
  257. ></ss-date-picker> -->
  258. <ss-search-date-picker
  259. v-model="${searchItem.name}"
  260. name="${searchItem.name}"
  261. type="date"
  262. placeholder="${searchItem.desc}"
  263. width="100px"
  264. fmt="${searchItem.fmt}"
  265. ></ss-search-date-picker>
  266. <script>
  267. //日期类型(出生日期)
  268. window.ss.dom.formElemConfig.${searchItem.name} = {
  269. desc: '${searchItem.desc}',
  270. value: '${searchItemValue}',
  271. type: 3,
  272. name: '${searchItem.name}'
  273. };//放当前页面表单元素配置的变量
  274. </script>
  275. </ss:equal>
  276. <!-- </div> -->
  277. </ss:equal>
  278. <%--文本--%>
  279. <%-- 改 equal:val1='false' val2='${searchItem.type == "time"}'。Lin --%>
  280. <ss:notEqual val='${searchItem.type}' valList='3,11'>
  281. <%--<input name='${searchItem.name}' placeholder="${searchItem.desc}" type='text'
  282. value='<ss:txt val='${searchItemValue}'/>'/>
  283. <input type="hidden" ssType="and" ssName="${searchItem.name}"/>--%>
  284. <%--<script>wd.edit.addClearTextButton("${searchItem.name}");</script>--%>
  285. <ss-search-input
  286. name="${searchItem.name}"
  287. placeholder="${searchItem.desc}"
  288. v-model="${searchItem.name}"
  289. width="100px"
  290. >
  291. </ss-search-input>
  292. </ss:notEqual>
  293. </ss:equal>
  294. </ss:rpt>
  295. <%-- 再去掉 -- 只支持一行。Lin
  296. </ss:rpt>
  297. --%>
  298. <%--
  299. <ss:rpt name='cadcadingName' id='item'>
  300. <ss:ccp name='${item}'/>
  301. </ss:rpt>
  302. --%>
  303. <ss:equal val='${hasKeyWord}' val2='true'>
  304. <%--
  305. <input name="ssKeyword" value="${ssKeyword}" type="text" placeholder="关键词"/>
  306. <script>wd.edit.addClearTextButton("ssKeyword");</script>
  307. --%>
  308. <!-- <ss-search-input
  309. name="ssKeyword"
  310. placeholder="关键词"
  311. v-model="form.keyword"
  312. width="120"
  313. >
  314. </ss-search-input> -->
  315. <ss-search-input
  316. name="ssKeyword"
  317. placeholder="关键词"
  318. v-model="ssKeyword"
  319. width="100px"
  320. @search="search"
  321. >
  322. </ss-search-input>
  323. <script>
  324. //关键词
  325. window.ss.dom.formElemConfig.ssKeyword = {
  326. value: '${ssKeyword}',
  327. name: "ssKeyword",
  328. desc: "关键词",
  329. type: 31
  330. };//放当前页面表单元素配置的变量
  331. </script>
  332. </ss:equal>
  333. <ss-search-button
  334. text="所有"
  335. icon-class="nav-icon-search"
  336. :opt="searchButtonConfig"
  337. :check-id="searchButtonConfigCheckId"
  338. ></ss-search-button>
  339. <%-- 新UI 单对象查询 选项卡、翻页start Ben --%>
  340. <ss:equal val='${"1"==isReady && !isMultipleObject}' val2='true'>
  341. <%--
  342. <ss:equal val='${hasScope}' val2='true'>
  343. <ul style="list-style: none;display: inline-block;">
  344. <li ssType="searchScope" ssVal=99>所有</li>
  345. <li ssType="searchScope" ssVal=2>管理</li>
  346. <li ssType="searchScope" ssVal=1>创建</li>
  347. <li ssType="searchScope" ssVal=3>已办</li>
  348. <li ssType="searchScope" ssVal=55>停用</li>
  349. </ul>
  350. </ss:equal>
  351. --%>
  352. </ss:equal>
  353. <%--
  354. <input type="submit" name="ssSearch" value="搜索" class="content-invertButton"/>
  355. --%>
  356. <!-- <ss-search-button text="搜索" @click="console.log('这里改成提交表单')">
  357. </ss-search-button> -->
  358. <script>
  359. <%-- 根按钮(管理按钮) --%>
  360. window.ss.dom.btnElemConfig = window.ss.dom.btnElemConfig || {};
  361. </script>
  362. <%--多对像搜索隐藏全文按钮--%>
  363. <%--
  364. <ss:equal val='${isMultipleObject}' val2='false'>
  365. --%><%--
  366. <input type="hidden" ssType="fts" ssName="fts" value="全文"/>--%> <%-- wdType="isFulltext" wdName="isFulltext"。Lin --%>
  367. <%--</ss:equal>--%>
  368. <%--根按钮(管理按钮)start--%>
  369. <ss:rpt name='buttonList' id='button'>
  370. <ss:auth serv='${button.service}'>
  371. <ss:equal val='${empty button.pluginList}' val2='true'>
  372. <%--
  373. <input type="button" name="${button.name}" value="${button.buttonName}" class="content-button"
  374. onclick='wd.display.showComponent({show:["wdDialog"],url:"<ss:serv name='${button.service}' dest='${button.dest}' parm='${button.param}'/>",title:"${button.title}",width:"${button.width}",height:"${button.height}",minHeight:"${button.minHeight}",maxHeight:"${button.maxHeight+100}",showTitle:"${button.showTitle}"});'/>
  375. --%>
  376. <!-- <ss-search-button
  377. text="${button.name}"
  378. icon-class="nav-icon-add"
  379. ></ss-search-button> -->
  380. <script>
  381. function ${button.id}handleClick() {
  382. wd.display.showComponent({
  383. show: ["wdDialog"],
  384. url: "<ss:serv name='${button.service}' dest='${button.dest}' parm='${button.param}'/>",
  385. title: "${button.title}",
  386. width: "${button.width}",
  387. height: "${button.height}",
  388. minHeight: "${button.minHeight}",
  389. maxHeight: "${button.maxHeight+100}",
  390. showTitle: "${button.showTitle}"
  391. });
  392. }
  393. <%-- 实际没用上,注释掉 Ben(20251225)
  394. window.ss.dom.btnElemConfig.${button.id}={
  395. desc:"${button.buttonName}",
  396. id:"${button.id}",
  397. dropOptions:[],
  398. onclick: () => {
  399. wd.display.showComponent({show:["wdDialog"],url:"<ss:serv name='${button.service}' dest='${button.dest}' parm='${button.param}'/>",title:"${button.title}",width:"${button.width}",height:"${button.height}",minHeight:"${button.minHeight}",maxHeight:"${button.maxHeight+100}",showTitle:"${button.showTitle}"});
  400. }
  401. };
  402. --%>
  403. </script>
  404. <ss-search-button
  405. text="${button.buttonName}"
  406. icon-class="nav-icon-add"
  407. :opt="[]"
  408. :check-id="0"
  409. onclick="${button.id}handleClick()"
  410. ></ss-search-button>
  411. </ss:equal>
  412. <ss:equal val='${empty button.pluginList}' val2='false'>
  413. <ss:rpt name='${button.pluginList}' id='plugin'>
  414. <span style="display:none;" class="${button.id}children" value="${plugin.plugin.desc}"
  415. onclick='wd.display.showComponent({show:["wdDialog"],url:"<ss:serv
  416. name='${plugin.service}' dest='${plugin.dest}'
  417. parm='${plugin.param}'/>",title:"${plugin.title}",width:"${plugin.width}",height:"${plugin.height}",minHeight:"${plugin.minHeight}",maxHeight:"${plugin.maxHeight}",showTitle:"${plugin.showTitle}"});'>
  418. </span>
  419. </ss:rpt>
  420. <%-- <input type="button" id="${button.id}" name="${button.name}" value="${button.buttonName}" class="content-button" onclick='void(0)'/>
  421. <span style=" display:inline-table; width: 60px; ">
  422. <input type="button" id="${button.id}" name="${button.name}" value="${button.buttonName}" class="content-button" onclick='void(0)'/>
  423. <span class="icon-dimPoint" style="margin-right: 7px;margin-top: -25px;position: relative;">
  424. </span>
  425. </span>
  426. <script>wd.display.attachButton("${button.id}","${button.id}children",null,false,true)</script>--%>
  427. <%-- 新UI start Ben --%>
  428. <!-- <ss-search-button
  429. text="${plugin.plugin.desc}"
  430. icon-class="nav-icon-add"
  431. :options="${button.id}DropOptions"
  432. ></ss-search-button> -->
  433. <%-- :opt改为取vue.data中的变量,而不取window.ss.dom.btnElemConfig(因为取不到),onclick改为:onclick xu(20251209) --%>
  434. <ss-search-button
  435. text="${button.buttonName}"
  436. icon-class="nav-icon-add"
  437. :opt="btnElemConfig.${button.id}.dropOptions"
  438. check-id="${management}"
  439. onclick="${button.id}handleClick()"
  440. ></ss-search-button>
  441. <script>
  442. function ${button.id}handleClick() {
  443. wd.display.showComponent({
  444. show: ["wdDialog"],
  445. url: "<ss:serv name='${plugin.service}' dest='${plugin.dest}' parm='${plugin.param}'/>",
  446. title: "${plugin.title}",
  447. width: "${plugin.width}",
  448. height: "${plugin.height}",
  449. minHeight: "${plugin.minHeight}",
  450. maxHeight: "${plugin.maxHeight}",
  451. showTitle: "${plugin.showTitle}"
  452. });
  453. }
  454. <%-- 实际没用上,注释掉 Ben(20251225)
  455. window.ss.dom.btnElemConfig.${button.id}={id:"${button.id}",dropOptions:[],
  456. onclick: () => {
  457. wd.display.showComponent({show:["wdDialog"],url:"<ss:serv name='${plugin.service}' dest='${plugin.dest}' parm='${plugin.param}'/>",title:"${plugin.title}",width:"${plugin.width}",height:"${plugin.height}",minHeight:"${plugin.minHeight}",maxHeight:"${plugin.maxHeight}",showTitle:"${plugin.showTitle}"});
  458. }
  459. };
  460. --%>
  461. <%-- 循环生成按钮数组 --%>
  462. <ss:rpt name='${button.pluginList}' id='plugin'>
  463. window.ss.dom.btnElemConfig
  464. .${button.id}.
  465. dropOptions.push(
  466. {
  467. desc: '${plugin.plugin.desc}', <%-- 按钮名 --%>
  468. callback: function () {
  469. wd.display.showComponent({
  470. show: ["wdDialog"],
  471. url: "<ss:serv name='${plugin.service}' dest='${plugin.dest}' parm='${plugin.param}'/>",
  472. title: "${plugin.title}",
  473. width: "${plugin.width}",
  474. height: "${plugin.height}",
  475. minHeight: "${plugin.minHeight}",
  476. maxHeight: "${plugin.maxHeight}",
  477. showTitle: "${plugin.showTitle}"
  478. });
  479. }
  480. }
  481. );
  482. </ss:rpt>
  483. </script>
  484. <%-- 新UI end Ben --%>
  485. </ss:equal>
  486. </ss:auth>
  487. </ss:rpt>
  488. <%--管理按钮end--%>
  489. </template>
  490. </div>
  491. </div>
  492. <%--搜索条件end--%>
  493. <script type="text/javascript" src="/ss/env/env_search.js"></script>
  494. <%-- 旧UI初始化查询列表的js,新UI做好后,要被删掉的 --%>
  495. <%-- 新UI初始化查询列表数据的JS --%>
  496. <script>
  497. window.ss.dom.listConfig = window.ss.dom.listConfig || {};
  498. //二级对象 草稿箱
  499. window.ss.dom.listConfig.draftbox = [];
  500. <%-- 草稿箱start --%>
  501. <ss:rpt name='cgxList' id='item'>
  502. {
  503. let item = {};
  504. window.ss.dom.listConfig.draftbox.push(item);
  505. <%-- 下面的titlexxx是原来td的一个属性,不知道有啥用 --%>
  506. <ss:equal val='${not empty item.service.play && empty item.service.update && empty item.service.change}' val2='true'>
  507. item.titlexxx = "${item.service.play.title}";
  508. item.onclick = function () {
  509. wd.display.showComponent({
  510. show: ["wdDialog"],
  511. url: "<ss:serv name='${item.service.play.service}' dest='${item.service.play.dest}' parm='${item.service.play.param}'/>",
  512. title: "${item.service.play.title}",
  513. width: "${item.service.play.width}",
  514. height: "${item.service.play.height}",
  515. minHeight: "${item.service.play.minHeight}",
  516. maxHeight: "${item.service.play.maxHeight}"
  517. });
  518. };
  519. </ss:equal>
  520. <ss:equal val='${item.service.lbm}' val2='1'>
  521. <ss:equal val='${empty item.service.update}' val2='false'>
  522. item.titlexxx = "${item.service.update.title}";
  523. item.onclick = function () {
  524. wd.display.showComponent({
  525. show: ["wdDialog"],
  526. url: "<ss:serv name='${item.service.update.service}' dest='${item.service.update.dest}' parm='${item.service.update.param}'/>",
  527. title: "${item.service.update.title}",
  528. width: "${item.service.update.width}",
  529. height: "${item.service.update.height}",
  530. minHeight: "${item.service.update.minHeight}",
  531. maxHeight: "${item.service.update.maxHeight}"
  532. });
  533. };
  534. </ss:equal>
  535. </ss:equal>
  536. <ss:equal val='${item.service.lbm}' val2='11'>
  537. <ss:equal val='${empty item.service.change}' val2='false'>
  538. item.titlexxx = "${item.service.change.title}";
  539. item.onclick = function () {
  540. wd.display.showComponent({
  541. show: ["wdDialog"],
  542. url: "<ss:serv name='${item.service.change.service}' dest='${item.service.change.dest}' parm='${item.service.change.param}'/>",
  543. title: "${item.service.change.title}",
  544. width: "${item.service.change.width}",
  545. height: "${item.service.change.height}",
  546. minHeight: "${item.service.change.minHeight}",
  547. maxHeight: "${item.service.change.maxHeight}"
  548. });
  549. };
  550. </ss:equal>
  551. </ss:equal>
  552. item.buttons = [];
  553. <ss:equal val='${item.service.lbm}' val2='1'>
  554. item.buttons.push(
  555. {
  556. class: "cart-list-setting",
  557. title: "增加",
  558. onclick: () => {
  559. console.log("点击了增加");
  560. }
  561. }
  562. );
  563. </ss:equal>
  564. <ss:equal val='${item.service.lbm}' val2='11'>
  565. item.buttons.push(
  566. {
  567. class: "cart-list-setting",
  568. title: "变动",
  569. onclick: () => {
  570. console.log("点击了变动");
  571. }
  572. }
  573. );
  574. </ss:equal>
  575. <ss:equal val='${item.service.lbm}' val2='51'>
  576. item.buttons.push(
  577. {
  578. class: "cart-list-setting",
  579. title: "停用",
  580. onclick: () => {
  581. console.log("点击了停用");
  582. }
  583. }
  584. );
  585. </ss:equal>
  586. <ss:equal val='${item.service.lbm}' val2='55'>
  587. item.buttons.push(
  588. {
  589. class: "cart-list-setting",
  590. title: "启用",
  591. onclick: () => {
  592. console.log("点击了启用");
  593. }
  594. }
  595. </ss:equal>
  596. <ss:equal val='${item.service.lbm}' valList='51,55'>
  597. item.buttons.push(
  598. {
  599. class: "cart-list-setting",
  600. title: "还原",
  601. onclick: () => {
  602. wd.display.showComponent({
  603. show: ["wdDialog"],
  604. url: "<ss:serv name='deleteSq' parm='{"wdConfirmationCaptchaService":"0","sqid":"${item.sqid}"}' dest='info'/>",
  605. title: "提示信息",
  606. width: 715,
  607. height: 483
  608. });
  609. }
  610. }
  611. </ss:equal>
  612. console.log('###item.thumbnail:${item.thumbnail}');
  613. <%--缩略图--%>
  614. <ss:equal val='${empty item.thumbnail}' val2='false'>
  615. <ss:equal val='${item.service.state}' val2='0'>
  616. item.thumb = "${sessionScope['ssUser'].skinDir}image/object/default-${item.ssObjName}.png"
  617. onerror = "javascript:this.src='${sessionScope['ssUser'].skinDir}image/default-photo.png';this.onerror=null;";
  618. </ss:equal>
  619. <ss:notEqual val='${item.service.state}' val2='0'>
  620. item.thumb = "<ss:serv name='dlByHttp' parm='{"wdConfirmationCaptchaService":"0","path":"${item.thumbnail.value}","type":"img"}'/>";
  621. </ss:notEqual>
  622. </ss:equal>
  623. <%-- 列标题 --%>
  624. <ss:equal val='${empty item.first}' val2='false'> <%-- 改 equal:val1="true" val2="${not empty item.first}"。Lin --%>
  625. <%--不带codebook--%>
  626. <ss:equal val='${empty item.first.field.cbName}' val2='true'>
  627. item.title = "<ss:txt val='${item.first.value}' miniDate='false' fmt='${item.first.field.fmt}'/>";
  628. </ss:equal>
  629. <%--带codebook--%>
  630. <ss:equal val='${empty item.first.field.cbName}' val2='false'>
  631. item.title = "<ss:cbTrans cb='${item.first.field.cbName}' val='${item.first.value}'/>";
  632. </ss:equal>
  633. </ss:equal>
  634. <%--列标题:缺标题显示属性start--%>
  635. <ss:equal val='${empty item.first && not empty item.third}' val2='true'>
  636. item.title = '';
  637. <ss:rpt name='${item.third}' id='itemList'>
  638. <ss:rpt name='${itemList}' id='item2'>
  639. item.title += '${item2.field.desc}:';
  640. <%--不带codebook--%>
  641. <ss:equal val='${empty item2.field.cbName}' val2='true'>
  642. item.title += '<ss:txt val='${item2.value}' fmt='${item2.field.fmt}' miniDate='false'/>&nbsp;';
  643. </ss:equal>
  644. <%--带codebook--%>
  645. <ss:equal val='${empty item2.field.cbName}' val2='false'>
  646. item.title += '<ss:cbTrans cb='${item2.field.cbName}' val='${item2.value}'/>&nbsp;';
  647. </ss:equal>
  648. </ss:rpt>
  649. </ss:rpt>
  650. </ss:equal>
  651. <%--缺标题显示属性end--%>
  652. <%--正文或摘要--%>
  653. <ss:equal val='${empty item.second}' val2='false'>
  654. // 摘要字段统一映射为 desc(用于 ss-list-card 右侧文字区规则) by xu 20260113
  655. item.desc = '${item.second.value}';
  656. item.summary = '${item.second.value}'; // legacy 兜底 by xu 20260113
  657. </ss:equal>
  658. // 对象号字段映射(用于 ss-list-card 第三部分回显) by xu 20260113
  659. item.objNum = '${item.ssObjId}';
  660. <%-- 列表底部的对象属性(tags) --%>
  661. <ss:equal val='${empty item.first || empty item.third}' val2='false'>
  662. item.tags = [];
  663. <ss:rpt name='${item.third}' id='itemList'>
  664. <ss:rpt name='${itemList}' id='item2'>
  665. {
  666. let v;
  667. <%--不带codebook--%>
  668. <ss:equal val='${empty item2.field.cbName}' val2='true'>
  669. v = '<ss:txt val='${item2.value}' fmt='${item2.field.fmt}' miniDate='false'/>';
  670. </ss:equal>
  671. <%--带codebook--%>
  672. <ss:equal val='${empty item2.field.cbName}' val2='false'>
  673. v = '<ss:cbTrans cb='${item2.field.cbName}' val='${item2.value}'/>';
  674. </ss:equal>
  675. item.tags.push({'${item2.field.desc}': v});
  676. }
  677. </ss:rpt>
  678. </ss:rpt>
  679. </ss:equal>
  680. <%-- 对象变动前后属性列表 --%>
  681. item.changeItems = [];
  682. <ss:rpt name='${item.forth}' id='item3'>
  683. item.changeItems.push({
  684. name: '${item3.name}',
  685. oldValue: '${item3.oldValue}',
  686. newValue: '${item3.newValue}'
  687. });
  688. </ss:rpt>
  689. }
  690. </ss:rpt>
  691. <%-- 草稿箱end --%>
  692. //在用或停用的对象列表
  693. window.ss.dom.listConfig.list = [];
  694. <ss:rpt name='objectList' id='item'><%-- 循环一次生成一行列表 start --%>
  695. {
  696. let item = {};//列表的其中一行的属性
  697. window.ss.dom.listConfig.list.push(item);
  698. <ss:equal val='${item.service.state}' val2='0'><%-- 借阅 --%>
  699. item.onclick = function () {
  700. wd.display.showComponent({
  701. show: ["wdDialog"],
  702. url: "<ss:serv name='ydsq_tj' parm='{"wdConfirmationCaptchaService":"0","ssObjId":"${item.ssObjId}","ssObjName":"${item.ssObjName}"}' dest='ydsq_tj'/>",
  703. title: "借阅",
  704. width: 613,
  705. height: 387
  706. });
  707. }
  708. </ss:equal>
  709. <ss:equal val='${empty item.service.play}' val2='false'><%-- 有权查看,不需要借阅的情况 --%>
  710. item.titlexxx = "${item.service.play.title}";
  711. item.onclick = function () {
  712. wd.display.showComponent({
  713. getSize: 1,
  714. width: ((${item.service.play.width}+DOM_SIZE_objInfoTabWidth) + ""),
  715. show: ["wdDialog"],
  716. url: "<ss:serv name='${item.service.play.service}' dest='${item.service.play.dest}' parm='${item.service.play.param}'/>",
  717. title: "${item.service.play.title}",
  718. height: "${item.service.play.height}",
  719. minHeight: "${item.service.play.minHeight}",
  720. maxHeight: "${item.service.play.maxHeight}"
  721. });
  722. }
  723. </ss:equal>
  724. <ss:equal val='${empty item.thumbnail}' val2='false'><%-- 缩略图 --%>
  725. <ss:equal val='${item.service.state}' val2='0'><%-- 借阅 --%>
  726. item.thumb = "${sessionScope['ssUser'].skinDir}image/object/default-${item.ssObjName}.png";
  727. </ss:equal>
  728. <ss:notEqual val='${item.service.state}' val2='0'>
  729. console.log('@@@图片:${item.thumbnail.value}');
  730. item.thumb = "<ss:serv name='dlByHttp' parm='{"wdConfirmationCaptchaService":"0","path":"${item.thumbnail.value}","type":"img"}'/>";
  731. </ss:notEqual>
  732. </ss:equal>
  733. <%-- 标题 --%>
  734. <ss:equal val='${empty item.first}' val2='false'> <%-- 改 equal:val1="true" val2="${not empty item.first}"。Lin --%>
  735. <%--不带codebook--%>
  736. <ss:equal val='${empty item.first.field.cbName}' val2='true'>
  737. item.title = "<ss:txt val='${item.first.value}' miniDate='false' fmt='${item.first.field.fmt}'/>";
  738. </ss:equal>
  739. <%--带codebook--%>
  740. <ss:equal val='${empty item.first.field.cbName}' val2='false'>
  741. item.title = "<ss:cbTrans cb='${item.first.field.cbName}' val='${item.first.value}'/>";
  742. </ss:equal>
  743. </ss:equal>
  744. <%--缺标题显示属性当标题start--%>
  745. <ss:equal val='${empty item.first && not empty item.third}' val2='true'>
  746. item.title = '';
  747. <ss:rpt name='${item.third}' id='itemList'>
  748. <ss:rpt name='${itemList}' id='item2'>
  749. item.title += '${item2.field.desc}:';
  750. <%--属性名--%>
  751. <%--不带codebook--%><%--属性值--%>
  752. <ss:equal val='${empty item2.field.cbName}' val2='true'>
  753. item.title += "<ss:txt val='${item2.value}' fmt='${item2.field.fmt}' miniDate='false'/>&nbsp;";
  754. </ss:equal>
  755. <%--带codebook--%><%--属性值--%>
  756. <ss:equal val='${empty item2.field.cbName}' val2='false'>
  757. item.title += "<ss:cbTrans cb='${item2.field.cbName}' val='${item2.value}'/>&nbsp;";
  758. </ss:equal>
  759. </ss:rpt>
  760. </ss:rpt>
  761. </ss:equal>
  762. <%--缺标题显示属性当标题end--%>
  763. <%-- 摘要 --%>
  764. <ss:equal val='${empty item.second}' val2='false'>
  765. // 摘要字段统一映射为 desc(用于 ss-list-card 右侧文字区规则) by xu 20260113
  766. item.desc = '${item.second.value}';
  767. item.summary = '${item.second.value}'; // legacy 兜底 by xu 20260113
  768. </ss:equal>
  769. // 对象号字段映射(用于 ss-list-card 第三部分回显) by xu 20260113
  770. item.objNum = '${item.ssObjId}';
  771. <%-- 列表底部的对象标签组 --%>
  772. item.tags = [];
  773. <%-- 提到条件外面 Ben(20251230) --%>
  774. <ss:equal val='${empty item.first || empty item.third}' val2='false'>
  775. <%--item.tags=[]; 提到条件外面,不然列表报 Mount failed:TypeError: Cannot read properties of undefined (reading 'map') Ben(20251230) --%>
  776. <ss:rpt name='${item.third}' id='itemList'>
  777. <ss:rpt name='${itemList}' id='item2'>
  778. {
  779. let v;
  780. <%--不带codebook--%>
  781. <ss:equal val='${empty item2.field.cbName}' val2='true'>
  782. v = '<ss:txt val='${item2.value}' miniDate='false' fmt='${item2.field.fmt}'/>';
  783. </ss:equal>
  784. <%--带codebook--%>
  785. <ss:equal val='${empty item2.field.cbName}' val2='false'>
  786. v = '<ss:cbTrans cb='${item2.field.cbName}' val='${item2.value}'/>';
  787. </ss:equal>
  788. item.tags.push({
  789. ${item2.field.desc}:
  790. v
  791. })
  792. ;
  793. }
  794. </ss:rpt>
  795. </ss:rpt>
  796. </ss:equal>
  797. <%--按钮--%>
  798. <ss:equal val='${empty item.service.btnList}' val2='false'>
  799. item.buttons = [];
  800. <ss:rpt name='${item.service.btnList}' id='btn'>
  801. item.buttons.push(
  802. {
  803. id: "${btn.btnID}",
  804. titlexxx: "${btn.title}",
  805. class: "cart-list-setting",
  806. title: "${btn.name}",
  807. onclick: () => {
  808. <%--列表的变动按钮就在这里,下面的width会根据不同业务对象而不同
  809. 弹窗参数加上getSize=1,使showComponent弹窗方法不再通过ajax从后台取宽高 --%>
  810. wd.display.showComponent({
  811. getSize: 1,
  812. width: ((${btn.width}+DOM_SIZE_objInfoTabWidth) + ""),
  813. show: ["wdDialog"],
  814. url: "<ss:serv name='${btn.service}' dest='${btn.dest}' parm='${btn.param}'/>",
  815. title: "${btn.title}",
  816. height: "${btn.height}",
  817. minHeight: "${btn.minHeight}",
  818. maxHeight: "${btn.maxHeight}"
  819. });
  820. }
  821. }
  822. );
  823. <%--
  824. <ss:equal val='${index}' val2='0'>
  825. </ss:equal>
  826. <ss:notEqual val='${index}' val2='0'>
  827. </ss:notEqual>
  828. 改为不区分是否第1个变动按钮,都输出相同json数据 --%>
  829. </ss:rpt>
  830. </ss:equal>
  831. }
  832. </ss:rpt><%-- 循环一次生成一行列表 end --%>
  833. </script>
  834. <%--搜索结果start--%>
  835. <%-- 功能说明:卡片区域使用 grid 自动分列,剩余宽度平分(不锁死 max-width) by xu 20260109 --%>
  836. <div v-show="userInteracted && loadingList" class="pobj-list-loading" style="display:none;">
  837. <div class="pobj-spinner"></div>
  838. </div>
  839. <div v-show="!(userInteracted && loadingList)"
  840. :class="['content-area','item-content-area', cardGridKind ? ('ss-card-grid--' + cardGridKind) : '']">
  841. <template v-for="(item, i) in listConfig.draftbox" :key="i">
  842. <ss-folder-card v-if="item.children" :item="item"></ss-folder-card>
  843. <!-- 功能说明:传入 ssObjName + cardClickAction(卡片主体点击 view/single;角标仍多选) by xu 20260109 -->
  844. <ss-list-card v-else :item="item" :ss-obj-name="ssObjName" :card-click-action="cardClickAction"
  845. @toggle-select="handleToggleSelect"></ss-list-card>
  846. </template>
  847. <template v-for="(item, i) in listConfig.list" :key="i">
  848. <ss-folder-card v-if="item.children" :item="item"></ss-folder-card>
  849. <!-- 功能说明:传入 ssObjName + cardClickAction(卡片主体点击 view/single;角标仍多选) by xu 20260109 -->
  850. <ss-list-card v-else :item="item" :ss-obj-name="ssObjName" :card-click-action="cardClickAction"
  851. @toggle-select="handleToggleSelect"></ss-list-card>
  852. </template>
  853. <ss-page
  854. :key="String(ssPaging?.pageNo || 1) + '-' + String(ssPaging?.rowNumPer || 12) + '-' + String(ssPaging?.rowNum || 0)" <%-- // 功能说明:分页默认每页12条 by xu 20260116 --%>
  855. :total="Number(ssPaging?.rowNum || 0)"
  856. :size="Number(ssPaging?.rowNumPer || 12)" <%-- // 功能说明:分页默认每页12条 by xu 20260116 --%>
  857. :page="Number(ssPaging?.pageNo || 1)"
  858. @change="handlePageChange">
  859. </ss-page>
  860. </div>
  861. </form>
  862. <!-- 功能说明:右侧业务面板(ss-sidebar),用于已选/服务/预定/图表等 by xu 20260113 -->
  863. <ss-sidebar
  864. :buttons="sidebarButtons"
  865. :panels="sidebarPanels"
  866. @remove="handleSidebarRemove"
  867. ></ss-sidebar>
  868. </div>
  869. </div>
  870. </body>
  871. </html>
  872. <script type="module">
  873. <%-- 打印新UI相关json对象 --%>
  874. const data = {
  875. ssObjName: "${ssObjName}", // 对象名
  876. // 后端注入的 ssServ 接口名(用于 ajax 拉取查询首页/列表数据)
  877. ssSearchPobjHomeServName: "${ssSearchPobjHomeServName}", // <对象名>_cxsy
  878. ssSearchPobjListServName: "${ssSearchPobjListServName}", // <对象名>_cxlb
  879. // 接口返回(调试用,可删)
  880. ssHomeResp: null,
  881. ssListResp: null,
  882. // 页面状态
  883. homeReady: false,
  884. userInteracted: false, // 用户触发(搜索/翻页/切换范围)
  885. loadingHome: false,
  886. loadingList: false,
  887. // 首页接口返回字段(按 page/objlist修改.txt)
  888. searchFieldList: [], // 查询条件字段
  889. rootFuncList: [], // 根功能
  890. hasKeyword: false, // 是否有关键词
  891. hasScope: false, // 是否带范围
  892. thnType: 0, // 缩略图类型:1横向 2竖向 3方形 4圆形(目前仅用 1/2,其他先不处理)
  893. ssPaging: {pageNo: 1, rowNumPer: 12, rowNum: 0}, // 翻页信息 // 功能说明:默认每页12条 by xu 20260116
  894. rbarFuncList: [], // 右侧栏功能
  895. rbarTabNameList: [], // 右侧栏选项卡
  896. pstatList: [], // 功能说明:个人统计图表配置(右侧图表区) by xu 20260114
  897. // 查询表单参数(用于接口入参)
  898. form: {management: "99", isFulltext: "0", isAnd: "", ssKeyword: ""},
  899. systemType: window.ss.dom.TYPE,
  900. listConfig: window.ss.dom.listConfig || {draftbox: [], list: []},
  901. formElemConfig: window.ss.dom.formElemConfig || {},
  902. // 卡片区域:自动分列/宽度平分 by xu 20260109
  903. cardGridKind: '', // none/photo/thumbnail by xu 20260109
  904. // 卡片主体点击动作:view=查看(调用 item.onclick);single=单选互斥;角标仍多选 by xu 20260109
  905. cardClickAction: "view", // 功能说明:默认先用 view,首页接口若返回预定面板则切到 single by xu 20260114
  906. hasBookPanel: false, // 功能说明:是否存在预定面板(驱动 cardClickAction + 选中后联动 cxycl) by xu 20260116
  907. hasServPanel: false, // 功能说明:是否存在服务面板(严格依赖首页 rbarTabNameList 是否返回 rbarServ) by xu 20260116
  908. yclLoading: false, // 功能说明:已选对象联动(cxycl)加载状态 by xu 20260114
  909. yclResp: null, // 功能说明:已选对象联动接口返回(调试/兜底) by xu 20260114
  910. // 右侧已选(用于未来接入 ss-sidebar 的已选面板) by xu 20260109
  911. selectedItems: [],
  912. // 右侧边栏:顶部按钮(由首页接口 rbarFuncList 初始化)
  913. sidebarButtons: [],
  914. // 右侧边栏:分区配置(由 initSidebarPanels 初始化) by xu 20260113
  915. sidebarPanels: [],
  916. <%-- 实际没用上,注释掉 Ben(20251225) btnElemConfig:window.ss.dom.btnElemConfig, --%>
  917. searchButtonConfigCheckId: "99",
  918. searchButtonConfig: [
  919. {
  920. id: "99",
  921. desc: "所有",
  922. callback: () => window.__objListVm && window.__objListVm.switchScope(99),
  923. },
  924. {
  925. id: "2",
  926. desc: "管理",
  927. callback: () => window.__objListVm && window.__objListVm.switchScope(2),
  928. },
  929. {
  930. id: "1",
  931. desc: "创建",
  932. callback: () => window.__objListVm && window.__objListVm.switchScope(1),
  933. },
  934. {
  935. id: "3",
  936. desc: "已办",
  937. callback: () => window.__objListVm && window.__objListVm.switchScope(3),
  938. },
  939. {
  940. id: "55",
  941. desc: "停用",
  942. callback: () => window.__objListVm && window.__objListVm.switchScope(55),
  943. }
  944. ],
  945. }
  946. if (window.ss.dom.formElemConfig) {
  947. Object.entries(window.ss.dom.formElemConfig).forEach(([key, config]) => {
  948. data[key] = config.value;
  949. // 处理 objPicker
  950. if (config.type === window.ss.dom.TYPE.OBJPICKER) {
  951. data[key + "ObjPicker"] = true;
  952. data[key + "Option"] = [];
  953. data[key + "Url"] = config.optUrl;
  954. }
  955. })
  956. }
  957. SS.ready(function () {
  958. // 功能说明:右侧 ss-sidebar__inner 统一增加 padding-top=10px(撤回外层 wrapper 方案) by xu 20260114
  959. try {
  960. if (!document.getElementById("ss-objlist-sidebar-inner-padding")) {
  961. var st = document.createElement("style");
  962. st.id = "ss-objlist-sidebar-inner-padding";
  963. st.type = "text/css";
  964. st.appendChild(document.createTextNode("#app .ss-sidebar__inner{padding-top:10px;}"));
  965. document.head.appendChild(st);
  966. }
  967. } catch (e) {
  968. }
  969. window.ss.dom.initializeFormApp({
  970. el: "#app",
  971. data() {
  972. return data;
  973. },
  974. methods: {
  975. openServiceDialog(srv) {
  976. if (!srv) return;
  977. // 功能说明:优先 ssToken 打开;若无 ssToken(如 rootFuncList),回退 servName+dest+parm 拼接 by xu 20260114
  978. const token = String(srv.ssToken || "").trim();
  979. let url = "";
  980. if (token) {
  981. url = "/service?ssToken=" + encodeURIComponent(token);
  982. } else if (srv.servName) {
  983. url = "/service?ssServ=" + encodeURIComponent(String(srv.servName));
  984. if (srv.dest) url += "&ssDest=" + encodeURIComponent(String(srv.dest));
  985. // 功能说明:兼容后端字段名 parm/param(两种都可能出现) by xu 20260114
  986. const p = (srv.parm !== undefined ? srv.parm : srv.param);
  987. try {
  988. if (typeof p === "string") {
  989. const s = p.trim();
  990. if (s && (s[0] === "{" || s[0] === "[")) {
  991. const obj = JSON.parse(s);
  992. if (obj && typeof obj === "object") {
  993. Object.entries(obj).forEach(([k, v]) => {
  994. if (v === undefined || v === null) return;
  995. url += "&" + encodeURIComponent(k) + "=" + encodeURIComponent(String(v));
  996. });
  997. }
  998. } else if (s) {
  999. url += (s.startsWith("&") ? "" : "&") + s;
  1000. }
  1001. } else if (p && typeof p === "object") {
  1002. Object.entries(p).forEach(([k, v]) => {
  1003. if (v === undefined || v === null) return;
  1004. url += "&" + encodeURIComponent(k) + "=" + encodeURIComponent(String(v));
  1005. });
  1006. }
  1007. } catch (_) {
  1008. }
  1009. } else if (srv.dest) {
  1010. // 功能说明:报表场景可能后端不返回 fwm(servName),支持仅用 ssDest 打开 by xu 20260115
  1011. url = "/service?ssDest=" + encodeURIComponent(String(srv.dest));
  1012. // 功能说明:兼容后端字段名 parm/param(两种都可能出现) by xu 20260115
  1013. const p = (srv.parm !== undefined ? srv.parm : srv.param);
  1014. try {
  1015. if (typeof p === "string") {
  1016. const s = p.trim();
  1017. if (s && (s[0] === "{" || s[0] === "[")) {
  1018. const obj = JSON.parse(s);
  1019. if (obj && typeof obj === "object") {
  1020. Object.entries(obj).forEach(([k, v]) => {
  1021. if (v === undefined || v === null) return;
  1022. url += "&" + encodeURIComponent(k) + "=" + encodeURIComponent(String(v));
  1023. });
  1024. }
  1025. } else if (s) {
  1026. url += (s.startsWith("&") ? "" : "&") + s;
  1027. }
  1028. } else if (p && typeof p === "object") {
  1029. Object.entries(p).forEach(([k, v]) => {
  1030. if (v === undefined || v === null) return;
  1031. url += "&" + encodeURIComponent(k) + "=" + encodeURIComponent(String(v));
  1032. });
  1033. }
  1034. } catch (_) {
  1035. }
  1036. }
  1037. // 功能说明:打开弹窗时透传当前搜索条件(searchFieldList.name -> form[name])by xu 20260116
  1038. try {
  1039. const list = Array.isArray(this.searchFieldList) ? this.searchFieldList : [];
  1040. const escapeRegExp = (s) => String(s || "").replace(/[.*+?^{}$()|[\]\\]/g, "\\$&");
  1041. const hasParam = (u, key) => {
  1042. if (!key) return false;
  1043. const re = new RegExp("([?&])" + escapeRegExp(key) + "=");
  1044. return re.test(String(u || ""));
  1045. };
  1046. const appendParam = (u, key, value) => {
  1047. if (!key) return u;
  1048. if (value === undefined || value === null) return u;
  1049. const s = String(value);
  1050. if (s === "") return u;
  1051. if (hasParam(u, key)) return u;
  1052. return (
  1053. u +
  1054. (u.indexOf("?") > -1 ? "&" : "?") +
  1055. encodeURIComponent(String(key)) +
  1056. "=" +
  1057. encodeURIComponent(s)
  1058. );
  1059. };
  1060. list.forEach((f) => {
  1061. const key = f && f.name;
  1062. if (!key) return;
  1063. const v = this.form ? this.form[key] : undefined;
  1064. if (Array.isArray(v)) {
  1065. v.forEach((vv) => {
  1066. url = appendParam(url, key, vv);
  1067. });
  1068. return;
  1069. }
  1070. url = appendParam(url, key, v);
  1071. });
  1072. } catch (_) {
  1073. }
  1074. if (!url) return;
  1075. console.log("[objList] openServiceDialog url =", url, "srv =", srv);
  1076. wd.display.showComponent({
  1077. show: ["wdDialog"],
  1078. url,
  1079. title: srv.title || srv.desc || "",
  1080. width: srv.width,
  1081. height: srv.height,
  1082. minHeight: srv.minHeight,
  1083. maxHeight: srv.maxHeight,
  1084. showTitle: srv.showTitle,
  1085. });
  1086. },
  1087. formatValByFmt(val, fmt) {
  1088. // 功能说明:统一按 fmt 动态格式化 {val, fmt}(支持任意组合 yyyy/MM/dd/HH/mm/ss),用于 title/catList by xu 20260114
  1089. try {
  1090. if (!fmt || val === undefined || val === null || val === "") return val;
  1091. const raw = typeof val === "string"
  1092. ? val.replace(/[\u202f\u00a0]/g, " ").replace(/ /g, " ").trim()
  1093. : val;
  1094. const d = raw instanceof Date ? raw : new Date(raw);
  1095. if (isNaN(d.getTime())) return val;
  1096. const pad2 = (n) => String(n).padStart(2, "0");
  1097. const tokens = {
  1098. yyyy: String(d.getFullYear()),
  1099. MM: pad2(d.getMonth() + 1),
  1100. M: String(d.getMonth() + 1),
  1101. dd: pad2(d.getDate()),
  1102. d: String(d.getDate()),
  1103. HH: pad2(d.getHours()),
  1104. H: String(d.getHours()),
  1105. mm: pad2(d.getMinutes()),
  1106. m: String(d.getMinutes()),
  1107. ss: pad2(d.getSeconds()),
  1108. s: String(d.getSeconds()),
  1109. };
  1110. const f = String(fmt);
  1111. // 不写死具体 fmt:按 token 替换;若 fmt 不含 token,则回退原值 by xu 20260114
  1112. if (!/(yyyy|MM|dd|HH|mm|ss|M|d|H|m|s)/.test(f)) return val;
  1113. return f.replace(/yyyy|MM|dd|HH|mm|ss|M|d|H|m|s/g, (k) => tokens[k] ?? k);
  1114. } catch (_) {
  1115. return val;
  1116. }
  1117. },
  1118. buildThumbUrl(thn) {
  1119. if (!thn) return "";
  1120. return "/service?ssServ=dlByHttp&type=img&path=" + encodeURIComponent(String(thn));
  1121. },
  1122. normalizeObjToCard(row) {
  1123. const card = {
  1124. _raw: row,
  1125. ssObjName: row?.ssObjName,
  1126. objNum: row?.ssObjId,
  1127. title: "",
  1128. desc: row?.abs || "",
  1129. tags: [],
  1130. buttons: [],
  1131. stateIconList: row?.stateIconList || [],
  1132. statusIcons: [],
  1133. bigState: row?.bigState,
  1134. bookState: row?.bookState,
  1135. };
  1136. // 功能说明:将后端 stateIconList 映射为卡片右上角 statusIcons(后缀=1忽略;icon-state-*/icon-servType-* 只要后缀!=1 则显示 icon-sauth) by xu 20260116
  1137. try {
  1138. const rawList = Array.isArray(row?.stateIconList) ? row.stateIconList : [];
  1139. const icons = [];
  1140. let showSauth = false;
  1141. rawList.forEach((s) => {
  1142. const cls = String(s || "").trim();
  1143. if (!cls) return;
  1144. const m = cls.match(/-(\d+)$/);
  1145. const code = m ? Number(m[1]) : NaN;
  1146. if (code === 1) return; // 约定:后缀为 1 的不处理 by xu 20260116
  1147. // 约定:state/servType 只要有一个后缀不为 1,就展示一个 icon-sauth(去重) by xu 20260116
  1148. if (cls.indexOf("icon-state-") === 0 || cls.indexOf("icon-servType-") === 0) {
  1149. showSauth = true;
  1150. }
  1151. });
  1152. if (showSauth) {
  1153. icons.push({class: "menu-base-icon icon-sauth", title: "使用中"});
  1154. }
  1155. card.statusIcons = icons;
  1156. } catch (e) {
  1157. }
  1158. // 功能说明:title 可能是 {val, fmt}(日期/时间),此处按 fmt 做格式化(避免显示英文时间串) by xu 20260114
  1159. try {
  1160. const t = row?.title;
  1161. if (t && typeof t === "object" && t.val !== undefined) {
  1162. card.title = String(this.formatValByFmt(t.val, t.fmt) ?? "");
  1163. } else {
  1164. card.title = String(row?.title?.val || row?.title || "");
  1165. }
  1166. } catch (_) {
  1167. card.title = String(row?.title?.val || row?.title || "");
  1168. }
  1169. // 功能说明:bookState 优先于 bigState(0绿空闲/1黄预订中/2红),映射到卡片 status by xu 20260116
  1170. const hasBookState = row?.bookState !== undefined && row?.bookState !== null && row?.bookState !== "";
  1171. const hasBigState = row?.bigState !== undefined && row?.bigState !== null && row?.bigState !== "";
  1172. if (hasBookState || hasBigState) {
  1173. const stateVal = hasBookState ? Number(row.bookState) : Number(row.bigState);
  1174. card.status = stateVal === 2 ? "disabled" : stateVal === 1 ? "unavailable" : stateVal === 0 ? "available" : "";
  1175. }
  1176. // thnType:缩略图类型(目前仅处理 1=横向、2=竖向;其余先按无缩略图处理)
  1177. // 注意:即使 thn 为空,只要 thnType 存在,也要保留缩略图区域,以显示“对象号_icon”占位
  1178. const thnType = Number(this.thnType || 0);
  1179. if (thnType === 1) card.thumbType = "thumbnail"; // 横向:180×100
  1180. else if (thnType === 2) card.thumbType = "photo"; // 竖向:73×100(默认)
  1181. // thn:缩略图不为空 => 有图片(否则 ss-list-card 会按 thumbType 显示占位 icon)
  1182. if (row?.thn) card.thumb = this.buildThumbUrl(row.thn);
  1183. if (Array.isArray(row?.catList)) {
  1184. row.catList.forEach((c) => {
  1185. if (!c || !c.desc) return;
  1186. // 功能说明:catList 存在 fmt 时格式化时间(yyyy-MM-dd / yyyy-MM-dd HH:mm:ss),避免直接显示英文时间串 by xu 20260114
  1187. const val = this.formatValByFmt(c.val, c.fmt);
  1188. card.tags.push({[c.desc]: val});
  1189. });
  1190. }
  1191. if (Array.isArray(row?.chgRootFuncList)) {
  1192. row.chgRootFuncList.forEach((btn) => {
  1193. card.buttons.push({
  1194. id: btn.id,
  1195. titlexxx: btn.title,
  1196. class: "cart-list-setting",
  1197. title: btn.desc || btn.title || "",
  1198. onclick: () => this.openServiceDialog(btn),
  1199. });
  1200. });
  1201. }
  1202. if (row?.play) {
  1203. card.onclick = () => this.openServiceDialog(row.play);
  1204. }
  1205. return card;
  1206. },
  1207. buildPstatChartPanels(bizIcon) {
  1208. // 功能说明:根据 pstatList 构建右侧图表面板(type=chart),用于 ss-sidebar 底部图表区 by xu 20260114
  1209. const list = Array.isArray(this.pstatList) ? this.pstatList : [];
  1210. return list
  1211. .filter((it) => Number(it?.grtjlbm) === 1)
  1212. .map((it) => ({
  1213. type: "chart",
  1214. _grtjid: it?.grtjid,
  1215. title: it?.mc || "",
  1216. iconClass: bizIcon,
  1217. // 功能说明:右侧面板图表高度做上限(避免后端 gd 过大导致 sidebar 竖向撑爆) by xu 20260114
  1218. height: (() => {
  1219. const gd = Number(it?.gd);
  1220. const h = (isFinite(gd) && gd > 0) ? Math.max(160, Math.min(260, gd)) : 220;
  1221. return String(h) + "px";
  1222. })(),
  1223. // 功能说明:统计图默认先用柱状图(后续可按后端类型再扩展) by xu 20260115
  1224. options: {
  1225. xAxis: {type: "category", data: []},
  1226. yAxis: {type: "value"},
  1227. series: [{type: "bar", data: []}]
  1228. },
  1229. }));
  1230. },
  1231. loadPstatChartData(panel) {
  1232. // 功能说明:根据 grtjid 调 wrPchtPlay 获取真实数据,并写回 panel.options 触发渲染 by xu 20260114
  1233. const grtjid = panel?._grtjid;
  1234. if (!grtjid) return Promise.resolve(null);
  1235. return this.callSsService("wrPchtPlay", {grtjid})
  1236. .then((res) => {
  1237. const payload = res && typeof res === "object" ? (res.ssData || res) : null;
  1238. // 功能说明:兼容 wrPchtPlay 返回 dataArray(name=mc,value=sl) by xu 20260114
  1239. let list =
  1240. (payload && (payload.dataArray || payload.dataList || payload.list || payload.data)) ||
  1241. payload;
  1242. if (!Array.isArray(list)) list = [];
  1243. const data = list
  1244. .map((it) => {
  1245. if (!it || typeof it !== "object") return null;
  1246. const name = it.name ?? it.mc ?? it.desc ?? it.title;
  1247. const value = it.value ?? it.sl ?? it.zrs ?? it.count ?? it.num;
  1248. if (name === undefined || name === null) return null;
  1249. const v = Number(value);
  1250. return {name: String(name), value: isNaN(v) ? 0 : v};
  1251. })
  1252. .filter(Boolean);
  1253. // 功能说明:统计图默认先用柱状图(tooltip/label 显示数值;全 0 也正常展示) by xu 20260115
  1254. const xData = data.map((it) => it.name);
  1255. const yData = data.map((it) => it.value);
  1256. panel.options = {
  1257. color: ["#5470C6", "#91CC75", "#FAC858", "#EE6666", "#73C0DE", "#3BA272", "#FC8452", "#9A60B4", "#EA7CCC"],
  1258. tooltip: {trigger: "axis", axisPointer: {type: "shadow"}},
  1259. grid: {left: "3%", right: "3%", top: 12, bottom: 24, containLabel: true},
  1260. xAxis: {
  1261. type: "category",
  1262. data: xData,
  1263. axisLabel: {interval: 0, rotate: xData.length > 6 ? 30 : 0}
  1264. },
  1265. yAxis: {type: "value"},
  1266. series: [{
  1267. type: "bar",
  1268. barMaxWidth: 36,
  1269. data: yData,
  1270. label: {show: true, position: "top"},
  1271. }],
  1272. };
  1273. // 功能说明:强制触发 sidebarPanels 更新(避免仅在选中卡片后才刷新图表) by xu 20260114
  1274. try {
  1275. this.sidebarPanels = (this.sidebarPanels || []).slice();
  1276. } catch (e) {
  1277. }
  1278. // 功能说明:图表初次渲染时容器尺寸可能未稳定,nextTick 后触发 resize,避免“未选中=灰色圆” by xu 20260114
  1279. try {
  1280. const self = this;
  1281. if (self && self.$nextTick) {
  1282. self.$nextTick(function () {
  1283. try {
  1284. window.dispatchEvent(new Event("resize"));
  1285. } catch (e) {
  1286. }
  1287. });
  1288. }
  1289. setTimeout(function () {
  1290. try {
  1291. window.dispatchEvent(new Event("resize"));
  1292. } catch (e) {
  1293. }
  1294. }, 120);
  1295. } catch (e) {
  1296. }
  1297. return res;
  1298. })
  1299. .catch((e) => {
  1300. console.error("loadPstatChartData failed", grtjid, e);
  1301. return null;
  1302. });
  1303. },
  1304. applyPobjHomeData(ssData) {
  1305. if (!ssData || typeof ssData !== "object") return;
  1306. this.ssHomeResp = ssData;
  1307. this.homeReady = true;
  1308. this.userInteracted = false;
  1309. this.ssObjName = ssData.ssObjName || this.ssObjName; // 对象名
  1310. this.searchFieldList = Array.isArray(ssData.searchFieldList) ? ssData.searchFieldList : []; // 查询条件字段
  1311. this.rootFuncList = Array.isArray(ssData.rootFuncList) ? ssData.rootFuncList : []; // 根功能
  1312. this.hasKeyword = !!ssData.hasKeyword; // 是否有关键词
  1313. this.hasScope = !!ssData.hasScope; // 是否带范围
  1314. this.thnType = Number(ssData.thnType || 0); // 缩略图类型
  1315. this.ssPaging = ssData.ssPaging || this.ssPaging; // 翻页信息
  1316. this.rbarFuncList = Array.isArray(ssData.rbarFuncList) ? ssData.rbarFuncList : []; // 右侧栏功能
  1317. this.rbarTabNameList = Array.isArray(ssData.rbarTabNameList) ? ssData.rbarTabNameList : []; // 右侧栏选项卡
  1318. // 功能说明:cd 一级对象先强制显示“服务”tab(后端暂未返回 rbarServ,仅用于当前阶段联调) by xu 20260116
  1319. if (String(this.ssObjName || "").trim().toLowerCase() === "cd" && !this.rbarTabNameList.includes("rbarServ") && !this.rbarTabNameList.includes("rbarserv")) {
  1320. this.rbarTabNameList.push("rbarServ");
  1321. }
  1322. this.pstatList = Array.isArray(ssData.pstatList) ? ssData.pstatList : []; // 功能说明:个人统计图表配置 by xu 20260114
  1323. // 功能说明:如果首页返回预定/服务面板(rbarBook/rbarbook/rbarServ),则卡片主体点击切到 single(单选互斥),否则保持 view(查看) by xu 20260116
  1324. {
  1325. const tabs = Array.isArray(this.rbarTabNameList) ? this.rbarTabNameList : [];
  1326. const norm = (s) => String(s || "").trim().toLowerCase();
  1327. this.hasBookPanel = tabs.some((t) => norm(t) === "rbarbook"); // 功能说明:预定tab存在才回显bookList by xu 20260116
  1328. this.hasServPanel = tabs.some((t) => norm(t) === "rbarserv"); // 功能说明:服务tab存在才回显servList by xu 20260116
  1329. this.cardClickAction = (this.hasBookPanel || this.hasServPanel) ? "single" : "view"; // 功能说明:预定/服务任一存在则单选联动 by xu 20260116
  1330. }
  1331. this.searchButtonConfigCheckId = String(this.form?.management || "99");
  1332. // 用当前表单参数初始化 form(确保 v-model 有值)
  1333. const curParams = this.getSearchFormParams();
  1334. const nextForm = {...this.form, ...curParams};
  1335. this.searchFieldList.forEach((f) => {
  1336. if (f && f.name && !(f.name in nextForm)) nextForm[f.name] = "";
  1337. });
  1338. this.form = nextForm;
  1339. this.searchButtonConfigCheckId = String(this.form?.management || "99");
  1340. // 初始化列表(objList/draftList)
  1341. const nextDraft = Array.isArray(ssData.draftList) ? ssData.draftList.map((r) => this.normalizeObjToCard(r)) : [];
  1342. const nextList = Array.isArray(ssData.objList) ? ssData.objList.map((r) => this.normalizeObjToCard(r)) : [];
  1343. if (this.listConfig) {
  1344. this.listConfig.draftbox = nextDraft;
  1345. this.listConfig.list = nextList;
  1346. }
  1347. // 列表数据更新后,重新探测卡片网格类型
  1348. this.cardGridKind = this.detectCardGridKind();
  1349. // 初始化右侧栏(选项卡 + 功能)
  1350. const bizIcon = this.ssObjName ? ("menu-icon icon-obj-" + this.ssObjName) : "";
  1351. const pickedPanel = {
  1352. type: "list",
  1353. title: "已选",
  1354. count: this.selectedItems.length,
  1355. iconClass: bizIcon,
  1356. closable: true,
  1357. itemLayout: "person",
  1358. items: this.selectedItems,
  1359. mode: "selected",
  1360. itemAction: true,
  1361. onClear: () => this.clearSelectedItems(),
  1362. };
  1363. // 功能说明:面板 key 兼容后端大小写(rbarBook/rbarbook/rbarServ),并移除“对象”tab by xu 20260116
  1364. const titleMap = {rbarry: "人员", rbarbook: "预定", rbarserv: "服务"};
  1365. const tabs = Array.isArray(this.rbarTabNameList) ? this.rbarTabNameList : [];
  1366. const normKey = (s) => String(s || "").trim().toLowerCase();
  1367. const tabPanels = tabs.map((k) => {
  1368. const nk = normKey(k);
  1369. // 功能说明:右侧栏去掉“对象”tab(即使后端返回 rbarObj 也忽略) by xu 20260116
  1370. if (nk === "rbarobj") return null;
  1371. const p = {
  1372. type: "list",
  1373. _tabKey: nk, // 功能说明:保存原始 tab key(用于后续写回 items) by xu 20260114
  1374. title: titleMap[nk] || k,
  1375. iconClass: bizIcon,
  1376. items: [],
  1377. itemAction: false,
  1378. };
  1379. // 功能说明:右侧“预定/服务”面板复用“已选”tab 的 person 布局(title+meta),避免渲染 ss-sidebar-tag by xu 20260116
  1380. if (nk === "rbarbook") {
  1381. p.itemLayout = "person";
  1382. }
  1383. if (nk === "rbarserv") {
  1384. p.itemLayout = "simple"; // 功能说明:服务面板只回显 mc(名称),不需要 meta 槽位 by xu 20260116
  1385. }
  1386. // 功能说明:右侧“人员”面板先做前端 mock 搜索头(仅展示,不接后端) by xu 20260114
  1387. if (nk === "rbarry") {
  1388. p.iconClass = "menu-icon icon-obj-ry";
  1389. p.count = 0;
  1390. p.headerFilters = [
  1391. {
  1392. key: "keyword",
  1393. component: "ss-search-input",
  1394. props: {name: "keyword", placeholder: "人员", width: "140px"},
  1395. value: "",
  1396. },
  1397. ];
  1398. p.headerSearchButton = true;
  1399. p.onSearch = function (payload) {
  1400. try {
  1401. console.log("人员搜索", payload);
  1402. } catch (_) {
  1403. }
  1404. };
  1405. p.mode = "selected";
  1406. p.items = [];
  1407. p.itemAction = false;
  1408. }
  1409. return p;
  1410. }).filter(Boolean);
  1411. this.sidebarPanels = [pickedPanel, ...tabPanels];
  1412. try {
  1413. console.log("[pobj] sidebarPanels(init)", (this.sidebarPanels || []).map((p) => ({
  1414. title: p?.title,
  1415. type: p?.type,
  1416. tab: p?._tabKey
  1417. })));
  1418. } catch (_) {
  1419. } // 功能说明:打印初始化后的面板列表,排查缺少“对象/预定”tab by xu 20260116
  1420. // 功能说明:将 pstatList=51 聚拢为一个“报表”面板(type=report-table),用于右侧报表区 by xu 20260115
  1421. try {
  1422. const reportList = (Array.isArray(this.pstatList) ? this.pstatList : []).filter((it) => Number(it?.grtjlbm) === 51);
  1423. if (reportList.length) {
  1424. this.sidebarPanels = this.sidebarPanels.concat([{
  1425. type: "report-table",
  1426. title: "报表",
  1427. iconClass: bizIcon,
  1428. items: reportList,
  1429. // 功能说明:点击统计表单元格,优先按 fwm/bjm 组装后端 service 并弹窗 by xu 20260115
  1430. onOpen: (srv) => this.openServiceDialog(srv),
  1431. }]);
  1432. }
  1433. } catch (e) {
  1434. }
  1435. // 功能说明:将 pstatList=1 映射为右侧图表区(type=chart),并调 wrPchtPlay 拉取真实数据 by xu 20260114
  1436. const chartPanels = this.buildPstatChartPanels(bizIcon);
  1437. if (chartPanels.length) {
  1438. this.sidebarPanels = this.sidebarPanels.concat(chartPanels);
  1439. chartPanels.forEach((p) => this.loadPstatChartData(p));
  1440. // 功能说明:追加 chartPanels 后触发一次 resize,避免首次进入图表为灰色圆(布局未完成) by xu 20260114
  1441. try {
  1442. setTimeout(function () {
  1443. window.dispatchEvent(new Event("resize"));
  1444. }, 0);
  1445. } catch (_) {
  1446. }
  1447. }
  1448. this.sidebarButtons = this.rbarFuncList.map((f) => ({
  1449. id: f.id || f.servName,
  1450. text: f.desc || f.title || "",
  1451. onClick: () => this.openServiceDialog(f),
  1452. }));
  1453. },
  1454. handleRootFuncClick(func) {
  1455. this.openServiceDialog(func);
  1456. },
  1457. // 切换范围(所有/管理/创建/已办/停用)=> 刷新列表(不刷新右侧栏)
  1458. switchScope(management) {
  1459. if (this.loadingList) return;
  1460. this.form.management = String(management);
  1461. this.searchButtonConfigCheckId = String(management);
  1462. this.ssPaging.pageNo = 1;
  1463. this.userInteracted = true;
  1464. this.loadPobjList();
  1465. },
  1466. // 提取当前表单参数(用于 ajax 调用 /service?ssServ=...)
  1467. getSearchFormParams() {
  1468. const params = {};
  1469. try {
  1470. const arr = $("#myForm").serializeArray();
  1471. arr.forEach(({name, value}) => {
  1472. if (!name) return;
  1473. if (Object.prototype.hasOwnProperty.call(params, name)) {
  1474. const cur = params[name];
  1475. params[name] = Array.isArray(cur) ? cur.concat([value]) : [cur, value];
  1476. } else {
  1477. params[name] = value;
  1478. }
  1479. });
  1480. } catch (e) {
  1481. console.error("getSearchFormParams failed", e);
  1482. }
  1483. return params;
  1484. },
  1485. // 以 Vue data 为准,组装列表/搜索请求参数(避免依赖组件内部 input 的实现)
  1486. getPobjParams() {
  1487. const params = {};
  1488. const form = this.form || {};
  1489. Object.keys(form).forEach((k) => {
  1490. const v = form[k];
  1491. if (v === undefined || v === null) return;
  1492. if (typeof v === "object") return;
  1493. params[k] = v;
  1494. });
  1495. if (this.ssPaging) {
  1496. params.pageNo = this.ssPaging.pageNo;
  1497. params.rowNumPer = this.ssPaging.rowNumPer;
  1498. params.rowNum = this.ssPaging.rowNum;
  1499. }
  1500. return params;
  1501. },
  1502. // 通用:调用后端 service(统一返回原生 Promise,避免 jqXHR 不支持 finally/catch)
  1503. callSsService(ssServ, extraParams = {}) {
  1504. if (!ssServ) return Promise.resolve(null);
  1505. return new Promise((resolve) => {
  1506. // 功能说明:演示兜底——后端 wrPchtPlay 异常时,固定 grtjid 返回 mock 数据 by xu 20260116
  1507. try {
  1508. const serv = String(ssServ || "").trim();
  1509. const grtjid = Number(extraParams?.grtjid);
  1510. if (serv === "wrPchtPlay" && (grtjid === 57243 || grtjid === 57241)) {
  1511. const mockMap = {
  1512. 57243: {
  1513. ssCode: 0,
  1514. ssData: {
  1515. dataArray: [
  1516. {
  1517. tjzbm: 9005000201,
  1518. sjtjzbm: 90050002,
  1519. mc: "教室",
  1520. ms: "教室",
  1521. xh: 1,
  1522. sl: 7,
  1523. jsgs: "yfbl_js",
  1524. dxm: "cd"
  1525. },
  1526. {
  1527. tjzbm: 9005000202,
  1528. sjtjzbm: 90050002,
  1529. mc: "图书馆",
  1530. ms: "图书馆",
  1531. xh: 2,
  1532. sl: 8,
  1533. jsgs: "yfbl_tsg",
  1534. dxm: "cd"
  1535. },
  1536. {
  1537. tjzbm: 9005000203,
  1538. sjtjzbm: 90050002,
  1539. mc: "学生宿舍",
  1540. ms: "学生宿舍",
  1541. xh: 3,
  1542. sl: 5,
  1543. jsgs: "yfbl_xsss",
  1544. dxm: "cd"
  1545. },
  1546. {
  1547. tjzbm: 9005000204,
  1548. sjtjzbm: 90050002,
  1549. mc: "办公室",
  1550. ms: "办公室",
  1551. xh: 4,
  1552. sl: 17,
  1553. jsgs: "yfbl_bgs",
  1554. dxm: "cd"
  1555. },
  1556. {
  1557. tjzbm: 9005000205,
  1558. sjtjzbm: 90050002,
  1559. mc: "食堂",
  1560. ms: "食堂",
  1561. xh: 5,
  1562. sl: 8,
  1563. jsgs: "yfbl_st",
  1564. dxm: "cd"
  1565. },
  1566. {
  1567. tjzbm: 9005000206,
  1568. sjtjzbm: 90050002,
  1569. mc: "教师宿舍",
  1570. ms: "教师宿舍",
  1571. xh: 6,
  1572. sl: 9,
  1573. jsgs: "yfbl_jsss",
  1574. dxm: "cd"
  1575. },
  1576. ],
  1577. grtj: {
  1578. grtjid: 57243,
  1579. jlztm: 1,
  1580. yjtjzbm: 0,
  1581. mc: "各类用房详细分布",
  1582. tjtlbm: 21,
  1583. grtjlbm: 0,
  1584. xh: 0,
  1585. kd: 0,
  1586. gd: 0,
  1587. tabName: "grtj",
  1588. idName: "grtjid",
  1589. fieldTypeMap: {
  1590. kd: 2,
  1591. jlztm: 2,
  1592. xh: 2,
  1593. grtjlbm: 2,
  1594. gd: 2,
  1595. grtjid: 4,
  1596. tjtlbm: 2,
  1597. mc: 1,
  1598. dxm: 1,
  1599. yyryid: 5,
  1600. grtjmbid: 5,
  1601. yjtjzbm: 4
  1602. },
  1603. },
  1604. },
  1605. },
  1606. 57241: {
  1607. ssCode: 0,
  1608. ssData: {
  1609. dataArray: [
  1610. {
  1611. tjzbm: 9005000101,
  1612. sjtjzbm: 90050001,
  1613. mc: "教学及辅助用房",
  1614. ms: "教学及辅助用房",
  1615. xh: 1,
  1616. sl: 49,
  1617. jsgs: "jzmjbl_jxjfzyf",
  1618. dxm: "cd"
  1619. },
  1620. {
  1621. tjzbm: 9005000102,
  1622. sjtjzbm: 90050001,
  1623. mc: "行政办公用房",
  1624. ms: "行政办公用房",
  1625. xh: 2,
  1626. sl: 17,
  1627. jsgs: "jzmjbl_xzbgyf",
  1628. dxm: "cd"
  1629. },
  1630. {
  1631. tjzbm: 9005000103,
  1632. sjtjzbm: 90050001,
  1633. mc: "生活用房",
  1634. ms: "生活用房",
  1635. xh: 3,
  1636. sl: 25,
  1637. jsgs: "jzmjbl_shyf",
  1638. dxm: "cd"
  1639. },
  1640. {
  1641. tjzbm: 9005000104,
  1642. sjtjzbm: 90050001,
  1643. mc: "其他用房",
  1644. ms: "其他用房",
  1645. xh: 4,
  1646. sl: 5,
  1647. jsgs: "jzmjbl_qtyf",
  1648. dxm: "cd"
  1649. },
  1650. ],
  1651. grtj: {
  1652. grtjid: 57241,
  1653. jlztm: 1,
  1654. yjtjzbm: 0,
  1655. mc: "校舍建筑面积总体分布",
  1656. tjtlbm: 21,
  1657. grtjlbm: 0,
  1658. xh: 0,
  1659. kd: 0,
  1660. gd: 0,
  1661. tabName: "grtj",
  1662. idName: "grtjid",
  1663. fieldTypeMap: {
  1664. kd: 2,
  1665. jlztm: 2,
  1666. xh: 2,
  1667. grtjlbm: 2,
  1668. gd: 2,
  1669. grtjid: 4,
  1670. tjtlbm: 2,
  1671. mc: 1,
  1672. dxm: 1,
  1673. yyryid: 5,
  1674. grtjmbid: 5,
  1675. yjtjzbm: 4
  1676. },
  1677. },
  1678. },
  1679. },
  1680. };
  1681. return resolve(mockMap[String(grtjid)] || null);
  1682. }
  1683. } catch (e) {
  1684. }
  1685. $.ajax({
  1686. type: "get",
  1687. url: "/service",
  1688. data: {ssServ, ...extraParams},
  1689. dataType: "text",
  1690. })
  1691. .done((text) => {
  1692. if (typeof text !== "string") return resolve(text);
  1693. const t = text.trim();
  1694. if (!t) return resolve(null);
  1695. try {
  1696. return resolve(JSON.parse(t));
  1697. } catch (_) {
  1698. return resolve(text);
  1699. }
  1700. })
  1701. .fail((xhr, status, err) => {
  1702. console.error("callSsService failed", ssServ, status, err);
  1703. resolve(null);
  1704. });
  1705. });
  1706. },
  1707. // 初始化:拉取查询首页数据(搜索条件/初始列表/右侧面板等)
  1708. loadPobjHome() {
  1709. const ssServ = this.ssSearchPobjHomeServName;
  1710. if (!ssServ) return Promise.resolve(null);
  1711. if (this.loadingHome) return Promise.resolve(null);
  1712. const startedAt = Date.now();
  1713. this.loadingHome = true;
  1714. this.homeReady = false;
  1715. this.userInteracted = false;
  1716. const params = this.getPobjParams();
  1717. return this.callSsService(ssServ, params)
  1718. .then((res) => {
  1719. const payload = res && typeof res === "object" ? (res.ssData || res) : null;
  1720. this.applyPobjHomeData(payload);
  1721. console.log("[pobj] home", ssServ, res);
  1722. return res;
  1723. })
  1724. .finally(() => {
  1725. const delay = Math.max(0, 200 - (Date.now() - startedAt));
  1726. setTimeout(() => {
  1727. this.loadingHome = false;
  1728. }, delay);
  1729. });
  1730. },
  1731. applyPobjListData(ssData) {
  1732. if (!ssData || typeof ssData !== "object") return;
  1733. // thnType(若列表接口也返回,则更新;一般由首页接口返回)
  1734. if (ssData.thnType != null) this.thnType = Number(ssData.thnType || 0);
  1735. if (ssData.ssPaging) {
  1736. this.ssPaging = {
  1737. ...this.ssPaging,
  1738. ...ssData.ssPaging,
  1739. pageNo: Number(ssData.ssPaging.pageNo || 1),
  1740. rowNumPer: Number(ssData.ssPaging.rowNumPer || 12), // 功能说明:分页默认每页12条 by xu 20260116
  1741. rowNum: Number(ssData.ssPaging.rowNum || 0),
  1742. };
  1743. }
  1744. const nextList = Array.isArray(ssData.objList) ? ssData.objList.map((r) => this.normalizeObjToCard(r)) : [];
  1745. if (this.listConfig) this.listConfig.list = nextList;
  1746. this.cardGridKind = this.detectCardGridKind();
  1747. },
  1748. // 功能说明:将 <对象名>_cxycl 返回的 bookList 写回右侧预定面板 by xu 20260114
  1749. applyPobjYclData(ssData) {
  1750. // 功能说明:兼容返回结构(ssData 可能是整包 {ssCode,ssData} 或直接 ssData),避免“预定tab不回显” by xu 20260116
  1751. if (!ssData) return;
  1752. const data = (ssData && typeof ssData === "object" && ssData.ssData && typeof ssData.ssData === "object") ? ssData.ssData : ssData;
  1753. if (!data || typeof data !== "object") return;
  1754. const ensurePanel = (tabKeyLower, title) => {
  1755. // 功能说明:严格依赖首页 rbarTabNameList 决定是否允许创建面板:未返回 rbarServ 时即使 cxycl 有 servList 也不处理 by xu 20260116
  1756. if (tabKeyLower === "rbarbook" && !this.hasBookPanel) return null;
  1757. if (tabKeyLower === "rbarserv" && !this.hasServPanel) return null;
  1758. const panels = Array.isArray(this.sidebarPanels) ? this.sidebarPanels : [];
  1759. let p = panels.find((it) => String(it?._tabKey || "").toLowerCase() === tabKeyLower);
  1760. if (p) return p;
  1761. const idx = panels.findIndex((it) => it?.type === "chart");
  1762. const insertAt = idx >= 0 ? idx : panels.length;
  1763. p = {
  1764. type: "list",
  1765. _tabKey: tabKeyLower,
  1766. title,
  1767. iconClass: this.ssObjName ? ("menu-icon icon-obj-" + this.ssObjName) : "",
  1768. items: [],
  1769. itemAction: false,
  1770. };
  1771. // 功能说明:预定面板默认使用 person 布局(title+meta) by xu 20260116
  1772. if (tabKeyLower === "rbarbook") p.itemLayout = "person";
  1773. if (tabKeyLower === "rbarserv") p.itemLayout = "simple"; // 功能说明:服务面板只回显 mc(名称) by xu 20260116
  1774. panels.splice(insertAt, 0, p);
  1775. this.sidebarPanels = panels;
  1776. return p;
  1777. };
  1778. const fmtDt = (v) => { // 功能说明:JSP 页面避免使用 JS 模板字符串插值(可能被 JSP EL 干扰),统一用字符串拼接 by xu 20260114
  1779. if (v === undefined || v === null || v === "") return "";
  1780. const d = new Date(v);
  1781. if (!isNaN(d.getTime())) {
  1782. const pad2 = (n) => String(n).padStart(2, "0");
  1783. return (
  1784. String(d.getFullYear()) +
  1785. "-" +
  1786. pad2(d.getMonth() + 1) +
  1787. "-" +
  1788. pad2(d.getDate()) +
  1789. " " +
  1790. pad2(d.getHours()) +
  1791. ":" +
  1792. pad2(d.getMinutes())
  1793. );
  1794. }
  1795. return String(v);
  1796. };
  1797. const bookList = Array.isArray(data.bookList) ? data.bookList : [];
  1798. const servList = Array.isArray(data.servList) ? data.servList : []; // 功能说明:将 <对象名>_cxycl 返回的 servList 写回右侧服务面板 by xu 20260116
  1799. try {
  1800. console.log("[pobj] cxycl bookList", {len: bookList.length, bookList: bookList});
  1801. } catch (_) {
  1802. } // 功能说明:打印 cxycl 返回,排查“预定tab不回显” by xu 20260116
  1803. // 功能说明:无论 bookList 是否为空,都要写回面板(为空则清空旧数据),避免“切换对象但仍显示上一次预定人” by xu 20260114
  1804. const bookPanel = ensurePanel("rbarbook", "预定");
  1805. if (bookPanel) {
  1806. bookPanel.itemLayout = "person"; // 功能说明:预定面板复用“已选”tab 的 person 布局(title+中间 meta 槽位),不走 tags by xu 20260114
  1807. bookPanel.items = bookList.map((b) => ({
  1808. _raw: b,
  1809. id: b?.id,
  1810. title: String(b?.ydr ?? ""), // 功能说明:预定面板 title=预订人 by xu 20260114
  1811. meta: String(b?.ydrdh ?? ""), // 功能说明:预定面板中间槽位 meta=电话(同“已选”tab) by xu 20260114
  1812. tags: [], // 功能说明:预定面板不使用 tags(避免自定义标签样式) by xu 20260114
  1813. }));
  1814. bookPanel.count = bookPanel.items.length;
  1815. }
  1816. // 功能说明:服务面板按预定(book)回显逻辑写回(title+meta),为空也要清空 by xu 20260116
  1817. const servPanel = ensurePanel("rbarserv", "服务");
  1818. if (servPanel) {
  1819. servPanel.itemLayout = "simple"; // 功能说明:服务面板字段只有 id/mc,仅回显 mc(名称) by xu 20260116
  1820. servPanel.items = servList.map((s) => ({
  1821. _raw: s,
  1822. id: s?.id, // 功能说明:服务面板 id=数据库ID by xu 20260116
  1823. title: String(s?.mc ?? ""), // 功能说明:服务面板 title=mc(名称) by xu 20260116
  1824. tags: [],
  1825. }));
  1826. servPanel.count = servPanel.items.length;
  1827. }
  1828. // 功能说明:强制触发 sidebarPanels 更新,避免仅修改子对象导致视图未刷新(预定tab回显不更新) by xu 20260116
  1829. try {
  1830. this.sidebarPanels = (this.sidebarPanels || []).slice();
  1831. } catch (e) {
  1832. }
  1833. try {
  1834. console.log("[pobj] cxycl bookPanel(after)", {
  1835. title: bookPanel.title,
  1836. tab: bookPanel._tabKey,
  1837. count: bookPanel.count,
  1838. first: bookPanel.items?.[0],
  1839. });
  1840. } catch (_) {
  1841. } // 功能说明:打印写回后的面板状态,排查“预定tab不回显” by xu 20260116
  1842. },
  1843. // 功能说明:选中对象后联动调用 <对象名>_cxycl,填充右侧预定/服务面板 by xu 20260114
  1844. loadPobjYclByItem(item) {
  1845. if (!(this.hasBookPanel || this.hasServPanel)) return Promise.resolve(null); // 功能说明:预定/服务任一存在才调用 cxycl;否则不联动 by xu 20260116
  1846. const objNum = item?.objNum || item?._raw?.ssObjId || item?._raw?.ssObjId;
  1847. if (!objNum) return Promise.resolve(null);
  1848. const ssServ = this.ssObjName ? (String(this.ssObjName) + "_cxycl") : "";
  1849. if (!ssServ) return Promise.resolve(null);
  1850. const reqId = (this.__yclReqId = (this.__yclReqId || 0) + 1);
  1851. this.yclLoading = true;
  1852. // 功能说明:cxycl 需要同时传入 ssObjIdName=<对象名>id,且把对象ID用该字段名提交(如 cdid=90052) by xu 20260114
  1853. const ssObjIdName = String(this.ssObjName || "") + "id";
  1854. const params = {ssObjName: this.ssObjName, ssObjIdName: ssObjIdName, ssObjId: objNum};
  1855. params[ssObjIdName] = objNum;
  1856. return this.callSsService(ssServ, params)
  1857. .then((res) => {
  1858. if (reqId !== this.__yclReqId) return null;
  1859. // 功能说明:兼容 $.ajax/axios 等不同返回结构,避免取错层级导致 bookList 为空 by xu 20260116
  1860. const payload = res && typeof res === "object" ? (res.ssData || res?.data?.ssData || res?.data || res) : null;
  1861. this.yclResp = payload;
  1862. this.applyPobjYclData(payload);
  1863. return res;
  1864. })
  1865. .finally(() => {
  1866. if (reqId !== this.__yclReqId) return;
  1867. this.yclLoading = false;
  1868. });
  1869. },
  1870. // 拉取列表数据(分页/搜索等)
  1871. loadPobjList() {
  1872. const ssServ = this.ssSearchPobjListServName;
  1873. if (!ssServ) return Promise.resolve(null);
  1874. // 只有用户操作后才允许请求列表(避免初始化阶段出现第二个 loading)
  1875. if (!this.userInteracted) return Promise.resolve(null);
  1876. if (this.loadingList) return Promise.resolve(null);
  1877. const startedAt = Date.now();
  1878. this.loadingList = true;
  1879. // 点击后立即清空列表区域,避免用户误以为没点到
  1880. if (this.listConfig) {
  1881. this.listConfig.draftbox = [];
  1882. this.listConfig.list = [];
  1883. }
  1884. const params = this.getPobjParams();
  1885. return this.callSsService(ssServ, params)
  1886. .then((res) => {
  1887. const payload = res && typeof res === "object" ? (res.ssData || res) : null;
  1888. this.ssListResp = payload;
  1889. this.applyPobjListData(payload);
  1890. console.log("[pobj] list", ssServ, res);
  1891. return res;
  1892. })
  1893. .finally(() => {
  1894. const delay = Math.max(0, 200 - (Date.now() - startedAt));
  1895. setTimeout(() => {
  1896. this.loadingList = false;
  1897. }, delay);
  1898. });
  1899. },
  1900. // 初始化右侧边栏分区(已选 items 指向 selectedItems) by xu 20260113
  1901. initSidebarPanels() {
  1902. const bizIcon = this.ssObjName ? ('menu-icon icon-obj-' + this.ssObjName) : '';
  1903. // 初始只放“已选”,其他分区由首页接口(rbarTabNameList)初始化
  1904. this.sidebarPanels = [];
  1905. this.sidebarButtons = [];
  1906. },
  1907. // 更新已选分区计数 by xu 20260113
  1908. updatePickedPanelCount() {
  1909. const pickedPanel = this.sidebarPanels?.find?.((p) => p?.title === '已选');
  1910. if (pickedPanel) pickedPanel.count = this.selectedItems.length;
  1911. },
  1912. // 清空已选(右侧 header 清空按钮) by xu 20260113
  1913. clearSelectedItems() {
  1914. this.selectedItems.forEach((it) => {
  1915. if (it) it._ssSelected = false;
  1916. });
  1917. this.selectedItems.splice(0, this.selectedItems.length);
  1918. this.updatePickedPanelCount();
  1919. },
  1920. // 右侧边栏移除 -> 反向同步左侧卡片状态 by xu 20260113
  1921. handleSidebarRemove(item) {
  1922. if (!item) return;
  1923. item._ssSelected = false;
  1924. const idx = this.selectedItems.indexOf(item);
  1925. if (idx > -1) this.selectedItems.splice(idx, 1);
  1926. this.updatePickedPanelCount();
  1927. },
  1928. // 功能:探测当前业务列表卡片类型,用于网格列最小宽度 by xu 20260109
  1929. detectCardGridKind() {
  1930. const all = [].concat(this.listConfig?.draftbox || []).concat(this.listConfig?.list || []);
  1931. if (all.some(it => String(it?.thumbType || '').trim() === 'thumbnail')) return 'thumbnail';
  1932. if (all.some(it => !!it?.thumb || !!it?.thumbType)) return 'photo';
  1933. return 'none';
  1934. },
  1935. // 左侧卡片角标选中(多选)+ 卡片主体单选互斥(exclusive) by xu 20260109
  1936. handleToggleSelect({item, selected, exclusive}) {
  1937. if (!item) return;
  1938. // 已选分区:中间槽位 meta(可按业务改为后端字段) by xu 20260113
  1939. if (!item.meta) item.meta = String(item.objNum || '');
  1940. const idx = this.selectedItems.indexOf(item);
  1941. if (exclusive && selected && this.cardClickAction === 'single') {
  1942. this.selectedItems.forEach((it) => {
  1943. if (it && it !== item) it._ssSelected = false;
  1944. });
  1945. // 不要重新赋值数组,保留引用(供右侧面板使用) by xu 20260109
  1946. this.selectedItems.splice(0, this.selectedItems.length);
  1947. this.selectedItems.push(item);
  1948. this.updatePickedPanelCount();
  1949. // 功能说明:预定模式下,单选互斥选中后联动加载右侧预定/服务数据 by xu 20260114
  1950. this.loadPobjYclByItem(item);
  1951. return;
  1952. }
  1953. if (selected) {
  1954. if (idx === -1) this.selectedItems.push(item);
  1955. // 功能说明:预定模式下,多选角标选中也可触发联动(以最近选中项为准) by xu 20260114
  1956. this.loadPobjYclByItem(item);
  1957. } else {
  1958. if (idx > -1) this.selectedItems.splice(idx, 1);
  1959. }
  1960. this.updatePickedPanelCount();
  1961. },
  1962. handlePageChange({pageNo, rowNumPer, rowNum}) {
  1963. // 翻页:调用列表接口刷新(不刷新右侧栏)
  1964. if (this.loadingList) return;
  1965. this.ssPaging.pageNo = pageNo;
  1966. this.ssPaging.rowNumPer = rowNumPer;
  1967. this.ssPaging.rowNum = rowNum;
  1968. this.userInteracted = true;
  1969. this.loadPobjList();
  1970. },
  1971. search() {
  1972. // 搜索:回到第一页,调用列表接口刷新(不刷新右侧栏)
  1973. if (this.loadingList) return;
  1974. this.ssPaging.pageNo = 1;
  1975. this.userInteracted = true;
  1976. this.loadPobjList();
  1977. }
  1978. },
  1979. mounted() {
  1980. // 供 scope 回调引用(ss-search-button 的 opt.callback 里需要找到 vm)
  1981. window.__objListVm = this;
  1982. // 初始化:把 URL/form 上已有的参数合并到 form/ssPaging(支持回显 + ajax)
  1983. const curParams = this.getSearchFormParams();
  1984. this.form = {...this.form, ...curParams};
  1985. if (curParams.pageNo) this.ssPaging.pageNo = Number(curParams.pageNo) || 1;
  1986. if (curParams.rowNumPer) this.ssPaging.rowNumPer = Number(curParams.rowNumPer) || 12; // 功能说明:默认每页12条(兼容未传rowNumPer) by xu 20260116
  1987. this.searchButtonConfigCheckId = String(this.form?.management || "99");
  1988. // 初始化卡片网格类型(用于自动分列/宽度平分) by xu 20260109
  1989. this.cardGridKind = this.detectCardGridKind();
  1990. // 初始化右侧边栏分区 by xu 20260113
  1991. this.initSidebarPanels();
  1992. this.userInteracted = false;
  1993. // 先跑通首页接口:用于初始化顶部搜索条件、初始列表、右侧栏
  1994. this.loadPobjHome();
  1995. }
  1996. });
  1997. });
  1998. <%-- 原有的清令牌 --%>
  1999. tokenCleanser("<ss:serv name='ss.clearPageToken'/>", {tokenList: "<%= pageContext.getAttribute(ss.page.PageC.PAGE_tokenList)%>"});
  2000. </script>