Ver código fonte

🚧 重建整理优化项目2 保护传承

快乐的梦鱼 1 mês atrás
pai
commit
63992ee974

+ 243 - 3
package-lock.json

@@ -11,10 +11,13 @@
         "@imengyu/imengyu-utils": "^0.0.14",
         "@imengyu/js-request-transform": "^0.3.5",
         "@imengyu/vue-scroll-rect": "^0.1.7",
+        "@vuemap/vue-amap": "^2.1.16",
         "ant-design-vue": "^4.2.6",
+        "echarts": "^5.6.0",
         "md5": "^2.3.0",
         "pinia": "^3.0.3",
         "vue": "^3.5.17",
+        "vue-echarts": "^7.0.3",
         "vue-router": "^4.5.1",
         "vue3-carousel": "^0.16.0",
         "vue3-marquee": "^4.2.2"
@@ -2257,6 +2260,23 @@
         "@jridgewell/sourcemap-codec": "^1.4.14"
       }
     },
+    "node_modules/@math.gl/core": {
+      "version": "3.6.3",
+      "resolved": "https://registry.npmjs.org/@math.gl/core/-/core-3.6.3.tgz",
+      "integrity": "sha512-jBABmDkj5uuuE0dTDmwwss7Cup5ZwQ6Qb7h1pgvtkEutTrhkcv8SuItQNXmF45494yIHeoGue08NlyeY6wxq2A==",
+      "license": "MIT",
+      "dependencies": {
+        "@babel/runtime": "^7.12.0",
+        "@math.gl/types": "3.6.3",
+        "gl-matrix": "^3.4.0"
+      }
+    },
+    "node_modules/@math.gl/types": {
+      "version": "3.6.3",
+      "resolved": "https://registry.npmjs.org/@math.gl/types/-/types-3.6.3.tgz",
+      "integrity": "sha512-3uWLVXHY3jQxsXCr/UCNPSc2BG0hNUljhmOBt9l+lNFDp7zHgm0cK2Tw4kj2XfkJy4TgwZTBGwRDQgWEbLbdTA==",
+      "license": "MIT"
+    },
     "node_modules/@polka/url": {
       "version": "1.0.0-next.29",
       "resolved": "https://registry.npmmirror.com/@polka/url/-/url-1.0.0-next.29.tgz",
@@ -2611,6 +2631,41 @@
       "dev": true,
       "license": "MIT"
     },
+    "node_modules/@turf/helpers": {
+      "version": "6.5.0",
+      "resolved": "https://registry.npmjs.org/@turf/helpers/-/helpers-6.5.0.tgz",
+      "integrity": "sha512-VbI1dV5bLFzohYYdgqwikdMVpe7pJ9X3E+dlr425wa2/sMJqYDhTO++ec38/pcPvPE6oD9WEEeU3Xu3gza+VPw==",
+      "license": "MIT",
+      "funding": {
+        "url": "https://opencollective.com/turf"
+      }
+    },
+    "node_modules/@turf/intersect": {
+      "version": "6.5.0",
+      "resolved": "https://registry.npmjs.org/@turf/intersect/-/intersect-6.5.0.tgz",
+      "integrity": "sha512-2legGJeKrfFkzntcd4GouPugoqPUjexPZnOvfez+3SfIMrHvulw8qV8u7pfVyn2Yqs53yoVCEjS5sEpvQ5YRQg==",
+      "license": "MIT",
+      "dependencies": {
+        "@turf/helpers": "^6.5.0",
+        "@turf/invariant": "^6.5.0",
+        "polygon-clipping": "^0.15.3"
+      },
+      "funding": {
+        "url": "https://opencollective.com/turf"
+      }
+    },
+    "node_modules/@turf/invariant": {
+      "version": "6.5.0",
+      "resolved": "https://registry.npmjs.org/@turf/invariant/-/invariant-6.5.0.tgz",
+      "integrity": "sha512-Wv8PRNCtPD31UVbdJE/KVAWKe7l6US+lJItRR/HOEW3eh+U/JwRCSUl/KZ7bmjM/C+zLNoreM2TU6OoLACs4eg==",
+      "license": "MIT",
+      "dependencies": {
+        "@turf/helpers": "^6.5.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/turf"
+      }
+    },
     "node_modules/@tweenjs/tween.js": {
       "version": "23.1.3",
       "resolved": "https://registry.npmjs.org/@tweenjs/tween.js/-/tween.js-23.1.3.tgz",
@@ -3047,6 +3102,69 @@
         }
       }
     },
