index.vue 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460
  1. <template>
  2. <el-container
  3. v-loading="isAppLoadings" class="hc-layout-box"
  4. :class="{ 'yn-theme': !isYunNanProject, 'is-no-layout': !isNullES(isLayout) && isLayout === 'no' }"
  5. >
  6. <el-header class="hc-layout-header">
  7. <!-- <div class="hc-layout-header-logo" :style="`width: ${isCollapse ? '0px' : '200px'};`" @click="logoClick">
  8. <img v-show="!isCollapse" id="logo-name" :src="appLogoName" alt="">
  9. </div> -->
  10. <div v-if="!isYunNanProject" class="hc-layout-header-logo" :style="`width: ${isCollapse ? '0px' : '200px'};`" @click="logoClick">
  11. <!-- <img id="logo-icon" :src="appLogoIcon" alt=""> -->
  12. <img v-show="!isCollapse" id="logo-name" :src="appLogoName" alt="">
  13. </div>
  14. <div v-else class="hc-layout-header-logo" @click="logoClick">
  15. <img id="logo-icon" :src="appLogoIcon" alt="">
  16. <!-- <img v-show="!isCollapse" id="logo-name" :src="appLogoName" alt=""> -->
  17. <div class="ml-2">
  18. <div class="text-18px font-900">工程项目档案资料管理系统</div>
  19. <div class="text-12px">Engineering Project File Manegement Platform</div>
  20. </div>
  21. </div>
  22. <div class="header-top-collapse-bar" @click="collapseChange">
  23. <HcIcon v-if="isCollapse" name="menu-unfold" />
  24. <HcIcon v-else name="menu-fold" />
  25. </div>
  26. <div class="header-top-menu-bar">
  27. <HcTopMenuBar v-if="!isYunNanProject" @load="topMenuLoad" @change="topMenuChange" />
  28. </div>
  29. <div class="header-content-bar">
  30. <HcCascader @send="cascaderSend" @change="cascaderChange" />
  31. <hc-upload-bar v-if="!isYunNanProject" />
  32. <hc-upload-bar v-else style="color:black" />
  33. <HelpInfoBar v-if="!isYunNanProject" />
  34. <ConfigBar v-if="!isYunNanProject" />
  35. <UserInfoBar @load="userInfoLoad" />
  36. </div>
  37. </el-header>
  38. <el-container class="hc-layout-container">
  39. <el-aside v-if="isAsideMenu" class="hc-layout-aside" :class="[isCollapse ? 'is-collapse' : '']" :width="isCollapse ? '90px' : '200px'">
  40. <MenuBar :collapse="isCollapse" :cur="menuBarKey" :datas="menuBarData" :msg-count="msgCount" @change="menuBarChange" />
  41. </el-aside>
  42. <el-main class="hc-layout-main">
  43. <div class="hc-router-menu-bar">
  44. <RouterMenu @load="routerMenuLoad" />
  45. </div>
  46. <div id="hc-main-box" class="hc-main-page">
  47. <div class="hc-main-body">
  48. <router-view v-if="reloadRouter" v-slot="{ Component }">
  49. <transition name="fade-transform">
  50. <keep-alive :max="10">
  51. <component :is="Component" :msg-count="msgCount" />
  52. </keep-alive>
  53. </transition>
  54. </router-view>
  55. </div>
  56. </div>
  57. </el-main>
  58. </el-container>
  59. <hc-reminder v-model="isReminderShow" :text="isReminderText" />
  60. </el-container>
  61. </template>
  62. <script setup>
  63. import { nextTick, onMounted, onUnmounted, ref, watch } from 'vue'
  64. import { useRoute, useRouter } from 'vue-router'
  65. import { getArrValue, getObjValue, isNullES, useClick } from 'js-fast-way'
  66. import { HcSocket } from '~src/plugins/HcSocket'
  67. import { HcAnnouncement } from 'hc-vue3-ui'
  68. import { useAppStore } from '~src/store'
  69. import { initButtons } from '~sto/app'
  70. import { useProject } from '~sto/useProject'
  71. import website from '~src/config'
  72. import appLogoIcon from '~src/assets/logo/yunnan.png'
  73. //初始组合式
  74. const router = useRouter()
  75. const useRoutes = useRoute()
  76. const store = useAppStore()
  77. const reloadRouter = ref(false)
  78. //获取项目信息
  79. const { isAppLoading } = useProject()
  80. //子组件
  81. import HcTopMenuBar from './modules/HcTopMenu.vue'
  82. import HcCascader from './modules/Cascader.vue'
  83. import UserInfoBar from './modules/UserInfoBar.vue'
  84. import HelpInfoBar from './modules/HelpInfoBar.vue'
  85. import ConfigBar from './modules/ConfigBar.vue'
  86. import RouterMenu from './modules/RouterMenu.vue'
  87. import MenuBar from '~src/layout/modules/MenuBar.vue'
  88. // logo
  89. const appLogoName = ref(store.getLogoName)
  90. //菜单数据
  91. const menuBarKey = ref('')
  92. const menuBarData = ref([])
  93. const isLayout = ref('')
  94. const projectId = ref(store.getProjectId)
  95. // 判断是否为云南项目
  96. const isYunNanProject = ref(projectId.value === '1904814720589430785')
  97. //渲染完成
  98. onMounted(async () => {
  99. const layout = useRoutes?.query?.layout, layout2 = store.isLayout
  100. isLayout.value = layout ?? layout2
  101. annRefs.value = []
  102. initButtons()
  103. // 确保初始化时加载样式
  104. currentStyle = await loadStyles()
  105. })
  106. //监听layout
  107. watch(() => [useRoutes?.query?.layout, store.isLayout], ([layout, layout2]) => {
  108. isLayout.value = layout ?? layout2
  109. }, { deep: true })
  110. //监听项目信息变化
  111. const isAppLoadings = ref(true)
  112. watch(() => isAppLoading.value, (res) => {
  113. if (!website.localModel) {
  114. reloadRouter.value = res
  115. isAppLoadings.value = !res
  116. } else {
  117. if (res) {
  118. setTimeout(() => {
  119. isAppLoadings.value = false
  120. }, 1000)
  121. } else {
  122. isAppLoadings.value = true
  123. }
  124. }
  125. }, { immediate:true })
  126. //路由信息
  127. const routerMenuLoad = ({ key }) => {
  128. menuBarKey.value = key
  129. }
  130. // 是否折叠
  131. const isCollapse = ref(false)
  132. const collapseChange = () => {
  133. const bool = !isCollapse.value
  134. isCollapse.value = bool
  135. store.setCollapse(bool)
  136. }
  137. //顶部菜单导航
  138. const isAsideMenu = ref(true)
  139. const topMenuLoad = () => {
  140. isAsideMenu.value = false
  141. }
  142. //顶部菜单导航被点击
  143. const topMenuChange = (data) => {
  144. if (!isYunNanProject.value && !isNullES(data)) {
  145. menuBarData.value = data
  146. isAsideMenu.value = true
  147. }
  148. }
  149. // 修改菜单数据的监听逻辑
  150. watch(() => store.getMenus, (val) => {
  151. if (isYunNanProject.value) {
  152. let newVal = getArrValue(val)
  153. let newArr = []
  154. for (let index = 0; index < newVal.length; index++) {
  155. const element = newVal[index]
  156. if (element.parentId === '0' && ( element.code !== 'tasks' && element.code !== 'patrol-menu' && element.code !== 'tentative-menu' )) {
  157. let arr2 = element.children
  158. if (arr2) {
  159. for (let index = 0; index < arr2.length; index++) {
  160. const element2 = arr2[index]
  161. newArr.push(element2)
  162. }
  163. }
  164. } else if (element.code === 'patrol-menu') {
  165. newArr.push(element)
  166. } else if (element.code === 'tasks') {
  167. // 先跳过 tasks,最后再添加
  168. newArr.push(element)
  169. }
  170. // 最后添加 tasks
  171. }
  172. let obj1 = newArr[newArr.length - 1]
  173. let obj2 = newArr[newArr.length - 2]
  174. newArr[newArr.length - 1] = obj2
  175. newArr[newArr.length - 2] = obj1
  176. menuBarData.value = newArr
  177. // menuBarData.value = getArrValue(val)
  178. }
  179. }, { immediate: true })
  180. //菜单被点击
  181. const menuBarChange = ({ code }) => {
  182. menuBarKey.value = code
  183. router.push({ name: code })
  184. }
  185. //消息数量
  186. const msgCount = ref({
  187. allCount: 0,
  188. taskCount: 0,
  189. messageCount: 0,
  190. messageCount_1: 0,
  191. messageCount_2: 0,
  192. messageCount_3: 0,
  193. messageCount_4: 0,
  194. messageCount_5: 0,
  195. })
  196. //用户信息
  197. const userId = ref('')
  198. const userInfoLoad = ({ user_id }) => {
  199. userId.value = user_id
  200. }
  201. //项目合同段的ID
  202. let socket
  203. const cascaderSend = async ({ projectId, contractId }) => {
  204. await useClick()
  205. if (isNullES(contractId)) {
  206. //本地模式
  207. if (website.localModel) {
  208. window.$message?.error('项目信息不存在,请联系管理员')
  209. reloadRouter.value = false
  210. }
  211. return
  212. }
  213. //本地模式
  214. if (website.localModel) {
  215. setTimeout(() => {
  216. reloadRouter.value = true
  217. }, 1000)
  218. } else {
  219. reloadRouter.value = true
  220. }
  221. //链接webSocket
  222. if (!isNullES(socket)) socket.close()
  223. socket = new HcSocket({ projectId, contractId, userId: userId.value }, (res) => {
  224. socketData(res?.data)
  225. })
  226. }
  227. //长链接消息
  228. let annUpdateRef
  229. const annRefs = ref([])
  230. const socketData = async (res) => {
  231. console.log('socket:', res)
  232. const { type, data } = getObjValue(res)
  233. if (type === 'msgUpdateMsg') {
  234. closeAnnUpdate()
  235. //内容为空时,代表公告已经取消,由于前面已经关闭,所以不再创建
  236. if (isNullES(data)) return
  237. await nextTick()
  238. //系统更新公告,直接替换
  239. annUpdateRef = await HcAnnouncement({ type: 'update', data: data })
  240. } else if (type === 'msgSystemMsg') {
  241. //内容为空时,代表公告已经取消,由于前面已经关闭,所以不再创建
  242. if (isNullES(data)) {
  243. closeAnnFun()
  244. return
  245. }
  246. await nextTick()
  247. //普通公告,追加公告
  248. const ref = await HcAnnouncement({ type: 'system', data: data })
  249. annRefs.value.push(ref)
  250. } else if (type === 'msgLink') {
  251. if (store.isLogin) {
  252. socket.send('getMsg')
  253. store.setIsLogin(false)
  254. }
  255. } else if (type === 'msgCountDown') {
  256. //倒计时更新
  257. if (isNullES(data) || data <= 0) {
  258. closeReminder()
  259. return
  260. }
  261. isReminderText.value = `系统将在${data}秒后,进行更新`
  262. setReminderText(data - 1)
  263. isReminderShow.value = true
  264. }
  265. }
  266. //倒计时
  267. let timeRef
  268. let startTime
  269. const isReminderShow = ref(false)
  270. const isReminderText = ref('')
  271. const setReminderText = (totalTime) => {
  272. startTime = performance.now()
  273. const step = () => {
  274. const elapsedTime = Math.floor((performance.now() - startTime) / 1000)
  275. const remainingTime = totalTime - elapsedTime
  276. if (remainingTime < 0) {
  277. closeReminder()
  278. return
  279. }
  280. isReminderText.value = `系统将在${remainingTime}秒后,进行更新`
  281. requestAnimationFrame(step)
  282. }
  283. requestAnimationFrame(step)
  284. }
  285. //关闭倒计时
  286. const closeReminder = () => {
  287. isReminderShow.value = false
  288. if (!isNullES(timeRef)) {
  289. clearTimeout(timeRef)
  290. timeRef = null
  291. }
  292. }
  293. // 项目切换
  294. const cascaderChange = () => {
  295. reloadRouter.value = false
  296. }
  297. //首页
  298. const logoClick = () => {
  299. router.push({ name: 'home-index' })
  300. }
  301. //关闭普通公告
  302. const closeAnnFun = () => {
  303. const refs = annRefs.value
  304. for (let i = 0; i < refs.length; i++) {
  305. if (!isNullES(refs[i])) {
  306. refs[i]?.close()
  307. }
  308. }
  309. annRefs.value = []
  310. }
  311. //关闭系统更新公告
  312. const closeAnnUpdate = () => {
  313. if (!isNullES(annUpdateRef)) {
  314. annUpdateRef.close()
  315. annUpdateRef = null
  316. }
  317. }
  318. //页面卸载
  319. onUnmounted(() => {
  320. if (!isNullES(socket)) socket.close()
  321. closeAnnFun()
  322. closeAnnUpdate()
  323. })
  324. // 动态加载样式
  325. const loadStyles = async () => {
  326. try {
  327. // 确保先移除旧样式
  328. if (currentStyle) {
  329. document.head.removeChild(currentStyle)
  330. currentStyle = null
  331. }
  332. const styleElement = document.createElement('style')
  333. styleElement.type = 'text/css'
  334. // 使用动态import加载样式模块
  335. const module = isYunNanProject.value
  336. ? await import('./test-yn/index-yn.scss?inline') // 添加?inline确保直接获取样式内容
  337. : await import('./index.scss?inline')
  338. styleElement.textContent = module.default
  339. document.head.appendChild(styleElement)
  340. currentStyle = styleElement
  341. // 强制触发重绘
  342. document.body.style.display = 'none'
  343. document.body.offsetHeight // 触发重排
  344. document.body.style.display = ''
  345. return styleElement
  346. } catch (error) {
  347. console.error('加载样式失败:', error)
  348. return null
  349. }
  350. }
  351. let currentStyle = null
  352. // 监听项目类型变化,动态切换样式
  353. watch(() => isYunNanProject.value, async (newVal) => {
  354. if (currentStyle) {
  355. document.head.removeChild(currentStyle)
  356. currentStyle = null
  357. }
  358. currentStyle = await loadStyles()
  359. console.log('样式已更新:', newVal ? 'yunnan' : 'default')
  360. }, { immediate: true })
  361. // 添加watch来更新isYunNanProject
  362. watch(() =>store.getProjectId, (newProjectId) => {
  363. isYunNanProject.value = newProjectId === '1904814720589430785'
  364. //重新加载菜单
  365. let val = store.getMenus
  366. if (!isYunNanProject.value) {
  367. return
  368. }
  369. let newVal = getArrValue(val)
  370. let newArr = []
  371. for (let index = 0; index < newVal.length; index++) {
  372. const element = newVal[index]
  373. if (element.parentId === '0' && ( element.code !== 'tasks' && element.code !== 'patrol-menu' && element.code !== 'tentative-menu' )) {
  374. let arr2 = element.children
  375. if (arr2) {
  376. for (let index = 0; index < arr2.length; index++) {
  377. const element2 = arr2[index]
  378. newArr.push(element2)
  379. }
  380. }
  381. } else if (element.code === 'patrol-menu') {
  382. newArr.push(element)
  383. } else if (element.code === 'tasks') {
  384. // 先跳过 tasks,最后再添加
  385. newArr.push(element)
  386. }
  387. // 最后添加 tasks
  388. }
  389. let obj1 = newArr[newArr.length - 1]
  390. let obj2 = newArr[newArr.length - 2]
  391. newArr[newArr.length - 1] = obj2
  392. newArr[newArr.length - 2] = obj1
  393. menuBarData.value = newArr
  394. console.log(menuBarData.value, 'menuBarData.value')
  395. isAsideMenu.value = true
  396. })
  397. </script>