index.vue 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  1. <template>
  2. <el-container v-loading="isAppLoadings" class="hc-layout-box" :class="[!isNullES(isLayout) && isLayout === 'no' ? 'is-no-layout' : '']">
  3. <div v-if="appTheme === 'dark'" class="hc-app-bg-box">
  4. <img :src="appViewBg" alt="">
  5. </div>
  6. <el-header class="hc-layout-header">
  7. <div class="hc-layout-header-logo" :style="`width: ${isCollapse ? '0px' : '200px'};`" @click="logoClick">
  8. <!-- <img id="logo-icon" :src="appLogoIcon" alt=""> -->
  9. <img v-show="!isCollapse" id="logo-name" :src="appLogoName" alt="">
  10. </div>
  11. <div class="header-top-collapse-bar" @click="collapseChange">
  12. <HcIcon v-if="isCollapse" name="menu-unfold" />
  13. <HcIcon v-else name="menu-fold" />
  14. </div>
  15. <div class="header-top-menu-bar">
  16. <HcTopMenuBar @load="topMenuLoad" @change="topMenuChange" />
  17. </div>
  18. <div class="header-content-bar">
  19. <HcCascader v-if="userRoleId !== website.role_id" @send="cascaderSend" @change="cascaderChange" />
  20. <div v-else class="hc-header-project-name-box" @click="userProjectClick">
  21. <HcIcon name="arrow-right" class="project-icon" />
  22. <div class="truncate">{{ projectInfoData?.projectName ?? '请先选择项目' }}</div>
  23. </div>
  24. <hc-upload-bar />
  25. <HelpInfoBar />
  26. <ConfigBar />
  27. <UserInfoBar @load="userInfoLoad" />
  28. </div>
  29. </el-header>
  30. <el-container class="hc-layout-container">
  31. <el-aside v-if="isAsideMenu" class="hc-layout-aside" :class="[isCollapse ? 'is-collapse' : '']" :width="isCollapse ? '90px' : '200px'">
  32. <MenuBar :collapse="isCollapse" :cur="menuBarKey" :datas="menuBarData" :msg-count="msgCount" @change="menuBarChange" />
  33. </el-aside>
  34. <el-main class="hc-layout-main">
  35. <div class="hc-router-menu-bar">
  36. <RouterMenu @load="routerMenuLoad" />
  37. </div>
  38. <div id="hc-main-box" class="hc-main-page">
  39. <div class="hc-main-body">
  40. <router-view v-if="reloadRouter" v-slot="{ Component }">
  41. <transition name="fade-transform">
  42. <keep-alive :max="10" exclude="HcDataV">
  43. <component :is="Component" :msg-count="msgCount" :msg-change="msgChange" />
  44. </keep-alive>
  45. </transition>
  46. </router-view>
  47. </div>
  48. </div>
  49. </el-main>
  50. </el-container>
  51. <hc-reminder v-model="isReminderShow" :text="isReminderText" />
  52. </el-container>
  53. </template>
  54. <script setup>
  55. import { nextTick, onMounted, onUnmounted, ref, watch } from 'vue'
  56. import { getObjValue, isNullES, useClick } from 'js-fast-way'
  57. import { HcSocket } from '~src/plugins/HcSocket'
  58. import { useRoute, useRouter } from 'vue-router'
  59. import { useAppStore } from '~src/store'
  60. import { initButtons } from '~sto/app'
  61. import { HcAnnouncement } from 'hc-vue3-ui'
  62. import { useProject } from '~sto/useProject'
  63. import website from '~src/config'
  64. //初始组合式
  65. const router = useRouter()
  66. const useRoutes = useRoute()
  67. const store = useAppStore()
  68. const reloadRouter = ref(false)
  69. const userRoleId = ref(store.getRoleId)
  70. const projectInfoData = ref(store.getProjectInfo)
  71. //子组件
  72. import HcTopMenuBar from './modules/HcTopMenu.vue'
  73. import HcCascader from './modules/Cascader.vue'
  74. import UserInfoBar from './modules/UserInfoBar.vue'
  75. import HelpInfoBar from './modules/HelpInfoBar.vue'
  76. import ConfigBar from './modules/ConfigBar.vue'
  77. import RouterMenu from './modules/RouterMenu.vue'
  78. import MenuBar from '~src/layout/modules/MenuBar.vue'
  79. import appViewBg from '~src/assets/view/bg.png'
  80. // logo
  81. const appLogoName = ref(store.getLogoName)
  82. const appTheme = ref(store.getTheme)
  83. //菜单数据
  84. const menuBarKey = ref('')
  85. const menuBarData = ref([])
  86. const isLayout = ref('')
  87. //获取项目信息
  88. const { isAppLoading } = useProject()
  89. //渲染完成
  90. onMounted(() => {
  91. const layout = useRoutes?.query?.layout, layout2 = store.isLayout
  92. isLayout.value = layout ?? layout2
  93. annRefs.value = []
  94. initButtons()
  95. })
  96. //监听layout
  97. watch(() => [useRoutes?.query?.layout, store.isLayout], ([layout, layout2]) => {
  98. isLayout.value = layout ?? layout2
  99. }, { deep: true })
  100. //监听
  101. watch(() => store.getTheme, (theme) => {
  102. appTheme.value = theme
  103. })
  104. //监听项目信息变化
  105. const isAppLoadings = ref(true)
  106. watch(() => isAppLoading.value, (res) => {
  107. if (!website.localModel) {
  108. reloadRouter.value = res
  109. isAppLoadings.value = !res
  110. } else {
  111. if (res) {
  112. setTimeout(() => {
  113. isAppLoadings.value = false
  114. }, 1000)
  115. } else {
  116. isAppLoadings.value = true
  117. }
  118. }
  119. }, { immediate:true })
  120. //路由信息
  121. const routerMenuLoad = ({ key }) => {
  122. menuBarKey.value = key
  123. }
  124. // 是否折叠
  125. const isCollapse = ref(false)
  126. const collapseChange = () => {
  127. const bool = !isCollapse.value
  128. isCollapse.value = bool
  129. store.setCollapse(bool)
  130. }
  131. //顶部菜单导航
  132. const isAsideMenu = ref(true)
  133. const topMenuLoad = () => {
  134. isAsideMenu.value = false
  135. }
  136. //顶部菜单导航被点击
  137. const topMenuChange = (data) => {
  138. if (!isNullES(data)) {
  139. menuBarData.value = data
  140. isAsideMenu.value = true
  141. }
  142. }
  143. //菜单被点击
  144. const menuBarChange = ({ code }) => {
  145. menuBarKey.value = code
  146. router.push({ name: code })
  147. }
  148. //消息数量
  149. const msgCount = ref({
  150. allCount: 0,
  151. taskCount: 0,
  152. messageCount: 0,
  153. messageCount_1: 0,
  154. messageCount_2: 0,
  155. messageCount_3: 0,
  156. messageCount_4: 0,
  157. messageCount_5: 0,
  158. })
  159. const msgChange = ref(0)
  160. //用户信息
  161. const userId = ref('')
  162. const userInfoLoad = ({ user_id }) => {
  163. userId.value = user_id
  164. }
  165. //项目合同段的ID
  166. let socket
  167. const cascaderSend = async ({ projectId, contractId }) => {
  168. await useClick()
  169. if (isNullES(contractId)) {
  170. //本地模式
  171. if (website.localModel) {
  172. window.$message?.error('项目信息不存在,请联系管理员')
  173. reloadRouter.value = false
  174. }
  175. return
  176. }
  177. //本地模式
  178. if (website.localModel) {
  179. setTimeout(() => {
  180. reloadRouter.value = true
  181. }, 1000)
  182. } else {
  183. reloadRouter.value = true
  184. }
  185. //链接webSocket
  186. if (!isNullES(socket)) socket.close()
  187. socket = new HcSocket({ projectId, contractId, userId: userId.value }, (res) => {
  188. socketData(res?.data)
  189. })
  190. }
  191. //长链接消息
  192. let annUpdateRef
  193. const annRefs = ref([])
  194. const isReminderShow = ref(false)
  195. const isReminderText = ref('')
  196. const socketData = async (res) => {
  197. console.log('socket:', res)
  198. const { type, data } = getObjValue(res)
  199. if (type === 'msgUpdateMsg') {
  200. closeAnnUpdate()
  201. //内容为空时,代表公告已经取消,由于前面已经关闭,所以不再创建
  202. if (isNullES(data)) return
  203. await nextTick()
  204. //系统更新公告,直接替换
  205. annUpdateRef = await HcAnnouncement({ type: 'update', data: data })
  206. } else if (type === 'msgSystemMsg') {
  207. //内容为空时,代表公告已经取消,由于前面已经关闭,所以不再创建
  208. if (isNullES(data)) {
  209. closeAnnFun()
  210. return
  211. }
  212. await nextTick()
  213. //普通公告,追加公告
  214. const ref = await HcAnnouncement({ type: 'system', data: data })
  215. annRefs.value.push(ref)
  216. } else if (type === 'msgLink') {
  217. if (store.isLogin) {
  218. socket.send('getMsg')
  219. store.setIsLogin(false)
  220. }
  221. } else if (type === 'msgCountDown') {
  222. //倒计时更新
  223. if (isNullES(data) || data <= 0) {
  224. closeReminder()
  225. return
  226. }
  227. isReminderText.value = `系统将在${data}秒后,进行更新`
  228. setReminderText(data - 1)
  229. isReminderShow.value = true
  230. }
  231. }
  232. //倒计时
  233. let timeRef
  234. let startTime
  235. const setReminderText = (totalTime) => {
  236. startTime = performance.now()
  237. const step = () => {
  238. const elapsedTime = Math.floor((performance.now() - startTime) / 1000)
  239. const remainingTime = totalTime - elapsedTime
  240. if (remainingTime < 0) {
  241. closeReminder()
  242. return
  243. }
  244. isReminderText.value = `系统将在${remainingTime}秒后,进行更新`
  245. requestAnimationFrame(step)
  246. }
  247. requestAnimationFrame(step)
  248. }
  249. //关闭倒计时
  250. const closeReminder = () => {
  251. isReminderShow.value = false
  252. if (!isNullES(timeRef)) {
  253. clearTimeout(timeRef)
  254. timeRef = null
  255. }
  256. }
  257. // 项目切换
  258. const cascaderChange = () => {
  259. reloadRouter.value = false
  260. }
  261. //首页
  262. const logoClick = () => {
  263. router.push({ name: 'datav' })
  264. }
  265. const userProjectClick = () => {
  266. router.push({ path: '/user/project' })
  267. }
  268. //关闭普通公告
  269. const closeAnnFun = () => {
  270. const refs = annRefs.value
  271. for (let i = 0; i < refs.length; i++) {
  272. if (!isNullES(refs[i])) {
  273. refs[i]?.close()
  274. }
  275. }
  276. annRefs.value = []
  277. }
  278. //关闭系统更新公告
  279. const closeAnnUpdate = () => {
  280. if (!isNullES(annUpdateRef)) {
  281. annUpdateRef.close()
  282. annUpdateRef = null
  283. }
  284. }
  285. //页面卸载
  286. onUnmounted(() => {
  287. if (!isNullES(socket)) socket.close()
  288. closeAnnFun()
  289. closeAnnUpdate()
  290. })
  291. </script>
  292. <style lang="scss">
  293. @import "./index.scss";
  294. </style>