+    "node_modules/@vuemap/amap-jsapi-loader": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/@vuemap/amap-jsapi-loader/-/amap-jsapi-loader-1.0.4.tgz",
+      "integrity": "sha512-s5fFHrsNkjYMovEmUJ5S23jpDtElTanDN2HdCt/amOD245a8wWVcTPjl06YEHXtxf6Ewm+z29wQByOCn209Hxg==",
+      "license": "MIT"
+    },
+    "node_modules/@vuemap/amap-jsapi-types": {
+      "version": "0.0.17",
+      "resolved": "https://registry.npmjs.org/@vuemap/amap-jsapi-types/-/amap-jsapi-types-0.0.17.tgz",
+      "integrity": "sha512-FHI8OMWxJWbgyuQ0tKclvurQIVHRexMIYAOwZ/z9+G7aHHK5EFhKM13siLczNNAgXdJ2dctPEghCdlhcByl3Ag==",
+      "license": "MIT"
+    },
+    "node_modules/@vuemap/amap-xyz-layer": {
+      "version": "0.0.15",
+      "resolved": "https://registry.npmjs.org/@vuemap/amap-xyz-layer/-/amap-xyz-layer-0.0.15.tgz",
+      "integrity": "sha512-L3rsgk2+i277RlMScpxVpjPBhSfMCeHcyFv7zkomMzd/J9W+X8yTUgSFvzVLWnnlXEXL80vNgs7lIJpSxa5vNg==",
+      "license": "MIT",
+      "dependencies": {
+        "@math.gl/core": "3.6.3",
+        "earcut": "2.2.4",
+        "gl-matrix": "3.4.3"
+      },
+      "engines": {
+        "node": ">= 16"
+      }
+    },
+    "node_modules/@vuemap/district-cluster": {
+      "version": "0.0.12",
+      "resolved": "https://registry.npmjs.org/@vuemap/district-cluster/-/district-cluster-0.0.12.tgz",
+      "integrity": "sha512-JmZxl99PfCsF98dvxTmi17Ti3XaQMwcKidgduiIQvAPNTKmZjBAR1RRtdQjxooIuJiLXJo9itWIRuBaD9MseTQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@turf/helpers": "^6.5.0",
+        "@turf/intersect": "^6.5.0",
+        "@vuemap/amap-jsapi-types": "^0.0.16",
+        "topojson-client": "3.1.0"
+      },
+      "engines": {
+        "node": ">= 16"
+      }
+    },
+    "node_modules/@vuemap/district-cluster/node_modules/@vuemap/amap-jsapi-types": {
+      "version": "0.0.16",
+      "resolved": "https://registry.npmjs.org/@vuemap/amap-jsapi-types/-/amap-jsapi-types-0.0.16.tgz",
+      "integrity": "sha512-1B1H2IS8sT2RDubbpEY+K8j11Gb7PZY5Bo0cszRkF8Nw+9HNqpbUNeqkQ6+rxLkwIedcSkOsFDy/IyzXCUXqVw==",
+      "license": "MIT"
+    },
+    "node_modules/@vuemap/vue-amap": {
+      "version": "2.1.16",
+      "resolved": "https://registry.npmjs.org/@vuemap/vue-amap/-/vue-amap-2.1.16.tgz",
+      "integrity": "sha512-sLCQbtgoHDNzgQjhqv2mbpqBU3+Yh/lJYQ+1O9UJsRLn76oI42W76BAVGLZhIWdcDJyW+YNmROo74iCl16hNxA==",
+      "license": "MIT",
+      "dependencies": {
+        "@vuemap/amap-jsapi-loader": "1.0.4",
+        "@vuemap/amap-jsapi-types": "^0.0.17",
+        "@vuemap/amap-xyz-layer": "0.0.15",
+        "@vuemap/district-cluster": "0.0.12",
+        "lodash-es": "^4.17.21"
+      },
+      "peerDependencies": {
+        "vue": "3"
+      }
+    },
     "node_modules/@webgpu/types": {
       "version": "0.1.64",
       "resolved": "https://registry.npmjs.org/@webgpu/types/-/types-0.1.64.tgz",
@@ -3409,9 +3527,7 @@
       "version": "2.20.3",
       "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
       "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
-      "dev": true,
-      "license": "MIT",
-      "peer": true
+      "license": "MIT"
     },
     "node_modules/compute-scroll-into-view": {
       "version": "1.0.20",
@@ -3612,6 +3728,28 @@
       "integrity": "sha512-bvVTQe1lfaUr1oFzZX80ce9KLDlZ3iU+XGNE/bz9HnGdklTieqsbmsLHe+rT2XWqopvL0PckkYqN7ksmm5pe3w==",
       "license": "MIT"
     },
+    "node_modules/earcut": {
+      "version": "2.2.4",
+      "resolved": "https://registry.npmjs.org/earcut/-/earcut-2.2.4.tgz",
+      "integrity": "sha512-/pjZsA1b4RPHbeWZQn66SWS8nZZWLQQ23oE3Eam7aroEFGEvwKAsJfZ9ytiEMycfzXWpca4FA9QIOehf7PocBQ==",
+      "license": "ISC"
+    },
+    "node_modules/echarts": {
+      "version": "5.6.0",
+      "resolved": "https://registry.npmjs.org/echarts/-/echarts-5.6.0.tgz",
+      "integrity": "sha512-oTbVTsXfKuEhxftHqL5xprgLoc0k7uScAwtryCgWF6hPYFLRwOUHiFmHGCBKP5NPFNkDVopOieyUqYGH8Fa3kA==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "tslib": "2.3.0",
+        "zrender": "5.6.1"
+      }
+    },
+    "node_modules/echarts/node_modules/tslib": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz",
+      "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==",
+      "license": "0BSD"
+    },
     "node_modules/electron-to-chromium": {
       "version": "1.5.190",
       "resolved": "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.5.190.tgz",
@@ -3861,6 +3999,12 @@
         "url": "https://github.com/sponsors/sindresorhus"
       }
     },
+    "node_modules/gl-matrix": {
+      "version": "3.4.3",
+      "resolved": "https://registry.npmjs.org/gl-matrix/-/gl-matrix-3.4.3.tgz",
+      "integrity": "sha512-wcCp8vu8FT22BnvKVPjXa/ICBWRq/zjFfdofZy1WSpQZpphblv12/bOQLBC1rMM7SGOFS9ltVmKOHil5+Ml7gA==",
+      "license": "MIT"
+    },
     "node_modules/glob-parent": {
       "version": "5.1.2",
       "resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz",
@@ -4616,6 +4760,16 @@
         "pathe": "^2.0.3"
       }
     },
