History.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425
  1. <template>
  2. <div class="history">
  3. <iframe
  4. id="iframe-echart"
  5. src="./chart.html"
  6. frameborder="0"
  7. />
  8. <h1>
  9. {{ timeNameList[activeTimeIdx] }}
  10. </h1>
  11. <menu>
  12. <img
  13. class="circle-no-light"
  14. src="@/assets/images/circle-no-light.png"
  15. alt=""
  16. draggable="false"
  17. >
  18. <img
  19. class="circle"
  20. src="@/assets/images/circle.png"
  21. alt=""
  22. draggable="false"
  23. >
  24. <ul>
  25. <li
  26. v-for="(time, idx) in timeNameList"
  27. :key="idx"
  28. :class="{active: activeTimeIdx === idx}"
  29. @click="onClickTimeItem(idx)"
  30. >
  31. <div
  32. class="point"
  33. />
  34. {{ time }}
  35. </li>
  36. </ul>
  37. </menu>
  38. <article>
  39. <h2>{{ articleTitle }}</h2>
  40. <img
  41. class="splitter"
  42. src="@/assets/images/splitter-history-top.png"
  43. alt=""
  44. draggable="false"
  45. >
  46. <div class="txt-wrapper">
  47. <img
  48. v-show="articleBannerUrl"
  49. class="banner"
  50. :src="articleBannerUrl"
  51. alt=""
  52. draggable="false"
  53. >
  54. <div
  55. class="txt"
  56. v-html="articleDesc"
  57. />
  58. <button
  59. v-show="activeTimeIdx !== 0"
  60. class="left"
  61. @click="onClickLeftArrow"
  62. />
  63. <button
  64. v-show="activeTimeIdx !== timeNameList.length - 1"
  65. class="right"
  66. @click="onClickRightArrow"
  67. />
  68. </div>
  69. <img
  70. class="splitter"
  71. src="@/assets/images/splitter-history-bottom.png"
  72. alt=""
  73. draggable="false"
  74. >
  75. </article>
  76. </div>
  77. </template>
  78. <script>
  79. import {
  80. ref,
  81. reactive,
  82. onMounted,
  83. onBeforeUnmount,
  84. computed,
  85. watch,
  86. } from 'vue'
  87. import dataRaw from "@/assets/mock/history.json"
  88. export default {
  89. name: 'HistoryView',
  90. setup () {
  91. const myIframe = ref(null)
  92. const timeNameList = [
  93. '开埠通商',
  94. '曲折发展',
  95. '步履维艰',
  96. '筚路蓝缕',
  97. '改革开放',
  98. '战略负重',
  99. '创新驱动',
  100. '追梦未来',
  101. ]
  102. const corpList = reactive({
  103. value: [],
  104. })
  105. const activeTimeIdx = ref(0)
  106. function onClickLeftArrow() {
  107. if (activeTimeIdx.value > 0) {
  108. activeTimeIdx.value--
  109. }
  110. }
  111. function onClickRightArrow() {
  112. if (activeTimeIdx.value < timeNameList.length - 1) {
  113. activeTimeIdx.value++
  114. }
  115. }
  116. watch(activeTimeIdx, async (newV) => {
  117. if (document.getElementById('iframe-echart')?.contentWindow?.changeTime) {
  118. document.getElementById('iframe-echart').contentWindow.changeTime(newV)
  119. }
  120. corpList.value = await api.getHistoryList({ stage: timeNameList[newV] })
  121. }, { immediate: true })
  122. const activeCorpId = ref('')
  123. const articleTitle = computed(() => {
  124. if (activeCorpId.value === '') {
  125. return ''
  126. } else if (activeCorpId.value === '-1') {
  127. return timeNameList[activeTimeIdx.value]
  128. } else {
  129. const targetCorp = corpList.value.find((item) => {
  130. return item.id.toString() === activeCorpId.value.toString()
  131. })
  132. return targetCorp?.name || targetCorp?.companyName || ''
  133. }
  134. })
  135. const articleDesc = computed(() => {
  136. if (activeCorpId.value === '') {
  137. return ''
  138. } else if (activeCorpId.value === '-1') {
  139. return dataRaw['阶段介绍'][activeTimeIdx.value]['介绍']
  140. } else {
  141. const targetCorp = corpList.value.find((item) => {
  142. return item.id.toString() === activeCorpId.value.toString()
  143. })
  144. return targetCorp?.description || targetCorp?.story || ''
  145. }
  146. })
  147. const articleBannerUrl = ref('')
  148. watch(activeCorpId, async (newV) => {
  149. if (newV === '') {
  150. articleBannerUrl.value = ''
  151. } else if (newV === '-1') {
  152. articleBannerUrl.value = ''
  153. } else {
  154. const corpDetail = await api.getHistoryDetail(newV)
  155. articleBannerUrl.value = corpDetail?.file[0]?.filePath ? process.env.VUE_APP_API_ORIGIN + corpDetail?.file[0]?.filePath : ''
  156. }
  157. })
  158. watch(activeTimeIdx, () => {
  159. articleBannerUrl.value = ''
  160. })
  161. function onClickTimeItem(idx) {
  162. activeTimeIdx.value = idx
  163. activeCorpId.value = ''
  164. }
  165. function onIframeMessage(e) {
  166. console.log('iframe message: ', e.data)
  167. if (e.data.startsWith && e.data.startsWith('node-selected: ')) {
  168. activeCorpId.value = e.data.split('node-selected: ')[1]
  169. }
  170. }
  171. onMounted(() => {
  172. window.addEventListener('message', onIframeMessage)
  173. })
  174. onBeforeUnmount(() => {
  175. window.removeEventListener('message', onIframeMessage)
  176. })
  177. return {
  178. timeNameList,
  179. activeTimeIdx,
  180. onClickLeftArrow,
  181. onClickRightArrow,
  182. activeCorpId,
  183. articleTitle,
  184. articleDesc,
  185. articleBannerUrl,
  186. onClickTimeItem,
  187. }
  188. }
  189. }
  190. </script>
  191. <style lang="less" scoped>
  192. .history {
  193. >iframe {
  194. position: absolute;
  195. left: 0;
  196. top: 0;
  197. width: 100%;
  198. height: 100%;
  199. }
  200. >h1 {
  201. position: absolute;
  202. top: 51px;
  203. left: 81px;
  204. max-width: 50%;
  205. overflow: hidden;
  206. white-space: pre;
  207. text-overflow: ellipsis;
  208. font-size: 48px;
  209. font-family: Source Han Sans CN-Heavy, Source Han Sans CN;
  210. font-weight: 800;
  211. color: #FFFFFF;
  212. padding-top: 20px;
  213. padding-bottom: 20px;
  214. border-top: 1px solid rgba(217, 217, 217, 0.2);
  215. border-bottom: 1px solid rgba(217, 217, 217, 0.2);
  216. }
  217. >menu {
  218. position: absolute;
  219. top: 214px;
  220. left: 0;
  221. >img.circle-no-light {
  222. position: absolute;
  223. top: -20%;
  224. left: -60px;
  225. height: 130%;
  226. pointer-events: none;
  227. }
  228. >img.circle {
  229. position: absolute;
  230. top: -20%;
  231. left: -60px;
  232. height: 130%;
  233. pointer-events: none;
  234. z-index: 1;
  235. }
  236. >ul {
  237. >li {
  238. display: flex;
  239. justify-content: flex-end;
  240. align-items: center;
  241. height: 50px;
  242. background: linear-gradient(270deg, #3A454F 0%, rgba(22,28,33,0) 100%);
  243. // border-radius: 3px;
  244. border: 1px solid;
  245. border-left: none;
  246. border-image: linear-gradient(270deg, rgba(78, 96, 112, 1), rgba(78, 96, 112, 0)) 1 1;
  247. margin-bottom: 15px;
  248. font-size: 20px;
  249. font-family: Source Han Sans CN-Light, Source Han Sans CN;
  250. font-weight: 400;
  251. color: rgba(255, 255, 255, 0.5);
  252. // backdrop-filter: blur(10px); // 会导致产生层叠上下文!!!使得圆点无法在弧线上层!!!
  253. padding-right: 14px;
  254. cursor: pointer;
  255. position: relative;
  256. >.point {
  257. position: absolute;
  258. top: 50%;
  259. transform: translateY(-50%);
  260. width: 8px;
  261. height: 8px;
  262. border-radius: 4px;
  263. background: #D1DCE5;
  264. box-shadow: 0px 0px 12px 0px #6D9DC6, 0px 0px 8px 0px #6D9DC6;
  265. z-index: 2;
  266. }
  267. &:hover {
  268. background: linear-gradient(270deg, #B0A179 0%, rgba(255,209,91,0) 100%);
  269. border-image: linear-gradient(270deg, rgba(176, 161, 121, 1), rgba(176, 161, 121, 0)) 1 1;
  270. }
  271. &.active {
  272. font-size: 22px;
  273. font-family: Source Han Sans CN-Bold, Source Han Sans CN;
  274. font-weight: bold;
  275. color: #FFFFFF;
  276. text-shadow: 0px 0px 10px #8B7C54;
  277. >.point {
  278. background: #FFFFFF;
  279. box-shadow: 0px 0px 12px 0px #FFD15B, 0px 0px 8px 0px #FFD15B, 0px 0px 10px 0px #FFD15B, 0px 0px 5px 0px #FFD15B;
  280. }
  281. }
  282. &:nth-of-type(1) {
  283. width: calc(170px + 25px * 1);
  284. >.point {
  285. margin-right: calc(48px + 80px);
  286. }
  287. }
  288. &:nth-of-type(2) {
  289. width: calc(170px + 25px * 2);
  290. >.point {
  291. margin-right: calc(43px + 80px);
  292. }
  293. }
  294. &:nth-of-type(3) {
  295. width: calc(170px + 25px * 3);
  296. >.point {
  297. margin-right: calc(50px + 80px);
  298. }
  299. }
  300. &:nth-of-type(4) {
  301. width: calc(170px + 25px * 4);
  302. >.point {
  303. margin-right: calc(65px + 80px);
  304. }
  305. }
  306. &:nth-of-type(5) {
  307. width: calc(170px + 25px * 3);
  308. >.point {
  309. margin-right: calc(40px + 80px);
  310. }
  311. }
  312. &:nth-of-type(6) {
  313. width: calc(170px + 25px * 2);
  314. >.point {
  315. margin-right: calc(23px + 80px);
  316. }
  317. }
  318. &:nth-of-type(7) {
  319. width: calc(170px + 25px * 1);
  320. >.point {
  321. margin-right: calc(15px + 80px);
  322. }
  323. }
  324. &:nth-of-type(8) {
  325. width: 170px;
  326. >.point {
  327. margin-right: calc(17px + 80px);
  328. }
  329. }
  330. }
  331. }
  332. }
  333. >article {
  334. position: absolute;
  335. top: 134px;
  336. right: 114px;
  337. width: 458px;
  338. @media only screen and (max-width: 1700px) {
  339. right: 20px;
  340. }
  341. >h2 {
  342. display: flex;
  343. align-items: center;
  344. font-size: 24px;
  345. font-family: Source Han Sans CN-Bold, Source Han Sans CN;
  346. font-weight: bold;
  347. color: #FFFFFF;
  348. text-shadow: 0px 0px 9px #B0A179;
  349. margin-bottom: 28px;
  350. &::before {
  351. content: '';
  352. display: inline-block;
  353. margin-right: 10px;
  354. width: 3px;
  355. height: 3px;
  356. background: #B0A179;
  357. }
  358. }
  359. >img.splitter {
  360. width: 100%;
  361. margin-bottom: 25px;
  362. }
  363. >.txt-wrapper {
  364. margin-bottom: 20px;
  365. position: relative;
  366. padding-left: 40px;
  367. padding-right: 40px;
  368. > img.banner {
  369. width: 100%;
  370. max-height: 300px;
  371. object-fit: contain;
  372. margin-top: 10px;
  373. }
  374. >.txt {
  375. max-height: calc(100vh - 400px);
  376. overflow: auto;
  377. font-size: 20px;
  378. font-family: Source Han Sans CN-Light, Source Han Sans CN;
  379. font-weight: 400;
  380. color: rgba(255, 255, 255, 0.8);
  381. line-height: 1.5;
  382. padding-right: 10px;
  383. margin-right: -10px;
  384. white-space: pre-wrap;
  385. text-indent: 2em;
  386. &::-webkit-scrollbar { background: transparent; width: 4px; } /*宽度是对垂直滚动条而言,高度是对水平滚动条而言*/
  387. &::-webkit-scrollbar-thumb {
  388. background: rgba(220, 231, 240, 0.2);
  389. border-radius: 2px;
  390. }
  391. }
  392. >button {
  393. width: 36px;
  394. height: 36px;
  395. position: absolute;
  396. top: 50%;
  397. transform: translateY(-50%);
  398. background-size: 90%;
  399. background-repeat: no-repeat;
  400. &.left {
  401. left: 0;
  402. background-image: url(@/assets/images/arrow_left.png);
  403. background-position: left center;
  404. }
  405. &.right {
  406. right: 0;
  407. background-image: url(@/assets/images/arrow_right.png);
  408. background-position: right center;
  409. }
  410. }
  411. }
  412. }
  413. }
  414. </style>