chenlei 2 veckor sedan
incheckning
f153be641e
100 ändrade filer med 1674 tillägg och 0 borttagningar
  1. 22 0
      .gitignore
  2. 2 0
      .npmrc
  3. 8 0
      .prettierignore
  4. 3 0
      .vscode/extensions.json
  5. 10 0
      .vscode/settings.json
  6. 2 0
      README.md
  7. 16 0
      package.json
  8. 18 0
      packages/base/package.json
  9. 60 0
      packages/base/src/constants.js
  10. 1 0
      packages/base/src/index.js
  11. 30 0
      packages/mobile/.gitignore
  12. 13 0
      packages/mobile/index.html
  13. 8 0
      packages/mobile/jsconfig.json
  14. 33 0
      packages/mobile/package.json
  15. 22 0
      packages/mobile/postcss.config.js
  16. BIN
      packages/mobile/public/favicon.ico
  17. 16 0
      packages/mobile/src/App.vue
  18. BIN
      packages/mobile/src/assets/fonts/SOURCEHANSERIFCN-BOLD.OTF
  19. BIN
      packages/mobile/src/assets/fonts/SOURCEHANSERIFCN-REGULAR.OTF
  20. 112 0
      packages/mobile/src/assets/main.css
  21. 7 0
      packages/mobile/src/assets/utils.scss
  22. 20 0
      packages/mobile/src/main.js
  23. 14 0
      packages/mobile/src/router/index.js
  24. 25 0
      packages/mobile/src/stores/base.js
  25. 12 0
      packages/mobile/src/stores/counter.js
  26. 107 0
      packages/mobile/src/views/Home/constants.js
  27. BIN
      packages/mobile/src/views/Home/images/bg-min.jpg
  28. BIN
      packages/mobile/src/views/Home/images/c1-min.png
  29. BIN
      packages/mobile/src/views/Home/images/c2-min.png
  30. BIN
      packages/mobile/src/views/Home/images/c3-min.png
  31. BIN
      packages/mobile/src/views/Home/images/c4-min.png
  32. BIN
      packages/mobile/src/views/Home/images/c5-min.png
  33. BIN
      packages/mobile/src/views/Home/images/c6-min.png
  34. BIN
      packages/mobile/src/views/Home/images/line-active.png
  35. BIN
      packages/mobile/src/views/Home/images/m1-min.png
  36. BIN
      packages/mobile/src/views/Home/images/m2-min.png
  37. BIN
      packages/mobile/src/views/Home/images/m3-min.png
  38. BIN
      packages/mobile/src/views/Home/images/m4-min.png
  39. BIN
      packages/mobile/src/views/Home/images/map-min.png
  40. BIN
      packages/mobile/src/views/Home/images/point-bg-min.png
  41. BIN
      packages/mobile/src/views/Home/images/step.png
  42. BIN
      packages/mobile/src/views/Home/images/t1-min.png
  43. BIN
      packages/mobile/src/views/Home/images/t2-min.png
  44. BIN
      packages/mobile/src/views/Home/images/t3-min.png
  45. BIN
      packages/mobile/src/views/Home/images/t4-min.png
  46. BIN
      packages/mobile/src/views/Home/images/t5-min.png
  47. BIN
      packages/mobile/src/views/Home/images/t6-min.png
  48. BIN
      packages/mobile/src/views/Home/images/tab-bg.png
  49. BIN
      packages/mobile/src/views/Home/images/title-min.png
  50. 210 0
      packages/mobile/src/views/Home/index.scss
  51. 95 0
      packages/mobile/src/views/Home/index.vue
  52. 29 0
      packages/mobile/vite.config.js
  53. 30 0
      packages/pc/.gitignore
  54. 13 0
      packages/pc/index.html
  55. 8 0
      packages/pc/jsconfig.json
  56. 34 0
      packages/pc/package.json
  57. 23 0
      packages/pc/postcss.config.js
  58. BIN
      packages/pc/public/favicon.ico
  59. 26 0
      packages/pc/src/App.vue
  60. 7 0
      packages/pc/src/assets/elements.scss
  61. BIN
      packages/pc/src/assets/fonts/FZJinLing.TTF
  62. BIN
      packages/pc/src/assets/fonts/SOURCEHANSERIFCN-BOLD.OTF
  63. BIN
      packages/pc/src/assets/fonts/SourceHanSerifCN-Light.otf
  64. BIN
      packages/pc/src/assets/fonts/SourceHanSerifCN-Medium.otf
  65. BIN
      packages/pc/src/assets/fonts/SourceHanSerifCN-Regular.otf
  66. BIN
      packages/pc/src/assets/fonts/SourceHanSerifCN-SemiBold.otf
  67. BIN
      packages/pc/src/assets/images/head-bd.png
  68. BIN
      packages/pc/src/assets/images/head-min.jpg
  69. BIN
      packages/pc/src/assets/images/home.png
  70. BIN
      packages/pc/src/assets/images/tab-hover.jpg
  71. 115 0
      packages/pc/src/assets/main.css
  72. 7 0
      packages/pc/src/assets/utils.scss
  73. 125 0
      packages/pc/src/components/Header.vue
  74. 15 0
      packages/pc/src/main.js
  75. 32 0
      packages/pc/src/router/index.js
  76. 12 0
      packages/pc/src/stores/counter.js
  77. 22 0
      packages/pc/src/utils/constants.js
  78. BIN
      packages/pc/src/views/Cover/images/1-1-min.png
  79. BIN
      packages/pc/src/views/Cover/images/1-min.png
  80. BIN
      packages/pc/src/views/Cover/images/2-1-min.png
  81. BIN
      packages/pc/src/views/Cover/images/2-min.png
  82. BIN
      packages/pc/src/views/Cover/images/3-1-min.png
  83. BIN
      packages/pc/src/views/Cover/images/3-min.png
  84. BIN
      packages/pc/src/views/Cover/images/4-1-min.png
  85. BIN
      packages/pc/src/views/Cover/images/4-min.png
  86. BIN
      packages/pc/src/views/Cover/images/5-1-min.png
  87. BIN
      packages/pc/src/views/Cover/images/5-min.png
  88. BIN
      packages/pc/src/views/Cover/images/bg-min.jpg
  89. BIN
      packages/pc/src/views/Cover/images/card-min.jpg
  90. BIN
      packages/pc/src/views/Cover/images/label-min.png
  91. BIN
      packages/pc/src/views/Cover/images/seal-min.png
  92. BIN
      packages/pc/src/views/Cover/images/title-min.png
  93. 164 0
      packages/pc/src/views/Cover/index.scss
  94. 31 0
      packages/pc/src/views/Cover/index.vue
  95. 115 0
      packages/pc/src/views/Home/constants.js
  96. BIN
      packages/pc/src/views/Home/images/bd-min.png
  97. BIN
      packages/pc/src/views/Home/images/bg-min.jpg
  98. BIN
      packages/pc/src/views/Home/images/bg2.jpg
  99. BIN
      packages/pc/src/views/Home/images/card-bg-min.png
  100. 0 0
      packages/pc/src/views/Home/images/footer-min.jpg

+ 22 - 0
.gitignore

@@ -0,0 +1,22 @@
+# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
+
+# dependencies
+node_modules
+npm-debug.log*
+yarn-error.log
+yarn.lock
+package-lock.json
+
+# production
+/es
+docs-dist
+
+# misc
+.DS_Store
+
+# ide
+/.idea
+
+dist
+dist-node
+build