+    "node_modules/polygon-clipping": {
+      "version": "0.15.7",
+      "resolved": "https://registry.npmjs.org/polygon-clipping/-/polygon-clipping-0.15.7.tgz",
+      "integrity": "sha512-nhfdr83ECBg6xtqOAJab1tbksbBAOMUltN60bU+llHVOL0e5Onm1WpAXXWXVB39L8AJFssoIhEVuy/S90MmotA==",
+      "license": "MIT",
+      "dependencies": {
+        "robust-predicates": "^3.0.2",
+        "splaytree": "^3.1.0"
+      }
+    },
     "node_modules/postcss": {
       "version": "8.5.6",
       "resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.5.6.tgz",
@@ -4828,6 +4982,12 @@
       "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==",
       "license": "MIT"
     },
+    "node_modules/robust-predicates": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.2.tgz",
+      "integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==",
+      "license": "Unlicense"
+    },
     "node_modules/rollup": {
       "version": "4.45.1",
       "resolved": "https://registry.npmmirror.com/rollup/-/rollup-4.45.1.tgz",
@@ -5334,6 +5494,12 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/splaytree": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/splaytree/-/splaytree-3.1.2.tgz",
+      "integrity": "sha512-4OM2BJgC5UzrhVnnJA4BkHKGtjXNzzUfpQjCO8I05xYPsfS/VuQDwjCGGMi8rYQilHEV4j8NBqTFbls/PZEE7A==",
+      "license": "MIT"
+    },
     "node_modules/strip-final-newline": {
       "version": "4.0.0",
       "resolved": "https://registry.npmmirror.com/strip-final-newline/-/strip-final-newline-4.0.0.tgz",
@@ -5483,6 +5649,20 @@
         "node": ">=8.0"
       }
     },
+    "node_modules/topojson-client": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/topojson-client/-/topojson-client-3.1.0.tgz",
+      "integrity": "sha512-605uxS6bcYxGXw9qi62XyrV6Q3xwbndjachmNxu8HWTtVPxZfEJN9fd/SZS1Q54Sn2y0TMyMxFj/cJINqGHrKw==",
+      "license": "ISC",
+      "dependencies": {
+        "commander": "2"
+      },
+      "bin": {
+        "topo2geo": "bin/topo2geo",
+        "topomerge": "bin/topomerge",
+        "topoquantize": "bin/topoquantize"
+      }
+    },
     "node_modules/totalist": {
       "version": "3.0.1",
       "resolved": "https://registry.npmmirror.com/totalist/-/totalist-3.0.1.tgz",
@@ -5892,6 +6072,51 @@
         }
       }
     },
+    "node_modules/vue-demi": {
+      "version": "0.13.11",
+      "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.13.11.tgz",
+      "integrity": "sha512-IR8HoEEGM65YY3ZJYAjMlKygDQn25D5ajNFNoKh9RSDMQtlzCxtfQjdQgv9jjK+m3377SsJXY8ysq8kLCZL25A==",
+      "hasInstallScript": true,
+      "license": "MIT",
+      "bin": {
+        "vue-demi-fix": "bin/vue-demi-fix.js",
+        "vue-demi-switch": "bin/vue-demi-switch.js"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/antfu"
+      },
+      "peerDependencies": {
+        "@vue/composition-api": "^1.0.0-rc.1",
+        "vue": "^3.0.0-0 || ^2.6.0"
+      },
+      "peerDependenciesMeta": {
+        "@vue/composition-api": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/vue-echarts": {
+      "version": "7.0.3",
+      "resolved": "https://registry.npmjs.org/vue-echarts/-/vue-echarts-7.0.3.tgz",
+      "integrity": "sha512-/jSxNwOsw5+dYAUcwSfkLwKPuzTQ0Cepz1LxCOpj2QcHrrmUa/Ql0eQqMmc1rTPQVrh2JQ29n2dhq75ZcHvRDw==",
+      "license": "MIT",
+      "dependencies": {
+        "vue-demi": "^0.13.11"
+      },
+      "peerDependencies": {
+        "@vue/runtime-core": "^3.0.0",
+        "echarts": "^5.5.1",
+        "vue": "^2.7.0 || ^3.1.1"
+      },
+      "peerDependenciesMeta": {
+        "@vue/runtime-core": {
+          "optional": true
+        }
+      }
+    },
     "node_modules/vue-router": {
       "version": "4.5.1",
       "resolved": "https://registry.npmmirror.com/vue-router/-/vue-router-4.5.1.tgz",
@@ -6033,6 +6258,21 @@
       "funding": {
         "url": "https://github.com/sponsors/sindresorhus"
       }
+    },
+    "node_modules/zrender": {
+      "version": "5.6.1",
+      "resolved": "https://registry.npmjs.org/zrender/-/zrender-5.6.1.tgz",
+      "integrity": "sha512-OFXkDJKcrlx5su2XbzJvj/34Q3m6PvyCZkVPHGYpcCJ52ek4U/ymZyfuV1nKE23AyBJ51E/6Yr0mhZ7xGTO4ag==",
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "tslib": "2.3.0"
+      }
+    },
+    "node_modules/zrender/node_modules/tslib": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz",
+      "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==",
+      "license": "0BSD"
     }
   }
 }

+ 3 - 0
package.json

@@ -14,10 +14,13 @@
     "@imengyu/imengyu-utils": "^0.0.14",
     "@imengyu/js-request-transform": "^0.3.5",
     "@imengyu/vue-scroll-rect": "^0.1.7",
+    "@vuemap/vue-amap": "^2.1.16",
     "ant-design-vue": "^4.2.6",
