mp_ccChk.html 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682
  1. <!DOCTYPE html>
  2. <html lang="zh-CN">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
  6. <title>审核</title>
  7. <script src="/js/mp_base/base.js"></script>
  8. <style>
  9. /* 防止Vue模板闪烁 */
  10. [v-cloak] {
  11. display: none !important;
  12. }
  13. #app {
  14. background: #f5f5f5;
  15. height: 100vh;
  16. display: flex;
  17. flex-direction: column;
  18. overflow: hidden; /* 防止页面级别滚动 */
  19. box-sizing: border-box; /* 确保padding计算在高度内 */
  20. }
  21. /* iframe样式 */
  22. .top-iframe {
  23. background: #fff;
  24. overflow: hidden;
  25. border: none;
  26. display: block;
  27. box-sizing: border-box;
  28. }
  29. .bottom-iframe {
  30. background: #fff;
  31. overflow: hidden;
  32. border: none;
  33. display: block;
  34. box-sizing: border-box;
  35. }
  36. .title{
  37. flex-shrink: 0;
  38. box-sizing: border-box;
  39. font-size: 16px;
  40. text-align: center;
  41. /* margin: 16px auto; */
  42. height: 48px;
  43. line-height: 48px;
  44. }
  45. /* 录入div样式 */
  46. .review-input-popup {
  47. position: fixed;
  48. bottom: 0;
  49. left: 0;
  50. right: 0;
  51. height: 50px;
  52. background: #e6e6e6;
  53. display: flex;
  54. align-items: center;
  55. padding: 0 0 0 10px;
  56. z-index: 1000;
  57. transform: translateY(100%);
  58. transition: transform 0.3s ease;
  59. box-sizing: border-box;
  60. }
  61. .review-input-popup.show {
  62. transform: translateY(0);
  63. }
  64. .review-input-wrapper {
  65. flex: 1;
  66. display: flex;
  67. align-items: center;
  68. gap: 4px;
  69. }
  70. .review-input {
  71. flex: 1;
  72. height: 32px;
  73. border: none;
  74. padding: 0 10px;
  75. font-size: 16px;
  76. background: #fff;
  77. outline: none;
  78. box-sizing: border-box;
  79. }
  80. .common-phrases-btn {
  81. padding: 6px 12px;
  82. background: #fff;
  83. border: none;
  84. font-size: 16px;
  85. color: #333;
  86. cursor: pointer;
  87. white-space: nowrap;
  88. margin-right: 4px;
  89. }
  90. .common-phrases-btn:hover {
  91. background: #e0e0e0;
  92. }
  93. .submit-btn {
  94. box-sizing: border-box;
  95. height: 50px;
  96. padding: 6px 16px;
  97. border: none;
  98. font-size: 16px;
  99. color: #fff;
  100. cursor: pointer;
  101. white-space: nowrap;
  102. transition: background-color 0.2s;
  103. }
  104. .submit-btn.agree {
  105. background: #585e6e;
  106. }
  107. .submit-btn.agree:active {
  108. background: #242835;
  109. }
  110. .submit-btn.reject {
  111. background: #e58846;
  112. }
  113. .submit-btn.reject:active {
  114. background: #eb6100;
  115. }
  116. /* 常用语popup */
  117. .common-phrases-popup {
  118. position: fixed;
  119. bottom: 50px;
  120. left: 10px;
  121. right: 10px;
  122. background: #fff;
  123. border-radius: 4px;
  124. box-shadow: 0 -2px 10px rgba(0,0,0,0.1);
  125. max-height: 200px;
  126. overflow-y: auto;
  127. z-index: 1001;
  128. display: none;
  129. }
  130. .common-phrases-popup.show {
  131. display: block;
  132. }
  133. .phrase-item {
  134. padding: 12px 16px;
  135. border-bottom: 1px solid #f0f0f0;
  136. cursor: pointer;
  137. font-size: 14px;
  138. }
  139. .phrase-item:hover {
  140. background: #f8f8f8;
  141. }
  142. .phrase-item:last-child {
  143. border-bottom: none;
  144. }
  145. /* 原ss-bottom隐藏 */
  146. .ss-bottom.hidden {
  147. display: none;
  148. }
  149. </style>
  150. </head>
  151. <body>
  152. <div id="app" v-cloak>
  153. <div class="title" @click="callApi">
  154. {{jdmc}}
  155. </div>
  156. <!-- 上方iframe - 基本信息区域 -->
  157. <iframe
  158. ref="topIframe"
  159. class="top-iframe"
  160. :src="`/page/mp_objInfo.html?sqid=${sqid}&shid=${shid}&bdlbm=${bdlbm}&dataType=${dataType}&encode_shid=${encode_shid}&ssObjName=${ssObjName}&ssObjId=${ssObjId}`"
  161. width="100%"
  162. frameborder="0">
  163. </iframe>
  164. <!-- 下方iframe - 审核列表区域 -->
  165. <iframe
  166. ref="bottomIframe"
  167. class="bottom-iframe"
  168. :src="`/page/mp_shList.html?sqid=${sqid}&shid=${shid}&bdlbm=${bdlbm}&dataType=${dataType}&encode_shid=${encode_shid}&ssObjName=${ssObjName}&ssObjId=${ssObjId}`"
  169. width="100%"
  170. frameborder="0">
  171. </iframe>
  172. <!-- 底部按钮 -->
  173. <ss-bottom
  174. :show-shyj="false"
  175. :buttons="bottomButtons"
  176. @button-click="handleBottomAction"
  177. :divider="false"
  178. :disabled="submitting"
  179. v-if="bottomButtons.length > 0"
  180. ></ss-bottom>
  181. </div>
  182. <script>
  183. // 等待SS框架加载完成
  184. window.SS.ready(function () {
  185. // 使用SS框架的方式创建Vue实例
  186. window.SS.dom.initializeFormApp({
  187. el: '#app',
  188. data() {
  189. return {
  190. pageParams: {},
  191. loading: false,
  192. jdmc:'',
  193. sqid: '',
  194. shid: '',
  195. bdlbm: '',
  196. dataType:'bdplay',
  197. encode_shid:'',
  198. ssObjName:'',
  199. ssObjId:'',
  200. agrCcData: null, // 存储agrCc返回的数据
  201. // 底部按钮相关
  202. submitting: false,
  203. bottomButtons: [],
  204. // iframe布局相关
  205. layoutCalculated: false,
  206. headerSectionHeight: 0,
  207. availableHeight: 0,
  208. topIframeHeight: 0,
  209. bottomIframeHeight: 0,
  210. bottomIframeMinHeight: 0,
  211. isExpanded: false,
  212. // 拖拽相关
  213. isDragging: false,
  214. dragStartY: 0,
  215. dragStartBottomHeight: 0,
  216. }
  217. },
  218. mounted() {
  219. // 获取URL参数
  220. this.pageParams = this.getUrlParams()
  221. console.log('🔗 mp_ccChk页面接收到参数:', this.pageParams)
  222. // 打印所有参数到控制台
  223. Object.keys(this.pageParams).forEach(key => {
  224. console.log(`参数 ${key}:`, this.pageParams[key])
  225. })
  226. this.callApi()
  227. // 添加iframe消息监听器
  228. window.addEventListener('message', this.handleIframeMessage)
  229. // 初始化布局计算
  230. this.$nextTick(() => {
  231. setTimeout(() => {
  232. this.initializeLayout()
  233. }, 500) // 等待iframe加载完成
  234. })
  235. },
  236. beforeDestroy() {
  237. // 清理事件监听器
  238. window.removeEventListener('message', this.handleIframeMessage)
  239. },
  240. methods: {
  241. // 获取URL参数
  242. getUrlParams() {
  243. const params = {}
  244. const urlSearchParams = new URLSearchParams(window.location.search)
  245. for (const [key, value] of urlSearchParams) {
  246. params[key] = decodeURIComponent(value)
  247. }
  248. return params
  249. },
  250. // 调用API
  251. async callApi() {
  252. if (!this.pageParams.ssToken) {
  253. alert('未找到ssToken参数')
  254. return
  255. }
  256. this.loading = true
  257. this.apiResult = null
  258. try {
  259. console.log('🚀 开始调用API,ssToken:', this.pageParams.ssToken)
  260. const apiResponse = await request.post(
  261. `/service?ssToken=${this.pageParams.ssToken}`,
  262. {},
  263. { loading: false }
  264. )
  265. console.log('✅ 原有API响应:', apiResponse)
  266. this.sqid = apiResponse.data.sqid
  267. this.shid = apiResponse.data.shid
  268. this.ssObjName = apiResponse.data.ssObjName
  269. this.ssObjId = apiResponse.data.ssObjId
  270. this.bdlbm = apiResponse.data.bdlbm
  271. this.jdmc = decodeURIComponent(apiResponse.data.jdmc)
  272. console.log('📋 调用dataTag服务获取agrCc数据...')
  273. const agrCcResponse = await request.post(
  274. `/service?ssServ=dataTag&ssDest=data&name=agrCc&ssObjName=${this.ssObjName}&ssObjId=${this.ssObjId}&sqid=${this.sqid}&shid=${this.shid}&bdlbm=${this.bdlbm}&dataType=${this.dataType}&encode_shid=${this.encode_shid}`,
  275. { loading: false,formData:true }
  276. )
  277. // 保存agrCc数据并设置底部按钮
  278. if (agrCcResponse && agrCcResponse.data && agrCcResponse.data.agrCc) {
  279. this.agrCcData = agrCcResponse.data.agrCc
  280. console.log('📦 agrCc数据:', this.agrCcData)
  281. // 根据agrCc数据设置底部按钮
  282. this.bottomButtons = [{
  283. text: '确认',
  284. action: 'submit',
  285. backgroundColor: '#585e6e',
  286. color: '#fff',
  287. clickBgColor: '#242835'
  288. }]
  289. }
  290. } catch (error) {
  291. console.error('❌ API调用失败:', error)
  292. } finally {
  293. this.loading = false
  294. }
  295. },
  296. // 处理底部按钮点击
  297. handleBottomAction(data) {
  298. if (data.action === 'submit') {
  299. this.handleConfirm()
  300. }
  301. },
  302. // 处理确认提交
  303. async handleConfirm() {
  304. if (!this.agrCcData) {
  305. alert('缺少必要的配置信息')
  306. return
  307. }
  308. if (this.submitting) return
  309. try {
  310. this.submitting = true
  311. console.log('📝 开始提交...')
  312. console.log('📦 提交参数:', {
  313. service: this.agrCcData.service,
  314. dest: this.agrCcData.dest,
  315. shid: this.shid
  316. })
  317. const response = await request.post(
  318. `/service?ssServ=${this.agrCcData.service}&ssDest=${this.agrCcData.dest}`,
  319. { shid: this.shid },
  320. { loading: true, formData: true }
  321. )
  322. console.log('✅ 提交成功:', response)
  323. NavigationManager.goBack({ refreshParent: true })
  324. } catch (error) {
  325. console.error('❌ 提交失败:', error)
  326. alert('提交失败: ' + (error.message || '未知错误'))
  327. } finally {
  328. this.submitting = false
  329. }
  330. },
  331. // ===== 布局相关方法 =====
  332. // 初始化布局计算
  333. initializeLayout() {
  334. console.log('🔄 开始初始化布局计算')
  335. try {
  336. // 获取bottom iframe
  337. const bottomIframe = document.querySelector('.bottom-iframe')
  338. const bottomWindow = bottomIframe?.contentWindow
  339. if (!bottomWindow) {
  340. console.error('无法获取bottom iframe的contentWindow')
  341. return
  342. }
  343. // 等待iframe加载完成,然后检查header-section
  344. const waitForIframeLoad = () => {
  345. if (!bottomIframe.contentWindow) {
  346. console.log('⏳ 等待iframe contentWindow...')
  347. setTimeout(waitForIframeLoad, 100)
  348. return
  349. }
  350. const bottomWindow = bottomIframe.contentWindow
  351. console.log('✅ 获取到iframe contentWindow')
  352. // 等待iframe中的DOM加载完成
  353. const checkHeaderSection = () => {
  354. try {
  355. const headerSection = bottomWindow.document.querySelector('.header-section')
  356. if (headerSection) {
  357. const headerHeight = headerSection.offsetHeight
  358. console.log('✅ 计算到header-section高度:', headerHeight)
  359. // 计算布局参数
  360. const viewportHeight = window.innerHeight
  361. // 动态获取title的实际高度
  362. const titleElement = document.querySelector('.title')
  363. const actualTitleHeight = titleElement ? titleElement.offsetHeight : 0
  364. // 等待bottom按钮渲染完成并获取实际高度
  365. const checkBottomButton = () => {
  366. try {
  367. const bottomElement = document.querySelector('ss-bottom')
  368. const actualBottomHeight = bottomElement ? bottomElement.offsetHeight : 50
  369. this.headerSectionHeight = headerHeight
  370. this.bottomIframeMinHeight = headerHeight
  371. this.availableHeight = viewportHeight - actualTitleHeight - actualBottomHeight
  372. // 初始状态:底部iframe刚好显示header-section高度
  373. this.bottomIframeHeight = this.bottomIframeMinHeight
  374. this.topIframeHeight = this.availableHeight - this.bottomIframeMinHeight
  375. this.isExpanded = false // 初始为收起状态
  376. this.layoutCalculated = true
  377. // 应用计算后的高度
  378. this.applyIframeHeights()
  379. console.log('📊 初始布局计算完成(仅显示header-section):', {
  380. '视口高度': viewportHeight,
  381. '标题实际高度': actualTitleHeight,
  382. '底部按钮实际高度': actualBottomHeight,
  383. '可用总高度': this.availableHeight,
  384. 'header-section高度': headerHeight,
  385. '上iframe高度': this.topIframeHeight,
  386. '下iframe高度': this.bottomIframeHeight,
  387. 'iframe总高度': this.topIframeHeight + this.bottomIframeHeight,
  388. '是否超限': (this.topIframeHeight + this.bottomIframeHeight) > this.availableHeight,
  389. '差额': (this.topIframeHeight + this.bottomIframeHeight) - this.availableHeight,
  390. isExpanded: this.isExpanded
  391. })
  392. } catch (bottomError) {
  393. console.error('❌ 获取底部按钮高度时出错:', bottomError)
  394. // 使用默认高度50px重试
  395. const defaultBottomHeight = 50
  396. this.headerSectionHeight = headerHeight
  397. this.bottomIframeMinHeight = headerHeight
  398. this.availableHeight = viewportHeight - actualTitleHeight - defaultBottomHeight
  399. this.bottomIframeHeight = this.bottomIframeMinHeight
  400. this.topIframeHeight = this.availableHeight - this.bottomIframeMinHeight
  401. this.isExpanded = false
  402. this.layoutCalculated = true
  403. this.applyIframeHeights()
  404. }
  405. }
  406. // 如果bottom按钮还没加载,稍后重试
  407. if (!document.querySelector('ss-bottom')) {
  408. setTimeout(checkBottomButton, 100)
  409. } else {
  410. checkBottomButton()
  411. }
  412. } else {
  413. // 如果还没有加载完成,继续等待
  414. console.log('⏳ 等待header-section加载...')
  415. setTimeout(checkHeaderSection, 100)
  416. }
  417. } catch (error) {
  418. console.error('❌ 访问iframe DOM时出错:', error)
  419. console.log('⏳ 重试中...')
  420. setTimeout(checkHeaderSection, 200)
  421. }
  422. }
  423. checkHeaderSection()
  424. }
  425. waitForIframeLoad()
  426. } catch (error) {
  427. console.error('❌ 布局初始化失败:', error)
  428. }
  429. },
  430. // 应用iframe高度
  431. applyIframeHeights() {
  432. const topIframe = document.querySelector('.top-iframe')
  433. const bottomIframe = document.querySelector('.bottom-iframe')
  434. if (topIframe && bottomIframe) {
  435. console.log('📏 设置iframe高度前:', {
  436. topIframe: {
  437. currentHeight: topIframe.style.height,
  438. offsetHeight: topIframe.offsetHeight
  439. },
  440. bottomIframe: {
  441. currentHeight: bottomIframe.style.height,
  442. offsetHeight: bottomIframe.offsetHeight
  443. },
  444. newTopHeight: this.topIframeHeight,
  445. newBottomHeight: this.bottomIframeHeight
  446. })
  447. // 直接应用新高度到iframe
  448. topIframe.style.height = `${this.topIframeHeight}px`
  449. bottomIframe.style.height = `${this.bottomIframeHeight}px`
  450. console.log('🎯 应用iframe高度:', {
  451. top: this.topIframeHeight,
  452. bottom: this.bottomIframeHeight
  453. })
  454. // 强制重新计算iframe内容
  455. this.$nextTick(() => {
  456. try {
  457. topIframe.contentWindow.dispatchEvent(new Event('resize'))
  458. } catch (e) {
  459. // 忽略跨域错误
  460. }
  461. try {
  462. bottomIframe.contentWindow.dispatchEvent(new Event('resize'))
  463. } catch (e) {
  464. // 忽略跨域错误
  465. }
  466. console.log('✅ iframe高度更新完成')
  467. })
  468. }
  469. },
  470. // 快速版本的高度应用,用于拖拽时减少延迟
  471. applyIframeHeightsFast() {
  472. const topIframe = document.querySelector('.top-iframe')
  473. const bottomIframe = document.querySelector('.bottom-iframe')
  474. if (topIframe && bottomIframe) {
  475. // 直接设置高度,不进行日志和额外的处理
  476. topIframe.style.height = `${this.topIframeHeight}px`
  477. bottomIframe.style.height = `${this.bottomIframeHeight}px`
  478. }
  479. },
  480. // 计算iframe高度分配
  481. calculateHeights(bottomHeight) {
  482. // 计算实际可用的bottom iframe高度
  483. // 当bottom iframe展开时,可以覆盖top iframe,所以最大高度就是availableHeight
  484. // 最小高度不能小于header-section高度
  485. const bottomActualHeight = Math.max(
  486. this.bottomIframeMinHeight,
  487. Math.min(bottomHeight, this.availableHeight)
  488. )
  489. this.bottomIframeHeight = bottomActualHeight
  490. this.topIframeHeight = this.availableHeight - bottomActualHeight
  491. console.log('📐 高度分配计算:', {
  492. requestedBottom: bottomHeight,
  493. availableHeight: this.availableHeight,
  494. minBottomHeight: this.bottomIframeMinHeight,
  495. actualBottom: this.bottomIframeHeight,
  496. actualTop: this.topIframeHeight,
  497. isExpanded: this.isExpanded
  498. })
  499. this.applyIframeHeights()
  500. return {
  501. top: this.topIframeHeight,
  502. bottom: this.bottomIframeHeight
  503. }
  504. },
  505. // 切换展开/收起状态
  506. toggleExpand() {
  507. if (!this.layoutCalculated) {
  508. console.warn('布局尚未计算完成')
  509. return
  510. }
  511. this.isExpanded = !this.isExpanded
  512. if (this.isExpanded) {
  513. // 展开:bottom iframe占用所有可用高度,覆盖top iframe到title底部
  514. // 也就是bottom iframe高度 = availableHeight (title + button之间的所有空间)
  515. this.calculateHeights(this.availableHeight)
  516. console.log('📈 展开状态 - 覆盖到title底部,高度:', this.availableHeight)
  517. } else {
  518. // 收起:bottom iframe占用最小高度(header-section高度)
  519. this.calculateHeights(this.bottomIframeMinHeight)
  520. console.log('📉 收起状态 - 最小高度:', this.bottomIframeMinHeight)
  521. }
  522. },
  523. // 处理来自iframe的消息
  524. handleIframeMessage(event) {
  525. // 安全检查:只接受同源的消息
  526. if (event.origin !== window.location.origin) {
  527. return
  528. }
  529. const { type, data } = event.data
  530. switch (type) {
  531. case 'header-section-click':
  532. console.log('📱 收到header-section点击事件')
  533. this.toggleExpand()
  534. break
  535. case 'header-section-drag-start':
  536. console.log('🔄 开始拖拽header-section')
  537. this.handleDragStart(data)
  538. break
  539. case 'header-section-drag-move':
  540. console.log('🔄 拖拽中:', data.deltaY)
  541. this.handleDragMove(data.deltaY)
  542. break
  543. case 'header-section-drag-end':
  544. console.log('🔚 结束拖拽')
  545. this.handleDragEnd()
  546. break
  547. }
  548. },
  549. // 处理拖拽开始
  550. handleDragStart(data) {
  551. if (!this.layoutCalculated) return
  552. this.isDragging = true
  553. this.dragStartY = data.startY
  554. this.dragStartBottomHeight = this.bottomIframeHeight
  555. },
  556. // 处理拖拽移动
  557. handleDragMove(deltaY) {
  558. if (!this.isDragging || !this.layoutCalculated) return
  559. // 计算新的底部高度
  560. const newBottomHeight = this.dragStartBottomHeight - deltaY
  561. // 直接设置高度,避免复杂的计算导致的延迟
  562. const bottomActualHeight = Math.max(
  563. this.bottomIframeMinHeight,
  564. Math.min(newBottomHeight, this.availableHeight)
  565. )
  566. this.bottomIframeHeight = bottomActualHeight
  567. this.topIframeHeight = this.availableHeight - bottomActualHeight
  568. // 直接应用高度,减少延迟
  569. this.applyIframeHeightsFast()
  570. },
  571. // 处理拖拽结束
  572. handleDragEnd() {
  573. this.isDragging = false
  574. this.dragStartY = 0
  575. this.dragStartBottomHeight = 0
  576. // 拖拽结束后用完整的方法确保状态正确
  577. this.applyIframeHeights()
  578. },
  579. }
  580. })
  581. })
  582. </script>
  583. </body>