HomeView.vue 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406
  1. <script setup lang="ts">
  2. import {ref, watch, onMounted, defineAsyncComponent} from 'vue'
  3. import {BasicApi} from '@/api/basic'
  4. import {RequestError} from '@/utils/request'
  5. import {isMobile} from "@/utils/useDeviceDetection.ts";
  6. import {useGlobalStore} from '@/stores/global'
  7. import {ElMessage} from 'element-plus'
  8. const SsHeader = defineAsyncComponent(() => import('@/components/SsHeader.vue'))
  9. const SsFooter = defineAsyncComponent(() => import('@/components/SsFooter.vue'))
  10. const SsNavigation = defineAsyncComponent(() => import('@/components/SsNavigation.vue'))
  11. const SsHeadline = defineAsyncComponent(() => import('@/components/SsHeadline.vue'))
  12. const SsInputBox = defineAsyncComponent(() => import('@/components/SsInputBox.vue'))
  13. const SsPanel = defineAsyncComponent(() => import('@/components/SsPanel.vue'))
  14. const SsCommonProblem = defineAsyncComponent(() => import('@/components/SsCommonProblem.vue'))
  15. const SsService = defineAsyncComponent(() => import('@/components/SsService.vue'))
  16. const SsGridEntrance = defineAsyncComponent(() => import('@/components/SsGridEntrance.vue'))
  17. const SsHotline = defineAsyncComponent(() => import('@/components/SsHotline.vue'))
  18. const SsOpinion = defineAsyncComponent(() => import('@/components/SsOpinion.vue'))
  19. const componentsLoaded = ref(0)
  20. const totalComponents = ref(8)
  21. const componentLoaded = () => {
  22. componentsLoaded.value++
  23. if (componentsLoaded.value >= totalComponents.value) {
  24. let warp = (document.querySelector('.first-loading-wrp') as HTMLElement | null)
  25. if (warp) {
  26. warp.style.display = 'none'
  27. }
  28. }
  29. }
  30. const global = useGlobalStore()
  31. const problemIsRefresh = ref(false)
  32. const mainBoxFlag = ref(true)
  33. const timeoutFlag = ref()
  34. const mainContentClass = ref({})
  35. const inputBox = ref<InstanceType<typeof SsInputBox> | null>(null)
  36. const commonProblem = ref<InstanceType<typeof SsCommonProblem> | null>(null)
  37. const hotlineRef = ref<InstanceType<typeof SsHotline> | null>(null)
  38. const opinionRef = ref<InstanceType<typeof SsOpinion> | null>(null)
  39. const loadBasicData = async () => {
  40. try {
  41. const result = await BasicApi.getWebSet()
  42. global.setBasic(result)
  43. } catch (error) {
  44. const err = error as RequestError
  45. ElMessage.warning(err.message || '请求错误')
  46. }
  47. }
  48. const refreshProblem = () => {
  49. problemIsRefresh.value = true
  50. if (commonProblem.value) {
  51. commonProblem.value.refresh()
  52. }
  53. }
  54. const problemLoadFinish = () => {
  55. problemIsRefresh.value = false
  56. }
  57. const editMessage = (message: string) => {
  58. if (inputBox.value) {
  59. inputBox.value.editMessage(message)
  60. }
  61. }
  62. const setMainContentClass = () => {
  63. mainContentClass.value = {
  64. 'input-line-1': global.spread && global.inputLine == 1,
  65. 'input-line-2': global.spread && global.inputLine == 2,
  66. 'input-line-3': global.spread && global.inputLine >= 3
  67. }
  68. }
  69. const quickSend = (msg: string) => {
  70. if (inputBox.value) {
  71. inputBox.value.quickSend(msg)
  72. }
  73. }
  74. const loadService = (categoryId: number, title: string, avatar: string) => {
  75. if (isMobile()) {
  76. global.setMenuSwitch(false)
  77. }
  78. if (inputBox.value) {
  79. inputBox.value.loadService(categoryId, title, avatar)
  80. }
  81. }
  82. const loadCommonProblem = () => {
  83. if (isMobile()) {
  84. global.setMenuSwitch(false)
  85. }
  86. if (inputBox.value) {
  87. inputBox.value.loadCommonProblem()
  88. }
  89. }
  90. const openHotlineDialog = () => {
  91. hotlineRef.value?.open()
  92. }
  93. const openOpinionDialog = () => {
  94. opinionRef.value?.open()
  95. }
  96. const openPolicyFile = () => {
  97. if (global.basic.policyFileUrl) {
  98. const newWindow = window.open(global.basic.policyFileUrl)
  99. if (newWindow) newWindow.opener = null
  100. }
  101. }
  102. const initPage = () => {
  103. let w = window.screen.width
  104. if (w <= 750) {
  105. global.setMenuSwitch(false)
  106. }
  107. setMainContentClass()
  108. }
  109. onMounted(() => {
  110. loadBasicData()
  111. initPage()
  112. window.addEventListener('resize', initPage)
  113. })
  114. watch(() => global.spread, (newValue, oldValue) => {
  115. setMainContentClass()
  116. if (timeoutFlag.value) {
  117. clearTimeout(timeoutFlag.value)
  118. }
  119. if (newValue) {
  120. timeoutFlag.value = setTimeout(() => {
  121. mainBoxFlag.value = false
  122. }, 50)
  123. } else {
  124. timeoutFlag.value = setTimeout(() => {
  125. mainBoxFlag.value = true
  126. }, 300)
  127. }
  128. })
  129. watch(() => global.inputLine, () => {
  130. setMainContentClass()
  131. })
  132. </script>
  133. <template>
  134. <div class="root">
  135. <ss-header @loaded="componentLoaded"/>
  136. <ss-navigation
  137. @open-hotline="openHotlineDialog"
  138. @open-opinion="openOpinionDialog"
  139. @quick-send="quickSend"
  140. @load-service="loadService"
  141. @load-common-problem="loadCommonProblem"
  142. @loaded="componentLoaded"
  143. />
  144. <div class="container" :class="{spread: global.spread, 'menu-close': !global.menuSwitch}">
  145. <div class="main">
  146. <div class="main-content" :class="mainContentClass">
  147. <ss-headline v-if="mainBoxFlag"/>
  148. <ss-input-box ref="inputBox" @loaded="componentLoaded"/>
  149. <div v-if="mainBoxFlag" class="main-box">
  150. <div class="ss-row ss-row-vertical">
  151. <div class="ss-col ss-col-service is-mobile">
  152. <ss-panel title="服务导航" :show-refresh="false">
  153. <ss-service @load-service="loadService" @loaded="componentLoaded"/>
  154. </ss-panel>
  155. </div>
  156. <div class="ss-col ss-col-service is-pc">
  157. <ss-panel title="服务导航" :show-refresh="false">
  158. <ss-service @load-service="loadService" @loaded="componentLoaded"/>
  159. </ss-panel>
  160. </div>
  161. <div class="ss-col ss-col-problem">
  162. <ss-panel title="常见问题" :show-refresh="true" :is-refresh="problemIsRefresh" @refresh="refreshProblem">
  163. <ss-common-problem ref="commonProblem" @quick-send="quickSend" @finish="problemLoadFinish" @loaded="componentLoaded"/>
  164. </ss-panel>
  165. </div>
  166. </div>
  167. <div class="ss-row ss-flex no-margin-bottom">
  168. <div class="ss-col">
  169. <ss-grid-entrance title="意见反馈" subject="诚谢建言,共筑精品" image="/images/bg07.png" @click="openOpinionDialog" @loaded="componentLoaded"/>
  170. </div>
  171. <div class="ss-col">
  172. <ss-grid-entrance title="政策文件" subject="便捷获取最新政策信息" image="/images/bg08.png" sub-class="yellow" @click="openPolicyFile" @loaded="componentLoaded"/>
  173. </div>
  174. </div>
  175. </div>
  176. </div>
  177. <ss-footer @loaded="componentLoaded"/>
  178. </div>
  179. </div>
  180. <ss-hotline ref="hotlineRef"/>
  181. <ss-opinion ref="opinionRef"/>
  182. </div>
  183. </template>
  184. <style lang="scss">
  185. .root {
  186. position: fixed;
  187. z-index: 1;
  188. top: 0;
  189. left: 0;
  190. right: 0;
  191. bottom: 0;
  192. .container {
  193. display: flex;
  194. position: absolute;
  195. top: 0;
  196. left: 0;
  197. right: 0;
  198. bottom: 0;
  199. z-index: 1;
  200. background: #ffffff url("/images/backgroup.png") no-repeat scroll center center;
  201. background-size: cover;
  202. .main {
  203. width: 100%;
  204. overflow: hidden auto;
  205. position: relative;
  206. .main-content {
  207. height: 100vh;
  208. padding: 4rem 0 2rem 0;
  209. overflow: hidden auto;
  210. position: relative;
  211. .main-box {
  212. max-width: calc(1200px + 2rem);
  213. margin: 0 auto 1.69rem auto;
  214. padding: 0 1rem;
  215. transition: all .3s linear;
  216. .ss-row {
  217. display: flex;
  218. justify-content: space-between;
  219. margin-bottom: 1.68rem;
  220. &.no-margin-bottom {
  221. margin-bottom: 0;
  222. }
  223. .ss-col {
  224. width: calc(50% - 0.9rem);
  225. }
  226. }
  227. }
  228. &.input-line-1 {
  229. padding-bottom: 9rem;
  230. }
  231. &.input-line-2 {
  232. padding-bottom: 10.5rem;
  233. }
  234. &.input-line-3 {
  235. padding-bottom: 12rem;
  236. }
  237. }
  238. }
  239. &.spread {
  240. padding-left: 18rem;
  241. background-image: none;
  242. .navigation {
  243. display: block;
  244. }
  245. .main {
  246. .main-content {
  247. padding-top: 3rem;
  248. padding-right: 0;
  249. padding-left: 0;
  250. }
  251. }
  252. &.menu-close {
  253. padding-left: 4.5rem;
  254. }
  255. }
  256. }
  257. }
  258. /* 1080x1920 中等尺寸设备优化 */
  259. @media screen and (min-width: 751px) and (max-width: 1200px) and (min-height: 1700px) {
  260. .root {
  261. .container {
  262. .main {
  263. .main-content {
  264. padding: 5rem 0 5rem 0; /* 增加底部 padding 为 Footer 留空间 */
  265. .main-box {
  266. max-width: calc(1100px + 2rem);
  267. margin: 0 auto 2rem auto;
  268. padding: 0 1.5rem;
  269. .ss-row {
  270. margin-bottom: 2rem;
  271. /* 垂直布局:服务导航在上,常见问题在下 */
  272. &.ss-row-vertical {
  273. display: block;
  274. .ss-col-service {
  275. width: 100%;
  276. margin-bottom: 1.5rem;
  277. }
  278. .ss-col-problem {
  279. width: 100%;
  280. }
  281. }
  282. .ss-col {
  283. width: calc(50% - 1rem);
  284. }
  285. }
  286. }
  287. &.input-line-1 {
  288. padding-bottom: 10rem;
  289. }
  290. &.input-line-2 {
  291. padding-bottom: 11.5rem;
  292. }
  293. &.input-line-3 {
  294. padding-bottom: 13rem;
  295. }
  296. }
  297. }
  298. }
  299. }
  300. }
  301. @media screen and (max-width: 750px) {
  302. .root {
  303. .container {
  304. .main {
  305. .main-content {
  306. padding-bottom: 0;
  307. .main-box {
  308. .ss-row {
  309. display: block;
  310. .ss-col {
  311. width: 100%;
  312. margin-bottom: 1rem;
  313. }
  314. &.ss-flex {
  315. display: flex;
  316. .ss-col {
  317. width: calc(50% - 0.47rem);
  318. margin-bottom: 0;
  319. }
  320. }
  321. }
  322. }
  323. &.input-line-1 {
  324. padding-bottom: 7rem;
  325. }
  326. &.input-line-2 {
  327. padding-bottom: 8.5rem;
  328. }
  329. &.input-line-3 {
  330. padding-bottom: 10rem;
  331. }
  332. }
  333. }
  334. &.spread {
  335. padding: 0;
  336. .main {
  337. .main-content {
  338. padding-top: 4rem;
  339. }
  340. }
  341. &.menu-close {
  342. padding-left: 0;
  343. }
  344. }
  345. }
  346. }
  347. }
  348. </style>