+ 2 - 0
.npmrc

@@ -0,0 +1,2 @@
+registry=https://registry.npmmirror.com/
+@dage:registry=http://192.168.20.245:4873/

+ 8 - 0
.prettierignore

@@ -0,0 +1,8 @@
+build/
+public/
+**/*.png
+**/*.svg
+**/*.jpg
+.DS_Store
+.history
+package.json

+ 3 - 0
.vscode/extensions.json

@@ -0,0 +1,3 @@
+{
+  "recommendations": ["Vue.volar"]
+}

+ 10 - 0
.vscode/settings.json

@@ -0,0 +1,10 @@
+{
+  "explorer.fileNesting.enabled": true,
+  "explorer.fileNesting.patterns": {
+    "tsconfig.json": "tsconfig.*.json, env.d.ts",
+    "vite.config.*": "jsconfig*, vitest.config.*, cypress.config.*, playwright.config.*",
+    "package.json": "package-lock.json, pnpm*, .yarnrc*, yarn*, .eslint*, eslint*, .prettier*, prettier*, .editorconfig"
+  },
+  "editor.formatOnSave": true,
+  "editor.defaultFormatter": "esbenp.prettier-vscode"
+}

+ 2 - 0
README.md

@@ -0,0 +1,2 @@
+WHS2504145-1 扬州中国大运河博物馆-研究部 2025 年线上图文展项目
+镇水安澜

+ 16 - 0
package.json

@@ -0,0 +1,16 @@
+{
+  "name": "crand-canal-museum",
+  "version": "1.0.0",
+  "description": "",
+  "main": "index.js",
+  "scripts": {
+    "test": "echo \"Error: no test specified\" && exit 1"
+  },
+  "keywords": [],
+  "author": "",
+  "license": "ISC",
+  "dependencies": {
+    "pinia": "^2.2.6",
+    "tslib": "^2.8.0"
+  }
+}

+ 18 - 0
packages/base/package.json

@@ -0,0 +1,18 @@
+{
+  "name": "@canal/base",
+  "version": "1.0.0",
+  "description": "",
+  "sideEffects": false,
+  "module": "src/index.js",
+  "main": "src/index.js",
+  "files": [
+    "src"
+  ],
+  "keywords": [],
+  "author": "",
+  "license": "ISC",
+  "dependencies": {
+    "apng-js": "^1.1.5",
+    "lodash": "^4.17.21"
+  }
+}

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 60 - 0
packages/base/src/constants.js


+ 1 - 0
packages/base/src/index.js

@@ -0,0 +1 @@
+export * from "./constants";

+ 30 - 0
packages/mobile/.gitignore

@@ -0,0 +1,30 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+lerna-debug.log*
+
+node_modules
+.DS_Store
+dist
+dist-ssr
+coverage
+*.local
+
+/cypress/videos/
+/cypress/screenshots/
+
+# Editor directories and files
+.vscode/*
+!.vscode/extensions.json
+.idea
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?
+
+*.tsbuildinfo

+ 13 - 0
packages/mobile/index.html

@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html lang="">
+  <head>
+    <meta charset="UTF-8" />
+    <link rel="icon" href="/favicon.ico" />
+    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+    <title>镇水安澜</title>
+  </head>
+  <body>
+    <div id="app"></div>
+    <script type="module" src="/src/main.js"></script>
+  </body>
+</html>

+ 8 - 0
packages/mobile/jsconfig.json

@@ -0,0 +1,8 @@
+{
+  "compilerOptions": {
+    "paths": {
+      "@/*": ["./src/*"]
+    }
+  },
+  "exclude": ["node_modules", "dist"]
+}

+ 33 - 0
packages/mobile/package.json

@@ -0,0 +1,33 @@
+{
+  "name": "@canal/mobile",
+  "version": "0.0.0",
+  "private": true,
+  "type": "module",
+  "scripts": {
+    "dev": "vite",
+    "build": "vite build",
+    "preview": "vite preview"
+  },
+  "dependencies": {
+    "@canal/base": "workspace:*",
+    "clipboard": "^2.0.11",
+    "pinia": "^2.2.6",
+    "swiper": "^11.2.10",
+    "vant": "^4.9.15",
+    "vue": "^3.5.13",
+    "vue-draggable-resizable": "^3.0.0",
+    "vue-qrcode": "^2.2.2",
+    "vue-router": "^4.4.5"
+  },
+  "devDependencies": {
+    "@vant/auto-import-resolver": "^1.2.1",
+    "@vitejs/plugin-vue": "^5.2.1",
+    "autoprefixer": "^10.4.20",
+    "postcss-px-to-viewport": "^1.1.1",
+    "sass": "^1.82.0",
+    "unplugin-auto-import": "^0.18.6",
+    "unplugin-vue-components": "^0.27.5",
+    "vite": "^6.0.1",
+    "vite-plugin-vue-devtools": "^7.6.5"
+  }
+}

+ 22 - 0
packages/mobile/postcss.config.js

@@ -0,0 +1,22 @@
+export default {
+  plugins: {
+    autoprefixer: {},
+    "postcss-px-to-viewport": {
+      unitToConvert: "px", // 需要转换的单位,默认为"px"
+      viewportWidth: 750, // 设计稿的视口宽度
+      unitPrecision: 5, // 单位转换后保留的精度
+      propList: ["*"], // 能转化为vw的属性列表
+      viewportUnit: "vw", // 希望使用的视口单位
+      fontViewportUnit: "vw", // 字体使用的视口单位
+      selectorBlackList: [], // 需要忽略的CSS选择器,不会转为视口单位,使用原有的px等单位。
+      minPixelValue: 1, // 设置最小的转换数值,如果为1的话,只有大于1的值会被转换
+      mediaQuery: true, // 媒体查询里的单位是否需要转换单位
+      replace: true, //  是否直接更换属性值,而不添加备用属性
+      exclude: /node_modules/i, // 忽略某些文件夹下的文件或特定文件,例如 'node_modules' 下的文件
+      include: undefined, // 如果设置了include,那将只有匹配到的文件才会被转换
+      landscape: false, // 是否添加根据 landscapeWidth 生成的媒体查询条件 @media (orientation: landscape)
+      landscapeUnit: "vw", // 横屏时使用的单位
+      landscapeWidth: 750, // 横屏时使用的视口宽度
+    },
+  },
+};

BIN
packages/mobile/public/favicon.ico


+ 16 - 0
packages/mobile/src/App.vue

@@ -0,0 +1,16 @@
+<script setup>
+import { RouterView } from "vue-router";
+</script>
+
+<template>
+  <RouterView />
+</template>
+
+<style>
+:root:root {
+  --van-primary-color: #981b23;
+  --van-base-font: "SourceHanSerifSC-Regular";
+  --design-width: 750;
+  --design-height: 1448;
+}
+</style>

BIN
packages/mobile/src/assets/fonts/SOURCEHANSERIFCN-BOLD.OTF


BIN
packages/mobile/src/assets/fonts/SOURCEHANSERIFCN-REGULAR.OTF


+ 112 - 0
packages/mobile/src/assets/main.css

@@ -0,0 +1,112 @@
+:root {
+  --z-index-normal: 1;
+  --z-index-top: 1000;
+  --z-index-popper: 2000;
+  --z-hot-popper: 3000;
+}
+
+body,
+ol,
+ul,
+h1,
+h2,
+h3,
+h4,
+h5,
+h6,
+p,
+th,
+td,
+dl,
+dd,
+form,
+fieldset,
+legend,
+input,
+textarea,
+select {
+  margin: 0;
+  padding: 0;
+}
+* {
+  box-sizing: border-box;
+  user-select: none;
+}
+body {
+  color: #333333;
+  text-align: justify;
+  font-family: "SourceHanSerifSC-Regular";
+  -webkit-tap-highlight-color: transparent;
+}
+a {
+  color: #fff;
+  cursor: pointer;
+  text-decoration: none;
+}
+em {
+  font-style: normal;
+}
+li {
+  list-style: none;
+}
+img {
+  border: 0;
+  vertical-align: middle;
+}
+table {
+  border-collapse: collapse;
+  border-spacing: 0;
+}
+p {
+  word-wrap: break-word;
+}
+iframe {
+  border: none;
+}
+
+@font-face {
+  font-family: "SourceHanSerifSC-Bold";
+  src: url("./fonts/SOURCEHANSERIFCN-BOLD.otf");
+}
+@font-face {
+  font-family: "SourceHanSerifSC-Regular";
+  src: url("./fonts/SOURCEHANSERIFCN-REGULAR.otf");
+}
+
+.limit-line {
+  display: -webkit-box;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  -webkit-line-clamp: 1;
+  -webkit-box-orient: vertical;
+  word-break: break-all;
+  word-wrap: break-word;
+}
+
+.line-2 {
+  -webkit-line-clamp: 2;
+}
+
+.line-3 {
+  -webkit-line-clamp: 3;
+}
+
+.hidden {
+  display: none !important;
+  visibility: hidden !important;
+}
+
+.darkGlass {
+  background-color: rgba(0, 0, 0, 0.5);
+}
+
+.message-outer {
+  position: absolute;
+  display: table;
+  height: 100%;
+  width: 100%;
+
+  * {
+    transition: all 0.3s;
+  }
+}

+ 7 - 0
packages/mobile/src/assets/utils.scss

@@ -0,0 +1,7 @@
+@function vh-calc($num) {
+  @return calc(100vh * ($num / var(--design-height)));
+}
+
+@function vw-calc($num) {
+  @return calc(100vw * ($num / var(--design-width)));
+}

+ 20 - 0
packages/mobile/src/main.js

@@ -0,0 +1,20 @@
+import "./assets/main.css";
+
+import { createApp } from "vue";
+import { createPinia } from "pinia";
+import { Lazyload } from "vant";
+
+import App from "./App.vue";
+import router from "./router";
+
+import { isDevelopment, checkDeviceAndRedirect } from "@canal/base";
+
+!isDevelopment && checkDeviceAndRedirect();
+
+const app = createApp(App);
+
+app.use(createPinia());
+app.use(router);
+app.use(Lazyload);
+
+app.mount("#app");

+ 14 - 0
packages/mobile/src/router/index.js

@@ -0,0 +1,14 @@
+import { createRouter, createWebHashHistory } from "vue-router";
+
+const router = createRouter({
+  history: createWebHashHistory(import.meta.env.BASE_URL),
+  routes: [
+    {
+      path: "/index",
+      name: "home",
+      component: () => import("../views/Home/index.vue"),
+    },
+  ],
+});
+
+export default router;

+ 25 - 0
packages/mobile/src/stores/base.js

@@ -0,0 +1,25 @@
+import { ref } from "vue";
+import { defineStore } from "pinia";
+import { parseUrlName } from "@canal/base";
+import Img1 from "../views/Home/images/bg-min.jpg";
+import Img2 from "../views/Home/images/title-min.png";
+import Img3 from "../views/Home/images/tab-bg.png";
+import Img4 from "../views/Home/images/step.png";
+import Img5 from "../views/Home/images/map-min.png";
+import Img7 from "../views/Home/images/line-active.png";
+
+export const RESOURCE_LIST = [Img1, Img2, Img3, Img4, Img5, Img7];
+
+export const useBaseStore = defineStore("base", () => {
+  const mediaUrlMap = ref(new Map());
+
+  RESOURCE_LIST.forEach((url) => {
+    mediaUrlMap.value.set(parseUrlName(url), url);
+  });
+
+  const setMediaUrlMap = (list) => {
+    mediaUrlMap.value = list;
+  };
+
+  return { mediaUrlMap, setMediaUrlMap };
+});

+ 12 - 0
packages/mobile/src/stores/counter.js

@@ -0,0 +1,12 @@
+import { ref, computed } from 'vue'
+import { defineStore } from 'pinia'
+
+export const useCounterStore = defineStore('counter', () => {
+  const count = ref(0)
+  const doubleCount = computed(() => count.value * 2)
+  function increment() {
+    count.value++
+  }
+
+  return { count, doubleCount, increment }
+})

+ 107 - 0
packages/mobile/src/views/Home/constants.js

@@ -0,0 +1,107 @@
+import M1Img from "./images/m1-min.png";
+import M2Img from "./images/m2-min.png";
+import M3Img from "./images/m3-min.png";
+import M4Img from "./images/m4-min.png";
+import C1Img from "./images/c1-min.png";
+import C2Img from "./images/c2-min.png";
+import C3Img from "./images/c3-min.png";
+import C4Img from "./images/c4-min.png";
+import C5Img from "./images/c5-min.png";
+import C6Img from "./images/c6-min.png";
+import T1Img from "./images/t1-min.png";
+import T2Img from "./images/t2-min.png";
+import T3Img from "./images/t3-min.png";
+import T4Img from "./images/t4-min.png";
+import T5Img from "./images/t5-min.png";
+import T6Img from "./images/t6-min.png";
+
+export const MIMAGES = [
+  {
+    default: M1Img,
+    href: "https://houseoss.4dkankan.com/project/guyuan/scene/index.html?m=SG-IDyBc0bleFF",
+  },
+  {
+    default: M2Img,
+    routeName: "antique",
+  },
+  {
+    default: M3Img,
+    routeName: "history",
+  },
+  {
+    type: "share",
+    default: M4Img,
+  },
+];
+
+export const POINTS = [
+  {
+    img: C1Img,
+    img2: T1Img,
+    content: `
+      <p>入境西吉县,</p>
+      <p>毛泽东夜访单南寺,</p>
+      <p>与马德海秉烛长谈,</p>
+      <p>宣讲民族政策及抗日主张,</p>
+      <p>军民鱼水情浓,</p>
+      <p>史称“单家集夜话”。</p>
+    `,
+  },
+  {
+    img: C2Img,
+    img2: T2Img,
+    content: `
+      <p>夜宿张易堡,</p>
+      <p>红军继续行军,</p>
+      <p>宿营张易堡,</p>
+      <p>严守纪律不扰民,</p>
+      <p>借门板铺地休息,</p>
+      <p>清晨归还并留铜元致谢,</p>
+      <p>百姓称“仁义之师”。</p>
+    `,
+  },
+  {
+    img: C3Img,
+    img2: T3Img,
+    content: `
+      <p>青石嘴突袭战,</p>
+      <p>红军突袭青石嘴,</p>
+      <p>半小时歼敌两个连,</p>
+      <p>缴获战马百余匹,</p>
+      <p>遂组建中央红军首支骑兵侦察连。</p>
+    `,
+  },
+  {
+    img: C4Img,
+    img2: T4Img,
+    content: `
+      <p>翻越六盘山后,</p>
+      <p>毛泽东登顶六盘山,</p>
+      <p>远望长城,</p>
+      <p>夜宿小岔沟土炕,</p>
+      <p>即兴吟成《长征谣》初稿,</p>
+      <p>“不到长城非好汉”由此诞生。</p>
+    `,
+  },
+  {
+    img: C5Img,
+    img2: T5Img,
+    content: `
+      <p>过境彭阳县,</p>
+      <p>午后抵乔家渠,</p>
+      <p>毛泽东宿乔生魁家,</p>
+      <p>土炕上铺地图部署陕北行军,</p>
+      <p>房东悄悄塞给战士两筐土豆,</p>
+      <p>军民泪别。</p>
+    `,
+  },
+  {
+    img: C6Img,
+    img2: T6Img,
+    content: `
+      <p>出境固原,</p>
+      <p>拂晓从彭阳长城塬出发,</p>
+      <p>踏上奔赴陕北抗日的新征程。</p>
+    `,
+  },
+];

BIN
packages/mobile/src/views/Home/images/bg-min.jpg


BIN
packages/mobile/src/views/Home/images/c1-min.png


BIN
packages/mobile/src/views/Home/images/c2-min.png


BIN
packages/mobile/src/views/Home/images/c3-min.png


BIN
packages/mobile/src/views/Home/images/c4-min.png


BIN
packages/mobile/src/views/Home/images/c5-min.png


BIN
packages/mobile/src/views/Home/images/c6-min.png


BIN
packages/mobile/src/views/Home/images/line-active.png


BIN
packages/mobile/src/views/Home/images/m1-min.png


BIN
packages/mobile/src/views/Home/images/m2-min.png


BIN
packages/mobile/src/views/Home/images/m3-min.png


BIN
packages/mobile/src/views/Home/images/m4-min.png


BIN
packages/mobile/src/views/Home/images/map-min.png


BIN
packages/mobile/src/views/Home/images/point-bg-min.png


BIN
packages/mobile/src/views/Home/images/step.png


BIN
packages/mobile/src/views/Home/images/t1-min.png


BIN
packages/mobile/src/views/Home/images/t2-min.png


BIN
packages/mobile/src/views/Home/images/t3-min.png


BIN
packages/mobile/src/views/Home/images/t4-min.png


BIN
packages/mobile/src/views/Home/images/t5-min.png


BIN
packages/mobile/src/views/Home/images/t6-min.png


BIN
packages/mobile/src/views/Home/images/tab-bg.png


BIN
packages/mobile/src/views/Home/images/title-min.png


+ 210 - 0
packages/mobile/src/views/Home/index.scss

@@ -0,0 +1,210 @@
+@use "@/assets/utils.scss";
+
+.home {
+  position: absolute;
+  inset: 0;
+  overflow: hidden;
+  background: url("./images/bg-min.jpg") no-repeat center / cover;
+
+  &::after {
+    content: "";
+    position: absolute;
+    top: utils.vh-calc(42);
+    left: utils.vh-calc(8);
+    width: utils.vh-calc(158);
+    height: utils.vh-calc(112);
+    background: url("@/assets/images/logo-min.png") no-repeat center / contain;
+    z-index: 9;
+  }
+  &::before {
+    content: "";
+    position: absolute;
+    top: utils.vh-calc(0);
+    right: utils.vh-calc(26);
+    width: utils.vh-calc(187);
+    height: utils.vh-calc(510);
+    background: url("./images/title-min.png") no-repeat center / contain;
+    z-index: 9;
+  }
+  &-menu {
+    position: fixed;
+    left: utils.vh-calc(28);
+    bottom: utils.vh-calc(256);
+    display: flex;
+    flex-direction: column;
+    justify-content: space-between;
+    padding: utils.vh-calc(10) 0;
+    width: utils.vh-calc(104);
+    height: utils.vh-calc(478);
+    border-radius: 4px;
+    background: rgba($color: #000000, $alpha: 0.2);
+
+    li {
+      img {
+        width: 100%;
+      }
+    }
+  }
+  &-tab {
+    position: fixed;
+    right: utils.vw-calc(30);
+    bottom: utils.vh-calc(35);
+    width: utils.vh-calc(429);
+    height: utils.vh-calc(248);
+    background: url("./images/tab-bg.png") no-repeat center / contain;
+    z-index: 5;
+
+    > div {
+      position: absolute;
+      cursor: pointer;
+    }
+    .story {
+      top: utils.vh-calc(34);
+      left: utils.vh-calc(100);
+      width: utils.vh-calc(200);
+      height: utils.vh-calc(85);
+    }
+    .biographies {
+      left: utils.vh-calc(100);
+      bottom: utils.vh-calc(35);
+      width: utils.vh-calc(220);
+      height: utils.vh-calc(80);
+    }
+  }
+  .vdr {
+    border: 0;
+  }
+  &__map {
+    width: 100%;
+    height: 100%;
+  }
+  &-point {
+    position: absolute;
+    width: utils.vw-calc(103);
+    height: utils.vw-calc(252);
+
+    &.point0 {
+      top: utils.vw-calc(780);
+      left: utils.vw-calc(460);
+    }
+    &.point1 {
+      top: utils.vw-calc(630);
+      left: utils.vw-calc(620);
+    }
+    &.point2 {
+      top: utils.vw-calc(560);
+      left: utils.vw-calc(340);
+    }
+    &.point3 {
+      top: utils.vw-calc(320);
+      left: utils.vw-calc(250);
+    }
+    &.point4 {
+      right: utils.vw-calc(110);
+      bottom: utils.vw-calc(340);
+    }
+    &.point5 {
+      right: utils.vw-calc(390);
+      bottom: utils.vw-calc(330);
+    }
+    img {
+      position: absolute;
+      left: 50%;
+      bottom: utils.vw-calc(85);
+      width: utils.vw-calc(103);
+      transform: translateX(-50%);
+    }
+    &::after {
+      content: "";
+      position: absolute;
+      left: 50%;
+      bottom: utils.vw-calc(0);
+      width: utils.vw-calc(100);
+      height: utils.vw-calc(100);
+      transform: translateX(-50%);
+      background: url("./images/step.png") no-repeat center / contain;
+    }
+    &.active {
+      z-index: 9;
+
+      img {
+        display: none;
+      }
+      &::after {
+        display: none;
+      }
+      &::before {
+        content: "";
+        position: absolute;
+        left: 50%;
+        bottom: utils.vw-calc(-70);
+        width: utils.vw-calc(360);
+        height: utils.vw-calc(600);
+        transform: translateX(-50%);
+        background: url("./images/line-active.png") no-repeat center / contain;
+      }
+    }
+  }
+}
+
+.point-panel {
+  display: flex;
+  align-items: center;
+  gap: 30px;
+  padding: 25px 70px 80px;
+  background: rgba(0, 0, 0, 0.8);
+  backdrop-filter: blur(20px);
+  box-shadow: 0 -8px 16px rgba(0, 0, 0, 0.6);
+
+  &-img {
+    position: relative;
+    width: utils.vw-calc(173);
+    height: utils.vw-calc(281);
+    background: url("./images/point-bg-min.png") no-repeat center / contain;
+
+    img {
+      position: relative;
+    }
+    .c0 {
+      top: utils.vw-calc(20);
+      left: utils.vw-calc(22);
+      width: utils.vw-calc(139);
+      height: utils.vw-calc(227);
+    }
+    .c1 {
+      top: utils.vw-calc(20);
+      left: utils.vw-calc(22);
+      width: utils.vw-calc(114);
+      height: utils.vw-calc(229);
+    }
+    .c2 {
+      top: utils.vw-calc(20);
+      left: utils.vw-calc(22);
+      width: utils.vw-calc(139);
+      height: utils.vw-calc(227);
+    }
+    .c3 {
+      top: utils.vw-calc(20);
+      left: utils.vw-calc(22);
+      width: utils.vw-calc(108);
+      height: utils.vw-calc(222);
+    }
+    .c4 {
+      top: utils.vw-calc(20);
+      left: utils.vw-calc(22);
+      width: utils.vw-calc(133);
+      height: utils.vw-calc(227);
+    }
+    .c5 {
+      top: utils.vw-calc(20);
+      left: utils.vw-calc(22);
+      width: utils.vw-calc(104);
+      height: utils.vw-calc(231);
+    }
+  }
+  &-content {
+    color: white;
+    font-size: 24px;
+    line-height: 56px;
+  }
+}

+ 95 - 0
packages/mobile/src/views/Home/index.vue

@@ -0,0 +1,95 @@
+<template>
+  <div class="home">
+    <VueDraggableResizable
+      :x="mapX"
+      :y="mapY"
+      :w="mapWidth"
+      :h="mapHeight"
+      :draggable="true"
+      :resizable="true"
+      :handles="[]"
+    >
+      <img class="home__map" src="./images/map-min.png" />
+
+      <div
+        v-for="(item, index) in POINTS"
+        :key="index"
+        class="home-point"
+        :class="[{ active: pointIndex === index }, `point${index}`]"
+        @click="handlePoint(index)"
+      >
+        <img :src="item.img2" />
+      </div>
+    </VueDraggableResizable>
+
+    <ul class="home-menu">
+      <li v-for="(item, index) in MIMAGES" :key="index">
+        <img :src="item.default" @click="handleMenu(index)" />
+      </li>
+    </ul>
+
+    <div class="home-tab">
+      <div class="story" />
+      <div class="biographies" @click="$router.push({ name: 'story' })" />
+    </div>
+  </div>
+
+  <SharePopup v-model:visible="shareVisible" />
+  <VanPopup
+    v-model:show="pointDetailVisible"
+    :overlay="true"
+    position="bottom"
+    class="point-panel"
+    :overlay-style="{
+      background: 'transparent',
+    }"
+    @closed="pointIndex = -1"
+  >
+    <template v-if="activePoint">
+      <div class="point-panel-img">
+        <img :class="`c${pointIndex}`" :src="activePoint.img" />
+      </div>
+      <div class="point-panel-content" v-html="activePoint.content" />
+    </template>
+  </VanPopup>
+</template>
+
+<script setup>
+import { ref, computed } from "vue";
+import VueDraggableResizable from "vue-draggable-resizable";
+import "vue-draggable-resizable/style.css";
+import { POINTS, MIMAGES } from "./constants";
+import SharePopup from "@/components/SharePopup.vue";
+import { useRouter } from "vue-router";
+
+const mapWidth = window.innerWidth * (1600 / 750);
+const mapHeight = window.innerWidth * (1215 / 750);
+const mapX = window.innerWidth * (30 / 750);
+const mapY = window.innerWidth * (80 / 750);
+
+const router = useRouter();
+const pointIndex = ref(-1);
+const shareVisible = ref(false);
+const pointDetailVisible = ref(false);
+const activePoint = computed(() => POINTS[pointIndex.value]);
+
+const handlePoint = (index) => {
+  pointIndex.value = index;
+  pointDetailVisible.value = true;
+};
+
+const handleMenu = (index) => {
+  const item = MIMAGES[index];
+  if (item.routeName) {
+    router.push({ name: item.routeName, query: item.query });
+  } else if (item.href) {
+    window.location.href = item.href;
+  } else if (item.type === "share") {
+    shareVisible.value = true;
+  }
+};
+</script>
+
+<style lang="scss" scoped>
+@use "./index.scss";
+</style>

+ 29 - 0
packages/mobile/vite.config.js

@@ -0,0 +1,29 @@
+import { fileURLToPath, URL } from "node:url";
+
+import { defineConfig } from "vite";
+import vue from "@vitejs/plugin-vue";
+import AutoImport from "unplugin-auto-import/vite";
+import Components from "unplugin-vue-components/vite";
+import { VantResolver } from "@vant/auto-import-resolver";
+
+// https://vite.dev/config/
+export default defineConfig({
+  base: "./",
+  server: {
+    host: "0.0.0.0",
+  },
+  plugins: [
+    vue(),
+    AutoImport({
+      resolvers: [VantResolver()],
+    }),
+    Components({
+      resolvers: [VantResolver()],
+    }),
+  ],
+  resolve: {
+    alias: {
+      "@": fileURLToPath(new URL("./src", import.meta.url)),
+    },
+  },
+});

+ 30 - 0
packages/pc/.gitignore

@@ -0,0 +1,30 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+lerna-debug.log*
+
+node_modules
+.DS_Store
+dist
+dist-ssr
+coverage
+*.local
+
+/cypress/videos/
+/cypress/screenshots/
+
+# Editor directories and files
+.vscode/*
+!.vscode/extensions.json
+.idea
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?
+
+*.tsbuildinfo

+ 13 - 0
packages/pc/index.html

@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html lang="">
+  <head>
+    <meta charset="UTF-8" />
+    <link rel="icon" href="/favicon.ico" />
+    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+    <title>镇水安澜</title>
+  </head>
+  <body>
+    <div id="app"></div>
+    <script type="module" src="/src/main.js"></script>
+  </body>
+</html>

+ 8 - 0
packages/pc/jsconfig.json

@@ -0,0 +1,8 @@
+{
+  "compilerOptions": {
+    "paths": {
+      "@/*": ["./src/*"]
+    }
+  },
+  "exclude": ["node_modules", "dist"]
+}

+ 34 - 0
packages/pc/package.json

@@ -0,0 +1,34 @@
+{
+  "name": "@canal/pc",
+  "version": "0.0.0",
+  "private": true,
+  "type": "module",
+  "scripts": {
+    "dev": "vite",
+    "build": "vite build",
+    "preview": "vite preview"
+  },
+  "dependencies": {
+    "@canal/base": "workspace:*",
+    "@element-plus/icons-vue": "^2.3.1",
+    "@vueuse/core": "^12.0.0",
+    "animate.css": "^4.1.1",
+    "autoprefixer": "^10.4.20",
+    "clipboard": "^2.0.11",
+    "element-plus": "^2.9.0",
+    "pinia": "^2.2.6",
+    "postcss-px-to-viewport": "^1.1.1",
+    "smooth-signature": "^1.1.0",
+    "swiper": "^11.1.15",
+    "vue": "^3.5.13",
+    "vue-qrcode": "^2.2.2",
+    "vue-router": "^4.4.5"
+  },
+  "devDependencies": {
+    "@vitejs/plugin-vue": "^5.2.1",
+    "sass": "^1.82.0",
+    "unplugin-auto-import": "^0.18.6",
+    "unplugin-vue-components": "^0.27.5",
+    "vite": "^6.0.1"
+  }
+}

+ 23 - 0
packages/pc/postcss.config.js

@@ -0,0 +1,23 @@
+export default {
+  plugins: {
+    autoprefixer: {},
+    "postcss-px-to-viewport": {
+      unitToConvert: "px", // 需要转换的单位,默认为"px"
+      viewportWidth: 1920, // 设计稿的视口宽度
+      unitPrecision: 5, // 单位转换后保留的精度
+      propList: ["*"], // 能转化为vw的属性列表
+      viewportUnit: "vw", // 希望使用的视口单位
+      fontViewportUnit: "vw", // 字体使用的视口单位
+      selectorBlackList: [], // 需要忽略的CSS选择器,不会转为视口单位,使用原有的px等单位。
+      minPixelValue: 1, // 设置最小的转换数值,如果为1的话,只有大于1的值会被转换
+      mediaQuery: true, // 媒体查询里的单位是否需要转换单位
+      replace: true, //  是否直接更换属性值,而不添加备用属性
+      exclude: undefined, // 忽略某些文件夹下的文件或特定文件,例如 'node_modules' 下的文件
+      include: undefined, // 如果设置了include,那将只有匹配到的文件才会被转换
+      landscape: false, // 是否添加根据 landscapeWidth 生成的媒体查询条件 @media (orientation: landscape)
+      landscapeUnit: "vw", // 横屏时使用的单位
+      landscapeWidth: 1920, // 横屏时使用的视口宽度
+      exclude: [/node_modules/],
+    },
+  },
+};

BIN
packages/pc/public/favicon.ico


+ 26 - 0
packages/pc/src/App.vue

@@ -0,0 +1,26 @@
+<script setup>
+import { RouterView } from "vue-router";
+import Header from "@/components/Header.vue";
+</script>
+
+<template>
+  <Header v-if="!$route.meta.hideTab" />
+
+  <RouterView />
+</template>
+
+<style lang="scss">
+:root {
+  --design-width: 1920;
+  --design-height: 1080;
+}
+
+.el-image {
+  width: 100%;
+  height: 100%;
+}
+
+#app {
+  font-size: 18px;
+}
+</style>

+ 7 - 0
packages/pc/src/assets/elements.scss

@@ -0,0 +1,7 @@
+@forward "element-plus/theme-chalk/src/common/var.scss" with (
+  $colors: (
+    "primary": (
+      "base": #610415,
+    ),
+  )
+);

BIN
packages/pc/src/assets/fonts/FZJinLing.TTF


BIN
packages/pc/src/assets/fonts/SOURCEHANSERIFCN-BOLD.OTF


BIN
packages/pc/src/assets/fonts/SourceHanSerifCN-Light.otf


BIN
packages/pc/src/assets/fonts/SourceHanSerifCN-Medium.otf


BIN
packages/pc/src/assets/fonts/SourceHanSerifCN-Regular.otf


BIN
packages/pc/src/assets/fonts/SourceHanSerifCN-SemiBold.otf


BIN
packages/pc/src/assets/images/head-bd.png


BIN
packages/pc/src/assets/images/head-min.jpg


BIN
packages/pc/src/assets/images/home.png


BIN
packages/pc/src/assets/images/tab-hover.jpg


+ 115 - 0
packages/pc/src/assets/main.css

@@ -0,0 +1,115 @@
+:root {
+  --z-index-normal: 1;
+  --z-index-top: 1000;
+  --z-index-popper: 2000;
+  --z-hot-popper: 3000;
+}
+
+body,
+ol,
+ul,
+h1,
+h2,
+h3,
+h4,
+h5,
+h6,
+p,
+th,
+td,
+dl,
+dd,
+form,
+fieldset,
+legend,
+input,
+textarea,
+select {
+  margin: 0;
+  padding: 0;
+}
+* {
+  box-sizing: border-box;
+}
+body {
+  color: #383838;
+  text-align: justify;
+  font-family: "SourceHanSerifSC-Regular";
+  -webkit-tap-highlight-color: transparent;
+}
+a {
+  color: #fff;
+  cursor: pointer;
+  text-decoration: none;
+}
+em {
+  font-style: normal;
+}
+li {
+  list-style: none;
+}
+img {
+  border: 0;
+  vertical-align: middle;
+}
+table {
+  border-collapse: collapse;
+  border-spacing: 0;
+}
+p {
+  word-wrap: break-word;
+}
+iframe {
+  border: none;
+}
+
+@font-face {
+  font-family: "SourceHanSerifSC-Bold";
+  src: url("@/assets/fonts/SOURCEHANSERIFCN-BOLD.otf");
+}
+@font-face {
+  font-family: "SourceHanSerifSC-Regular";
+  src: url("@/assets/fonts/SOURCEHANSERIFCN-REGULAR.otf");
+}
+@font-face {
+  font-family: "SourceHanSerifCN-Light";
+  src: url("@/assets/fonts/SOURCEHANSERIFCN-LIGHT.otf");
+}
+
+.limit-line {
+  display: -webkit-box;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  -webkit-line-clamp: 1;
+  -webkit-box-orient: vertical;
+  word-break: break-all;
+  word-wrap: break-word;
+}
+
+.line-2 {
+  -webkit-line-clamp: 2;
+}
+
+.line-3 {
+  -webkit-line-clamp: 3;
+}
+
+.hidden {
+  display: none !important;
+  visibility: hidden !important;
+}
+
+.darkGlass {
+  background-color: rgba(0, 0, 0, 0.5);
+}
+
+.message-outer {
+  position: absolute;
+  display: table;
+  height: 100%;
+  width: 100%;
+
+  * {
+    transition: all 0.3s;
+  }
+}

+ 7 - 0
packages/pc/src/assets/utils.scss

@@ -0,0 +1,7 @@
+@function vh-calc($num) {
+  @return calc(100vh * ($num / var(--design-height)));
+}
+
+@function vw-calc($num) {
+  @return calc(100vw * ($num / var(--design-width)));
+}

+ 125 - 0
packages/pc/src/components/Header.vue

@@ -0,0 +1,125 @@
+<template>
+  <div class="header">
+    <ul class="header__tabs">
+      <li
+        v-for="(item, index) in TABS"
+        :key="item.path"
+        :class="{ active: activeTab === index }"
+        @click="handleTab(index)"
+      >
+        <span>{{ item.name }}</span>
+      </li>
+    </ul>
+
+    <div class="header-home" @click="router.push('/index')">
+      <span>首页</span>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { computed } from "vue";
+import { useRouter, useRoute } from "vue-router";
+import { TABS } from "@/utils/constants";
+
+const router = useRouter();
+const route = useRoute();
+
+const activeTab = computed(() => {
+  const currentPath = route.path;
+  const index = TABS.findIndex((tab) => tab.path === currentPath);
+  return index >= 0 ? index : 0;
+});
+
+const handleTab = (index) => {
+  const targetTab = TABS[index];
+  if (targetTab && targetTab.path !== route.path) {
+    router.push(targetTab.path);
+  }
+};
+</script>
+
+<style lang="scss" scoped>
+@use "@/assets/utils.scss";
+
+.header {
+  position: fixed;
+  left: 0;
+  right: 0;
+  padding: 0 47px 0 425px;
+  height: 73px;
+  background: url("@/assets/images/head-min.jpg") no-repeat left center / cover;
+  z-index: 9;
+
+  &::after {
+    content: "";
+    position: absolute;
+    top: 50%;
+    right: 168px;
+    width: 4px;
+    height: 59px;
+    background: url("@/assets/images/head-bd.png") no-repeat center / cover;
+    transform: translateY(-50%);
+  }
+  &-home {
+    position: absolute;
+    top: 20px;
+    right: 47px;
+    cursor: pointer;
+    transition: all linear 0.2s;
+
+    &:hover {
+      opacity: 0.8;
+    }
+    &::before {
+      content: "";
+      position: absolute;
+      top: -6px;
+      left: -34px;
+      width: 43px;
+      height: 43px;
+      background: url("@/assets/images/home.png") no-repeat center / contain;
+    }
+    span {
+      position: relative;
+      z-index: 1;
+      color: white;
+    }
+  }
+  &__tabs {
+    display: flex;
+    gap: 138px;
+    height: inherit;
+    font-size: 20px;
+    color: white;
+
+    li {
+      position: relative;
+      padding-top: 20px;
+      cursor: pointer;
+      transition: all linear 0.2s;
+
+      span {
+        position: relative;
+        z-index: 1;
+      }
+      &:hover {
+        opacity: 0.8;
+      }
+      &.active {
+        &::after {
+          content: "";
+          position: absolute;
+          top: 0;
+          left: 50%;
+          width: 130px;
+          height: 77px;
+          background: url("@/assets/images/tab-hover.jpg") no-repeat center /
+            cover;
+          transform: translateX(-50%);
+        }
+      }
+    }
+  }
+}
+</style>

+ 15 - 0
packages/pc/src/main.js

@@ -0,0 +1,15 @@
+import "./assets/main.css";
+import "animate.css";
+
+import { createApp } from "vue";
+import { createPinia } from "pinia";
+
+import App from "./App.vue";
+import router from "./router";
+
+const app = createApp(App);
+
+app.use(createPinia());
+app.use(router);
+
+app.mount("#app");

+ 32 - 0
packages/pc/src/router/index.js

@@ -0,0 +1,32 @@
+import { createRouter, createWebHashHistory } from "vue-router";
+
+const router = createRouter({
+  history: createWebHashHistory(import.meta.env.BASE_URL),
+  routes: [
+    {
+      path: "/",
+      name: "cover",
+      meta: {
+        hideTab: true,
+      },
+      component: () => import("../views/Cover/index.vue"),
+    },
+    {
+      path: "/index",
+      name: "home",
+      component: () => import("../views/Home/index.vue"),
+    },
+    {
+      path: "/step1",
+      name: "step1",
+      component: () => import("../views/Step1/index.vue"),
+    },
+    {
+      path: "/step2",
+      name: "step2",
+      component: () => import("../views/Step2/index.vue"),
+    },
+  ],
+});
+
+export default router;

+ 12 - 0
packages/pc/src/stores/counter.js

@@ -0,0 +1,12 @@
+import { ref, computed } from 'vue'
+import { defineStore } from 'pinia'
+
+export const useCounterStore = defineStore('counter', () => {
+  const count = ref(0)
+  const doubleCount = computed(() => count.value * 2)
+  function increment() {
+    count.value++
+  }
+
+  return { count, doubleCount, increment }
+})

+ 22 - 0
packages/pc/src/utils/constants.js

@@ -0,0 +1,22 @@
+export const TABS = [
+  {
+    path: "/index",
+    name: "前 言",
+  },
+  {
+    path: "/step1",
+    name: "第一部分",
+  },
+  {
+    path: "/step2",
+    name: "第二部分",
+  },
+  {
+    path: "/step3",
+    name: "第三部分",
+  },
+  {
+    path: "/end",
+    name: "结 语",
+  },
+];

BIN
packages/pc/src/views/Cover/images/1-1-min.png


BIN
packages/pc/src/views/Cover/images/1-min.png


BIN
packages/pc/src/views/Cover/images/2-1-min.png


BIN
packages/pc/src/views/Cover/images/2-min.png


BIN
packages/pc/src/views/Cover/images/3-1-min.png


BIN
packages/pc/src/views/Cover/images/3-min.png


BIN
packages/pc/src/views/Cover/images/4-1-min.png


BIN
packages/pc/src/views/Cover/images/4-min.png


BIN
packages/pc/src/views/Cover/images/5-1-min.png


BIN
packages/pc/src/views/Cover/images/5-min.png


BIN
packages/pc/src/views/Cover/images/bg-min.jpg


BIN
packages/pc/src/views/Cover/images/card-min.jpg


BIN
packages/pc/src/views/Cover/images/label-min.png


BIN
packages/pc/src/views/Cover/images/seal-min.png


BIN
packages/pc/src/views/Cover/images/title-min.png


+ 164 - 0
packages/pc/src/views/Cover/index.scss

@@ -0,0 +1,164 @@
+@use "@/assets/utils.scss";
+
+.cover {
+  position: absolute;
+  inset: 0;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  padding-top: utils.vh-calc(52);
+  width: 100%;
+  height: 100%;
+  overflow: hidden;
+  background: url("./images/bg-min.jpg") no-repeat center / cover;
+
+  &__head {
+    width: 1538px;
+    height: 96px;
+  }
+  &__content {
+    margin: utils.vh-calc(66) 0 utils.vh-calc(19);
+    position: relative;
+    width: utils.vh-calc(1543);
+    height: utils.vh-calc(589);
+    background: url("./images/card-min.jpg") no-repeat center / contain;
+
+    &::before {
+      content: "";
+      position: absolute;
+      top: 0;
+      left: 50%;
+      width: utils.vh-calc(252);
+      height: utils.vh-calc(649);
+      transform: translateX(-50%);
+      background: url("./images/label-min.png") no-repeat center / contain;
+    }
+  }
+  &__tabs {
+    display: flex;
+
+    li {
+      position: relative;
+      cursor: pointer;
+
+      &::before {
+        content: "";
+        position: absolute;
+        top: 50%;
+        left: 50%;
+        width: 100%;
+        height: 100%;
+        transform: translate(-50%, -50%);
+        transition: background linear 0.2s;
+      }
+      &:hover {
+        span {
+          display: block;
+        }
+      }
+      span {
+        display: none;
+        position: absolute;
+        top: utils.vh-calc(50);
+        left: utils.vh-calc(-70);
+        color: #323232;
+        writing-mode: vertical-rl;
+        white-space: nowrap;
+        font-size: utils.vh-calc(28);
+        letter-spacing: utils.vh-calc(2);
+
+        &::before {
+          content: "";
+          position: absolute;
+          top: utils.vh-calc(-34);
+          left: 50%;
+          width: utils.vh-calc(21);
+          height: utils.vh-calc(20);
+          background: url("./images/seal-min.png") no-repeat center / contain;
+          transform: translateX(-50%);
+        }
+      }
+    }
+    &-item1 {
+      width: utils.vh-calc(175);
+      height: utils.vh-calc(174);
+
+      &:hover {
+        &::before {
+          width: utils.vh-calc(242);
+          height: utils.vh-calc(242);
+          background-image: url("./images/1-1-min.png");
+        }
+      }
+      &::before {
+        background: url("./images/1-min.png") no-repeat center / contain;
+      }
+    }
+    &-item2 {
+      top: utils.vh-calc(-19);
+      margin: 0 168px;
+      width: utils.vh-calc(218);
+      height: utils.vh-calc(219);
+
+      &:hover {
+        &::before {
+          width: utils.vh-calc(257);
+          height: utils.vh-calc(258);
+          background-image: url("./images/2-1-min.png");
+        }
+      }
+      &::before {
+        background: url("./images/2-min.png") no-repeat center / contain;
+      }
+    }
+    &-item3 {
+      top: utils.vh-calc(34);
+      width: utils.vh-calc(175);
+      height: utils.vh-calc(174);
+
+      &:hover {
+        &::before {
+          width: utils.vh-calc(242);
+          height: utils.vh-calc(242);
+          background-image: url("./images/3-1-min.png");
+        }
+      }
+      &::before {
+        background: url("./images/3-min.png") no-repeat center / contain;
+      }
+    }
+    &-item4 {
+      top: utils.vh-calc(-9);
+      margin: 0 161px 0 153px;
+      width: utils.vh-calc(210);
+      height: utils.vh-calc(210);
+
+      &:hover {
+        &::before {
+          width: utils.vh-calc(257);
+          height: utils.vh-calc(258);
+          background-image: url("./images/4-1-min.png");
+        }
+      }
+      &::before {
+        background: url("./images/4-min.png") no-repeat center / contain;
+      }
+    }
+    &-item5 {
+      top: utils.vh-calc(-47);
+      width: utils.vh-calc(175);
+      height: utils.vh-calc(174);
+
+      &:hover {
+        &::before {
+          width: utils.vh-calc(242);
+          height: utils.vh-calc(242);
+          background-image: url("./images/5-1-min.png");
+        }
+      }
+      &::before {
+        background: url("./images/5-min.png") no-repeat center / contain;
+      }
+    }
+  }
+}

+ 31 - 0
packages/pc/src/views/Cover/index.vue

@@ -0,0 +1,31 @@
+<template>
+  <div class="cover">
+    <img
+      class="cover__head"
+      draggable="false"
+      src="./images/title-min.png"
+      alt="head"
+    />
+
+    <div class="cover__content" />
+
+    <ul class="cover__tabs">
+      <li
+        v-for="(item, index) in TABS"
+        :key="item.path"
+        :class="`cover__tabs-item${index + 1}`"
+        @click="$router.push(item.path)"
+      >
+        <span>{{ item.name }}</span>
+      </li>
+    </ul>
+  </div>
+</template>
+
+<script setup>
+import { TABS } from "@/utils/constants";
+</script>
+
+<style scoped lang="scss">
+@use "./index.scss";
+</style>

+ 115 - 0
packages/pc/src/views/Home/constants.js

@@ -0,0 +1,115 @@
+import M1Img from "./images/m1-min.png";
+import M2Img from "./images/m2-min.png";
+import M3Img from "./images/m3-min.png";
+import M4Img from "./images/m4-min.png";
+import M1ActImg from "./images/m1-active-min.png";
+import M2ActImg from "./images/m2-active-min.png";
+import M3ActImg from "./images/m3-active-min.png";
+import M4ActImg from "./images/m4-active-min.png";
+import C1Img from "./images/c1-min.png";
+import C2Img from "./images/c2-min.png";
+import C3Img from "./images/c3-min.png";
+import C4Img from "./images/c4-min.png";
+import C5Img from "./images/c5-min.png";
+import C6Img from "./images/c6-min.png";
+import T1Img from "./images/t1-min.png";
+import T2Img from "./images/t2-min.png";
+import T3Img from "./images/t3-min.png";
+import T4Img from "./images/t4-min.png";
+import T5Img from "./images/t5-min.png";
+import T6Img from "./images/t6-min.png";
+
+export const MIMAGES = [
+  {
+    default: M1Img,
+    active: M1ActImg,
+    href: "https://houseoss.4dkankan.com/project/guyuan/scene/index.html?m=SG-H7glLFFd4dn",
+  },
+  {
+    default: M2Img,
+    active: M2ActImg,
+    routeName: "antique",
+  },
+  {
+    default: M3Img,
+    active: M3ActImg,
+    routeName: "history",
+  },
+  {
+    type: "share",
+    default: M4Img,
+    active: M4ActImg,
+  },
+];
+
+export const POINTS = [
+  {
+    img: C1Img,
+    img2: T1Img,
+    content: `
+      <p>入境西吉县,</p>
+      <p>毛泽东夜访单南寺,</p>
+      <p>与马德海秉烛长谈,</p>
+      <p>宣讲民族政策及抗日主张,</p>
+      <p>军民鱼水情浓,</p>
+      <p>史称“单家集夜话”。</p>
+    `,
+  },
+  {
+    img: C2Img,
+    img2: T2Img,
+    content: `
+      <p>夜宿张易堡,</p>
+      <p>红军继续行军,</p>
+      <p>宿营张易堡,</p>
+      <p>严守纪律不扰民,</p>
+      <p>借门板铺地休息,</p>
+      <p>清晨归还并留铜元致谢,</p>
+      <p>百姓称“仁义之师”。</p>
+    `,
+  },
+  {
+    img: C3Img,
+    img2: T3Img,
+    content: `
+      <p>青石嘴突袭战,</p>
+      <p>红军突袭青石嘴,</p>
+      <p>半小时歼敌两个连,</p>
+      <p>缴获战马百余匹,</p>
+      <p>遂组建中央红军首支骑兵侦察连。</p>
+    `,
+  },
+  {
+    img: C4Img,
+    img2: T4Img,
+    content: `
+      <p>翻越六盘山后,</p>
+      <p>毛泽东登顶六盘山,</p>
+      <p>远望长城,</p>
+      <p>夜宿小岔沟土炕,</p>
+      <p>即兴吟成《长征谣》初稿,</p>
+      <p>“不到长城非好汉”由此诞生。</p>
+    `,
+  },
+  {
+    img: C5Img,
+    img2: T5Img,
+    content: `
+      <p>过境彭阳县,</p>
+      <p>午后抵乔家渠,</p>
+      <p>毛泽东宿乔生魁家,</p>
+      <p>土炕上铺地图部署陕北行军,</p>
+      <p>房东悄悄塞给战士两筐土豆,</p>
+      <p>军民泪别。</p>
+    `,
+  },
+  {
+    img: C6Img,
+    img2: T6Img,
+    content: `
+      <p>出境固原,</p>
+      <p>拂晓从彭阳长城塬出发,</p>
+      <p>踏上奔赴陕北抗日的新征程。</p>
+    `,
+  },
+];

BIN
packages/pc/src/views/Home/images/bd-min.png


BIN
packages/pc/src/views/Home/images/bg-min.jpg


BIN
packages/pc/src/views/Home/images/bg2.jpg


BIN
packages/pc/src/views/Home/images/card-bg-min.png


+ 0 - 0
packages/pc/src/views/Home/images/footer-min.jpg


Vissa filer visades inte eftersom för många filer har ändrats