+    "echarts": "^5.6.0",
     "md5": "^2.3.0",
     "pinia": "^3.0.3",
     "vue": "^3.5.17",
+    "vue-echarts": "^7.0.3",
     "vue-router": "^4.5.1",
     "vue3-carousel": "^0.16.0",
     "vue3-marquee": "^4.2.2"

BIN
src/assets/images/Home/Background/4.jpg


BIN
src/assets/images/Home/Background/5.jpg


BIN
src/assets/images/Home/Background/6.jpg


BIN
src/assets/images/Inherit/map.jpg


+ 10 - 0
src/assets/scss/main.scss

@@ -23,6 +23,7 @@ html, body {
 main {
   width: 100%;
   height: 100vh;
+  background-color: var(--color-light-bg);
 
   button {
     font-family: nzgrRuyinZouZhangKai-Regular;
@@ -73,6 +74,15 @@ main {
   &3 {
     background-image: url('@/assets/images/Home/Background/3.jpg');
   }
+  &4 {
+    background-image: url('@/assets/images/Home/Background/4.jpg');
+  }
+  &5 {
+    background-image: url('@/assets/images/Home/Background/5.jpg');
+  }
+  &6 {
+    background-image: url('@/assets/images/Home/Background/6.jpg');
+  }
 }
 .main-content {
   position: relative;

+ 6 - 3
src/components/small/ImagePreview.vue

@@ -4,9 +4,12 @@
     @update:show="(v) => emit('update:show', v)"
   >
     <div class="dialog bg-light p-4 d-flex flex-col">
-      <div class="d-flex flex-row align-center ">
-        <img class="main-image-button" src="@/assets/images/IconBack.png" tabindex="0" @click="emit('update:show', false)"></img>
-        <h2 class="mb-0 ml-3">查看大图</h2>
+      <div class="d-flex flex-row align-center justify-between">
+        <div class="d-flex flex-row align-center">
+          <img class="main-image-button" src="@/assets/images/IconBack.png" tabindex="0" @click="emit('update:show', false)"></img>
+          <h2 class="mb-0 ml-3">查看大图</h2>
+        </div>
+        <h2 class="mb-0">{{ activeItem + 1 }} / {{ list.length }}</h2>
       </div>
       <ScrollLeftRightButton
         class="d-flex flex-row align-center w-100 position-relative"

+ 5 - 0
src/components/small/Tab.vue

@@ -6,6 +6,7 @@
       class="tab-item"
       :tabindex="1"
       :class="{ 'active': activeIndex === index }"
+      :style="{ width: itemWidth }"
       @click="handleTabClick(index)"
     >
       {{ tab.label }}
@@ -31,6 +32,10 @@ const props = defineProps({
   autoSize: {
     type: Boolean,
     default: false
+  },
+  itemWidth: {
+    type: String,
+    default: 'auto'
   }
 });
 

+ 8 - 0
src/main.ts

@@ -1,6 +1,7 @@
 import './assets/scss/main.scss'
 import './assets/scss/mengyuu/index.scss'
 import 'ant-design-vue/dist/reset.css';
+import "@vuemap/vue-amap/dist/style.css";
 import '@imengyu/vue-scroll-rect/lib/vue-scroll-rect.css';
 import "vue3-carousel/carousel.css";
 
@@ -11,14 +12,21 @@ import Vue3Marquee from 'vue3-marquee'
 import VueScrollRect from '@imengyu/vue-scroll-rect';
 import App from './App.vue'
 import router from './router'
+import VueAMap, { initAMapApiLoader } from '@vuemap/vue-amap';
 import { registryConvert } from './common/ConvertRgeistry';
 
+initAMapApiLoader({
+  key: '212b86dc49a5116a34e945d31da7ad14',
+  securityJsCode: '46cae8205a707cfaf5801abcc4301566',
+});
+
 const app = createApp(App)
 
 app.use(createPinia())
 app.use(router)
 app.use(VueScrollRect);
 app.use(Vue3Marquee);
+app.use(VueAMap);
 
 app.mount('#app').$nextTick(() => {
   registryConvert();

+ 16 - 0
src/router/index.ts

@@ -29,6 +29,22 @@ const router = createRouter({
       name: 'VideoDetail',
       component: () => import('../views/Details/VideoDetail.vue'),
     },
+    {
+      path: '/artifact',
+      name: 'ArtifactDetail',
+      component: () => import('../views/Details/ArtifactDetail.vue'),
+    },
+    {
+      path: '/artifact',
+      name: 'ArtifactDetail',
+      component: () => import('../views/Details/ArtifactDetail.vue'),
+    },
+    {
+      path: '/IntangibleList',
+      name: 'IntangibleList',
+      component: () => import('../views/Intangible/List.vue'),
+    },
+
   ],
 })
 

+ 279 - 14
src/views/Content/TabInherit.vue

@@ -1,43 +1,308 @@
 <template>
   <SimplePageListContentLoader class="d-flex flex-col flex-fill" :loader="loader">
-    <Tab  />
+    <Tab 
+      v-model="tab" 
+      :tabs="[
+        { label: '文物' },
+        { label: '非遗' },
+      ]" 
+      autoSize
+      itemWidth="100px" 
+    />
+    <div v-if="tab === 0" class="content flex-fill gap-s">
+      <div class="round-box introduction">
+        <h1>闽南建筑文化简介</h1>
+        <Vue3Marquee class="introduction-content" :duration="60" vertical>
+          <p>闽南建筑风格独特。闽南建筑风格以红墙、红瓦、燕尾脊为特征,给人以鲜明、热烈的视觉感受。建筑中大量使用装饰性构件,如砖雕、石雕、木雕等,这些雕刻工艺精湛,图案丰富,具有很高的艺术价值。同时,建筑内部的布局和装饰也充满了闽南文化的特色,如“出砖入石”的墙体、精细的石雕和木雕、富有地方特色的彩绘等。</p>
+          <p>闽南建筑融合了多元文化。闽南地区历史上是中原移民的重要聚居地,同时又与东南亚地区交往密切,因此闽南建筑文化中融合了中原文化、东南亚文化等多元文化因素。例如,闽南建筑的屋顶形式、装饰风格等受到中原文化的影响;而建筑中的雕刻艺术、建筑材料等则体现了东南亚文化的特色。这种多元文化的融合使得闽南建筑具有更加丰富的文化内涵</p>
+          <p>通过了解这些闽南建筑文物的特点,人们能够更好地认识和了解闽南文化。同时,这些文物也是传承和弘扬闽南文化的重要载体。在今天的社会中,随着城市化进程的加速和传统文化的逐渐消失,保护和传承这些宝贵的文化遗产显得尤为重要。</p>
+        </Vue3Marquee>
+        <div class="d-flex flex-row chart-container">
+          <v-chart class="chart" :option="chartOptionCity" autoresize />
+          <v-chart class="chart" :option="chartOptionRegion" autoresize />
+        </div>
+      </div>
+      <div class="d-flex flex-col map">
+        <div class="round-box box main-any-button" :tabindex="1" />
+        <GridList 
+          :list="loader.content.value?.list"
+          item-style-type="round-box main-any-button" 
+          @itemClick="handleItemClick"
+        />
+      </div>
+    </div>
+    <div v-else-if="tab === 1" class="content flex-fill">
+      <div></div>
+      <div class="d-flex flex-col">
+        <h1>非遗传承</h1>
+        <p>以“闽南人”与“闽南建筑文化”为纽带构建三种精神:闽南精神、闽台精神、海丝精神体现闽南“魂”</p>
+        <div class="intangible-list">
+          <div
+            v-for="(value, key) in intangibleList"
+            :key="key"
+            :style="{ background: `url(${value.image}) center center / cover` }"
+            :tabindex="1"
+            class="main-any-button"
+            @click="handleIntangibleClick(value)"
+          >
+            {{ value.title }}
+          </div>
+      </div>
+      </div>
+    </div>
   </SimplePageListContentLoader>
 </template>
 
 <script setup lang="ts">
+import { ref } from 'vue';
 import { useSimpleDataLoader } from '@/composeable/SimpleDataLoader';
 import { useRouter } from 'vue-router';
-import CommonContent, { GetColumListParams } from '@/api/CommonContent';
+import CommonContent, { GetContentListParams } from '@/api/CommonContent';
 import SimplePageListContentLoader from '@/components/SimplePageListContentLoader.vue';
 import GridList from '@/components/small/GridList.vue';
-import { ScrollRect } from '@imengyu/vue-scroll-rect';
+import VChart from 'vue-echarts';
 import Tab from '@/components/small/Tab.vue';
+import { ScrollRect } from '@imengyu/vue-scroll-rect';
+import { Vue3Marquee } from 'vue3-marquee';
+import { use } from 'echarts/core';
+import { CanvasRenderer } from 'echarts/renderers';
+import { PieChart } from 'echarts/charts';
+import { TitleComponent, TooltipComponent } from 'echarts/components';
+import IndexContent from '@/api/introduction/IndexContent';
+
+use([
+  TitleComponent,
+  CanvasRenderer,
+  PieChart,
+  TooltipComponent,
+]);
 
 const emit = defineEmits(['itemClick']);
 const router = useRouter();
 const loader = useSimpleDataLoader(async () => {
-  return CommonContent.getColumList(new GetColumListParams()
-    .setModelId(4)
-    .setMainBodyColumnId([ 8,9,246,248 ])
-  )
+
+  const stats = await IndexContent.getStats();
+  stats.crData.forEach((i: any) => {
+    const existsItem = chartOptionCity.value.series[0].data.find((p) => p.name === i.title);
+    if (existsItem)
+      existsItem.value = i.total;
+  });
+  stats.ichData.forEach((i: any) => {
+    const existsItem = chartOptionRegion.value.series[0].data.find((p) => p.name === i.title);
+    if (existsItem)
+      existsItem.value = i.total;
+  });
+
+  return CommonContent.getContentList(new GetContentListParams().setModelId(1), 1, 12)
 });
 
+const tab = ref(0);
+const chartOptionCity = ref({
+  tooltip: {
+    trigger: 'item',
+    formatter: '{a} <br/>{b} : {c} ({d}%)',
+  },
+  title: {
+    text: '厦门市文物统计',
+    left: 'center',
+  },
+  series: [
+    {
+      name: '厦门市文物统计',
+      type: 'pie',
+      radius: '55%',
+      center: ['50%', '60%'],
+      data: [
+        { name: '湖里区', value: 126 },
+        { name: '思明区', value: 178 },
+        { name: '海沧区', value: 478 },
+        { name: '鼓浪屿', value: 151 },
+        { name: '集美区', value: 220 },
+        { name: '同安区', value: 503 },
+        { name: '翔安区', value: 365 },
+      ],
+      label: {
+        show: true,
+        formatter: '{b}: {c}'
+      },
+      emphasis: {
+        itemStyle: {
+          shadowBlur: 10,
+          shadowOffsetX: 0,
+          shadowColor: 'rgba(0, 0, 0, 0.5)',
+        },
+      },
+    },
+  ],
+});
+const chartOptionRegion = ref({
+  tooltip: {
+    trigger: 'item',
+    formatter: '{a} <br/>{b} : {c} ({d}%)',
+  },
+  title: {
+    text: '厦门市非遗统计',
+    left: 'center',
+  },
+  series: [
+    {
+      name: '厦门市非遗统计',
+      type: 'pie',
+      radius: '55%',
+      center: ['50%', '60%'],
+      data: [
+        { name: '国家级', value: 48 },
+        { name: '省级', value: 57 },
+        { name: '市级', value: 88 },
+        { name: '县级', value: 35 },
+        { name: '区级', value: 54 },
+      ],
+      label: {
+        show: true,
+        formatter: '{b}: {c}'
+      },
+      emphasis: {
+        itemStyle: {
+          shadowBlur: 10,
+          shadowOffsetX: 0,
+          shadowColor: 'rgba(0, 0, 0, 0.5)',
+        },
+      },
+    },
+  ],
+});
+const intangibleList = [
+  {
+    image: 'https://huli-app.wenlvti.net/app_static/minnanhun/image/fy_1.png',
+    title: '非遗项目',
+    modelId: 2,
+  },
+  {
+    image: 'https://huli-app.wenlvti.net/app_static/minnanhun/image/fy_2.png',
+    title: '传承人',
+    modelId: 7,
+    mainBodyColumnId: 38,
+  },
+  {
+    image: 'https://huli-app.wenlvti.net/app_static/minnanhun/image/fy_3.png',
+    title: '非遗产品',
+    modelId: 9,
+  },
+  {
+    image: 'https://huli-app.wenlvti.net/app_static/minnanhun/image/fy_4.png',
+    title: '非遗活动',
+    modelId: 18,
+    mainBodyColumnId: 290,
+  }
+];
+
+function handleIntangibleClick(item: any) {
+  router.push({ name: 'IntangibleList', query: { 
+    modelId: item.modelId,
+    mainBodyColumnId: item.mainBodyColumnId,
+  } });
+}
 function handleItemClick(item: any) {
-  router.push({ name: 'ArticleDetail', query: { id: item.id } });
+  router.push({ name: 'ArtifactDetail', query: { id: item.id } });
 }
-
-
 </script>
 
 <style scoped lang="scss">
+.content {
+  width: 100%;
+  height: 80%;
+  display: flex;
+  flex-direction: row;
+  align-items: stretch;
+  position: relative;
+
+  > div {
+    width: 50%;
+    height: 100%;
+    display: flex;
+    flex-direction: column;
+
+  }
+}
+
+.round-box {
+  padding: 1vw;
+  border-radius: 1vw;
+  box-sizing: border-box;
+  border: var(--color-primary) solid 3px;
+  overflow: hidden;
+}
+.introduction {
+  background: url(https://huli-app.wenlvti.net/app_static/minnanhun/image/jzjs_bg.jpg) no-repeat center;
+  background-size: cover;
+  color: var(--color-text-primary);
+  font-size: 0.8rem;
+
+  .introduction-content {
+    width: 100%;
+    height: 50%;
+  }
+  .chart-container {
+    position: relative;
+    width: 100%;
+    height: 48%;
+    margin-top: 2%;
+  }
+  .chart {
+    width: 50%;
+    height: 100%;
+  }
+}
+.map {
+  position: relative;
+
+  .box {
+    width: 100%;
+    height: 26%;
+    background: url('@/assets/images/Inherit/map.jpg') no-repeat center;
+    background-size: cover;
+  }
+  :deep(.grid-container) {
+    height: 72%;
+    margin-top: 2%;
+    grid-template-rows: repeat(3, 1fr);
+  }
+  :deep(.round-box) {
+    padding: 1vw;
+    border-radius: 1vw;
+    box-sizing: border-box;
+    border: var(--color-primary) solid 3px;
+    overflow: hidden;
+  }
+}
+.intangible-list {
+  display: flex;
+  flex-direction: row;
+  justify-content: space-between;
+  align-items: center;
+  flex-wrap: wrap;
+  gap: 20px;
+
+  > div {
+    width: 48%;
+    aspect-ratio: 3 / 1;
+    border-radius: 15px;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    padding-left: 20%;
+    color: var(--color-text-primary);
+  }
+}
+
 h1 {
   margin-top: 0.5rem;
   font-size: 1rem;
   color: var(--color-text-primary);
 }
-
-:deep(.grid-container) {
-  grid-template-rows: repeat(1, 1fr);
+p {
+  font-size: 0.8rem;
+  color: var(--color-text);
 }
-</style>
 
+</style>

+ 20 - 1
src/views/ContentView.vue

@@ -5,6 +5,7 @@ import Tab from '@/components/small/Tab.vue';
 import TabCommonList from './Content/TabCommonList.vue';
 import Header from '@/components/parts/Header.vue';
 import TabCustomList from './Content/TabCustomList.vue';
+import TabInherit from './Content/TabInherit.vue';
 
 const route = useRoute();
 const router = useRouter();
@@ -51,6 +52,23 @@ const activeTabId = computed(() => {
   return mainTabData.find((item) => item.id === activeTab.value)?.id;
 });
 
+const backgroundType = computed(() => {
+  switch(activeTabId.value) {
+    case 3:
+      return 'main-bg4';
+    case 4:
+      return 'main-bg2';
+    case 5:
+      return 'main-bg5';
+    case 8:
+      return 'main-bg6';
+    default:
+      return 'main-bg1';
+  }
+
+})
+
+
 watch(() => activeTab.value, (newVal) => {
   if (newVal === 0)
     router.push({ name: 'Home' });
@@ -58,7 +76,7 @@ watch(() => activeTab.value, (newVal) => {
 </script>
 
 <template>
-  <main class="main-content main-bg main-bg1">
+  <main class="main-content main-bg" :class="backgroundType">
     <Header />
     <div class="inner fill pt-0">
       <Tab v-model="activeTab" :tabs="mainTabData" />
@@ -73,6 +91,7 @@ watch(() => activeTab.value, (newVal) => {
           :detailPageNames="['VideoDetail', 'VideoDetail', 'Play']"
         />
         <TabCustomList v-else-if="activeTabId === 4" />
+        <TabInherit v-else-if="activeTabId === 5" />
         <TabCommonList v-else-if="activeTabId === 6" :modelId="16" />
         <TabCommonList 
           v-else-if="activeTabId === 7" 

+ 2 - 90
src/views/Details/ArticleDetail.vue

@@ -1,95 +1,7 @@
 <script setup lang="ts">
-import { ref } from 'vue';
-import { useRoute, useRouter } from 'vue-router';
-import { useSimpleDataLoader } from '@/composeable/SimpleDataLoader';
-import { Vue3Marquee } from 'vue3-marquee';
-import CommonContent from '@/api/CommonContent';
-import SimplePageContentLoader from '@/components/SimplePageContentLoader.vue';
-import SimpleRichHtml from '@/components/SimpleRichHtml.vue';
-import Header from '@/components/parts/Header.vue';
-import ImageLine from '@/components/small/ImageLine.vue';
-import ImagePreview from '@/components/small/ImagePreview.vue';
-
-const route = useRoute();
-const router = useRouter();
-const loader = useSimpleDataLoader(async () => {
-  return (await CommonContent.getContentDetail(Number(route.query.id)));
-});
-
-const showPreview = ref(false);
-const activeItem = ref(0);
-
+import CommonDetail from './CommonDetail.vue';
 </script>
 
 <template>
-  <main class="main-content main-bg main-bg1">
-    <Header show-back absolute />
-    <SimplePageContentLoader :loader="loader">
-      <div class="content absolute">
-        <div class="left" :style="{
-          backgroundImage: `url(${loader.content.value?.image})`,
-        }">
-        </div>
-        <div class="right">
-          <h1>{{ loader.content.value?.title }}</h1>
-          <Vue3Marquee style="width:100%;" :duration="70" vertical>
-            <SimpleRichHtml :contents="[ loader.content.value?.content as string ]" noScroll />
-          </Vue3Marquee>
-          <ImageLine 
-            :data="loader.content.value?.images"
-            :max-count="5" 
-            image-height="20vh"
-            @itemClick="(v, i) => { showPreview = true; activeItem = i }"
-            @moreClick="showPreview = true; activeItem=5"
-          />
-        </div>
-      </div>
-    </SimplePageContentLoader>
-    <ImagePreview 
-      v-model:active-item="activeItem"
-      v-model:show="showPreview"
-      :list="loader.content.value?.images"
-    />
-  </main>
+  <CommonDetail />
 </template>
-
-<style lang="scss" scoped>
-
-.left {
-  position: absolute;
-  top: 0;
-  left: 0;
-  width: 50%;
-  height: 100%;
-  background-size: cover;
-  background-position: center;
-}
-.right {
-  position: absolute;
-  top: 0;
-  right: 0;
-  width: 70%;
-  height: 100%;
-  z-index: 1;
-  background: linear-gradient(90deg, rgba(240, 235, 222, 0.1) 0%, #f0ebde 15%, #f0ebde 100%);
-  font-size: 0.8rem;
-  color: var(--color-text);
-  padding: 8%;
-  padding-left: 20%;
-  display: flex;
-  flex-direction: column;
-
-  h1 {
-    font-size: 1rem;
-    color: var(--color-text-primary);
-  }
-
-  :deep(.image-line) { 
-    margin-top: 4%;
-  }
-}
-
-@media (max-height: 600px) {
-}
-
-</style>

+ 47 - 0
src/views/Details/ArtifactDetail.vue

@@ -0,0 +1,47 @@
+<script setup lang="ts">
+import CommonDetail from './CommonDetail.vue';
+</script>
+
+<template>
+  <CommonDetail>
+    <template #afterTitle="{ content }">
+      <div class="desc">
+        <div>地址: {{ content?.address }}</div>
+        <div>年代: {{ content?.age }}</div>
+        <div>级别: {{ content?.levelText }}</div>
+        <div>所属区域: {{ content?.regionText }}</div>
+        <div>文物类型: {{ content?.crTypeText }}</div>
+      </div>
+    </template>
+    <template #beforeContent="{ content }">
+      <h3>文物介绍:</h3>
+    </template>
+    <template #afterContent="{ content }">
+      <h3>文物价值:</h3>
+      <SimpleRichHtml :contents="[ content?.value as string || '暂无' ]" noScroll />
+    </template>
+  </CommonDetail>
+</template>
+
+<style lang="scss" scoped>
+h3 {
+  font-size: 0.8rem;
+  color: var(--color-text-primary);
+  text-align: left;
+  width: 100%;
+  margin-top: 0.5rem;
+}
+.desc {
+  display: flex;
+  flex-direction: row;
+  flex-wrap: wrap;
+  justify-content: space-between;
+
+  div {
+    width: 48%;
+    color: var(--color-text-primary);
+    font-size: 0.8rem;
+  }
+
+}
+</style>

+ 96 - 0
src/views/Details/CommonDetail.vue

@@ -0,0 +1,96 @@
+<script setup lang="ts">
+import { ref } from 'vue';
+import { useRoute } from 'vue-router';
+import { useSimpleDataLoader } from '@/composeable/SimpleDataLoader';
+import { Vue3Marquee } from 'vue3-marquee';
+import CommonContent from '@/api/CommonContent';
+import SimplePageContentLoader from '@/components/SimplePageContentLoader.vue';
+import SimpleRichHtml from '@/components/SimpleRichHtml.vue';
+import Header from '@/components/parts/Header.vue';
+import ImageLine from '@/components/small/ImageLine.vue';
+import ImagePreview from '@/components/small/ImagePreview.vue';
+
+const route = useRoute();
+const loader = useSimpleDataLoader(async () => {
+  return (await CommonContent.getContentDetail(Number(route.query.id)));
+});
+
+const showPreview = ref(false);
+const activeItem = ref(0);
+
+</script>
+
+<template>
+  <main class="main-content main-bg main-bg1">
+    <Header show-back absolute />
+    <SimplePageContentLoader :loader="loader">
+      <div class="content absolute">
+        <div class="left" :style="{ backgroundImage: `url(${loader.content.value?.image})` }" />
+        <div class="right">
+          <h1>{{ loader.content.value?.title }}</h1>
+          <slot name="afterTitle" :content="loader.content.value" />
+          <Vue3Marquee style="width:100%;" :duration="70" vertical>      
+            <div class="d-flex flex-col">
+              <slot name="beforeContent" :content="loader.content.value" />
+              <SimpleRichHtml :contents="[ loader.content.value?.content as string || '暂无' ]" noScroll />
+              <slot name="afterContent" :content="loader.content.value" />
+            </div>
+          </Vue3Marquee>
+          <ImageLine 
+            :data="loader.content.value?.images"
+            :max-count="5" 
+            image-height="20vh"
+            @itemClick="(v, i) => { showPreview = true; activeItem = i }"
+            @moreClick="showPreview = true; activeItem=5"
+          />
+        </div>
+      </div>
+    </SimplePageContentLoader>
+    <ImagePreview 
+      v-model:active-item="activeItem"
+      v-model:show="showPreview"
+      :list="loader.content.value?.images"
+    />
+  </main>
+</template>
+
+<style lang="scss" scoped>
+
+.left {
+  position: absolute;
+  top: 0;
+  left: 0;
+  width: 50%;
+  height: 100%;
+  background-size: cover;
+  background-position: center;
+}
+.right {
+  position: absolute;
+  top: 0;
+  right: 0;
+  width: 70%;
+  height: 100%;
+  z-index: 1;
+  background: linear-gradient(90deg, rgba(240, 235, 222, 0.1) 0%, #f0ebde 15%, #f0ebde 100%);
+  font-size: 0.8rem;
+  color: var(--color-text);
+  padding: 8%;
+  padding-left: 20%;
+  display: flex;
+  flex-direction: column;
+
+  h1 {
+    font-size: 1rem;
+    color: var(--color-text-primary);
+  }
+
+  :deep(.image-line) { 
+    margin-top: 4%;
+  }
+}
+
+@media (max-height: 600px) {
+}
+
+</style>

+ 68 - 0
src/views/Intangible/List.vue

@@ -0,0 +1,68 @@
+<template>
+  <main class="main-content main-bg main-bg1">
+    <Header show-back />
+    <div class="inner fill pt-0">
+      <SimplePageListContentLoader class="list d-flex flex-col mt-3 flex-fill" :loader="loader">
+        <GridList 
+          class="flex-fill"
+          :list="loader.list.value"
+          :defaultImage="AppCofig.defaultImage" 
+          :playAudio="detailPageName === 'Play'"
+          @itemClick="handleClick"
+        />
+        <SimplePageListContentPager :loader="loader" />
+      </SimplePageListContentLoader>
+    </div>
+  </main>
+</template>
+
+<script setup lang="ts">
+import { useSimplePagerDataLoader } from '@/composeable/SimplePagerDataLoader';
+import { useRoute, useRouter } from 'vue-router';
+import { computed, ref, watch, type PropType } from 'vue';
+import CommonContent, { GetContentListItem, GetContentListParams } from '@/api/CommonContent';
+import SimplePageListContentLoader from '@/components/SimplePageListContentLoader.vue';
+import SimplePageListContentPager from '@/components/SimplePageListContentPager.vue';
+import GridList from '@/components/small/GridList.vue';
+import Header from '@/components/parts/Header.vue';
+import AppCofig from '@/common/config/AppCofig';
+
+const emit = defineEmits(['itemClick']);
+const route = useRoute();
+const router = useRouter();
+const loader = useSimplePagerDataLoader(16, async (page, size) => {
+  let modelId = parseInt(route.query.modelId as string)
+  let mainBodyColumnId = parseInt(route.query.mainBodyColumnId as string) as any;
+  if (isNaN(mainBodyColumnId))
+    mainBodyColumnId = undefined;
+  return CommonContent.getContentList(new GetContentListParams()
+    .setModelId(modelId)
+    .setMainBodyColumnId(mainBodyColumnId as number)
+  , page, size)
+});
+const detailPageName = computed(() => {
+  let detailPageNames = route.query.detailPageNames as string;
+  if (!detailPageNames)
+    detailPageNames = 'ArticleDetail';
+  return detailPageNames;
+})
+
+function handleClick(item: GetContentListItem) {
+  router.push({
+    name: detailPageName.value,
+    query: {
+      id: item.id,
+    }
+  })
+}
+</script>
+
+<style lang="scss" scoped>
+.list {
+  position: absolute;
+  top: 2rem;
+  left: 10%;
+  right: 10%;
+  bottom: 2rem;
+}
+</style>