jinx 2 ay önce
ebeveyn
işleme
5d811c07b0

+ 30 - 0
.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?
+/public/static/tiles
+*.tsbuildinfo

+ 20 - 0
index.html

@@ -0,0 +1,20 @@
+<!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, maximum-scale=1, minimum-scale=1, user-scalable=no" />
+    <title>四维看房</title>
+  </head>
+  <body>
+    <div id="app"></div>
+    <script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyD-nVI43AYI9qp4rsQMJgN6abyPdn3QRY0&libraries=maps,marker&v=beta" defer></script>
+    <script type="module" src="/src/main.js"></script>
+
+    <script>
+      if (window.location.href.indexOf("vlog") > -1) {
+        var vConsole = new VConsole();
+      }
+    </script>
+  </body>
+</html>

+ 38 - 0
index1.html

@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>Add a Map with Markers using HTML</title>
+
+    <link rel="stylesheet" type="text/css" href="./style.css" />
+    <script type="module" src="./index.js"></script>
+  </head>
+  <body>
+    <gmp-map
+      center="43.4142989,-124.2301242"
+      zoom="4"
+      map-id="DEMO_MAP_ID"
+      style="height: 400px"
+    >
+      <gmp-advanced-marker
+        position="37.4220656,-122.0840897"
+        title="Mountain View, CA"
+      ></gmp-advanced-marker>
+      <gmp-advanced-marker
+        position="47.648994,-122.3503845"
+        title="Seattle, WA"
+      ></gmp-advanced-marker>
+    </gmp-map>
+
+    <!-- 
+      The `defer` attribute causes the script to execute after the full HTML
+      document has been parsed. For non-blocking uses, avoiding race conditions,
+      and consistent behavior across browsers, consider loading using Promises. See
+      https://developers.google.com/maps/documentation/javascript/load-maps-js-api
+      for more information.
+      -->
+    <script
+      src="https://maps.googleapis.com/maps/api/js?key=AIzaSyD-nVI43AYI9qp4rsQMJgN6abyPdn3QRY0&libraries=maps,marker&v=beta"
+      defer
+    ></script>
+  </body>
+</html>

+ 8 - 0
jsconfig.json

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

+ 29 - 0
package.json

@@ -0,0 +1,29 @@
+{
+  "name": "yueyang-map-h5",
+  "version": "0.0.0",
+  "private": true,
+  "type": "module",
+  "scripts": {
+    "dev": "vite",
+    "build": "vite build",
+    "preview": "vite preview"
+  },
+  "dependencies": {
+    "animate.css": "^4.1.1",
+    "axios": "^1.9.0",
+    "naive-ui": "^2.41.1",
+    "pinia": "^3.0.1",
+    "swiper": "^5.4.5",
+    "vue": "^3.5.13",
+    "vue-router": "^4.5.0",
+    "xlsx": "^0.18.5"
+  },
+  "devDependencies": {
+    "@vitejs/plugin-vue": "^5.2.3",
+    "autoprefixer": "^10.4.21",
+    "postcss-px-to-viewport": "^1.1.1",
+    "sass": "^1.86.3",
+    "vite": "^6.2.4",
+    "vite-plugin-vue-devtools": "^7.7.2"
+  }
+}

Dosya farkı çok büyük olduğundan ihmal edildi
+ 2597 - 0
pnpm-lock.yaml


+ 22 - 0
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, // 横屏时使用的视口宽度
+    // },
+  },
+};

+ 11 - 0
src/App.vue

@@ -0,0 +1,11 @@
+<template>
+  <RouterView />
+</template>
+<script setup>
+import { RouterView } from "vue-router";
+import { nextTick, onMounted, ref, watch, computed } from "vue";
+import { useRoute } from "vue-router";
+import router from "@/router";
+</script>
+
+<style scoped></style>

BIN
src/assets/images/baicon.d0289dc.png


BIN
src/assets/images/logo.png


BIN
src/assets/images/search.png


+ 153 - 0
src/assets/main.css

@@ -0,0 +1,153 @@
+: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;
+
+}
+html,
+body,
+#app {
+  width: 100%;
+  height: 100%;
+}
+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;
+  }
+}
+::-webkit-scrollbar {
+  width: 4px;
+  height: 1px;
+}
+
+::-webkit-scrollbar-thumb {
+  border-radius: 4px;
+  box-shadow: inset 0 0 5px rgba(106, 73, 52, 1);
+  background: #ccc;
+}
+
+::-webkit-scrollbar-thumb:hover {
+  background: #999;
+}
+
+::-webkit-scrollbar-track {
+  box-shadow: inset 0 0 5px transparent;
+  border-radius: 4px;
+  /* background: #000000; */
+}
+input {
+  background: none;
+}
+.amap-logo,
+.amap-copyright {
+  display: none !important;
+}
+.amap-info-close {
+  display: none;
+}
+.amap-info-content {
+  padding: 0;
+  border-radius: 0.16rem;
+}

+ 35 - 0
src/components/copyRight/index.vue

@@ -0,0 +1,35 @@
+<!--  -->
+<template>
+  <div class="copy-right-info">
+    <div class="copy">Copyright © 2022 4DAGE Co., Ltd. All rights reserved.</div>
+    <!-- <div>
+      <a target="_blank" href="https://beian.mps.gov.cn/#/query/webSearch">
+        <div class="icon"></div>
+        <span> 粤公网安备 44049102496647号 </span>
+      </a>
+      <a target="_blank" href="https://beian.miit.gov.cn/#/Integrated/index">
+        <div class="icon"></div>
+        <span>粤ICP备14078495号</span>
+      </a>
+    </div> -->
+  </div>
+</template>
+
+<script setup>
+import { reactive, ref, toRefs, onBeforeMount, onMounted } from "vue";
+</script>
+<style lang="scss" scoped>
+.copy-right-info {
+  padding-top: 1rem;
+  padding-bottom: 1rem;
+  width: 1174px;
+  // min-height: 1200px;
+  margin: 30px auto 0;
+  border-top: 1px solid #393939;
+  .copy {
+    color: #888c8e;
+    font-size: 12px;
+    line-height: 20px;
+  }
+}
+</style>

+ 170 - 0
src/components/header/index.vue

@@ -0,0 +1,170 @@
+<!--  -->
+<template>
+  <div class="header">
+    <div class="main-nav-container">
+      <div class="main-nav">
+        <div class="nav-left">
+          <div class="nav-logo">
+            <img src="@/assets/images/logo.png" alt="logo" />
+          </div>
+          <template v-if="route.path == '/'">
+            <span class="nav-border"></span>
+            <div class="nav-city">
+              <n-dropdown trigger="click" :options="options" :show-arrow="true" @select="handleSelect">
+                <div class="s-city">珠海</div>
+              </n-dropdown>
+            </div></template
+          >
+        </div>
+        <div class="nav-right" v-if="route.path == '/'">
+          <div class="search-wrapper">
+            <n-input round placeholder="请输入楼盘名"> </n-input>
+          </div>
+          <n-button tertiary round>搜索</n-button>
+        </div>
+      </div>
+      <div v-if="route.path == '/'" class="main-tab">
+        <div class="tab-item active">全部楼盘</div>
+        <div class="tab-item">近期楼盘</div>
+        <div class="tab-item">优惠楼盘</div>
+      </div>
+      <!-- <div class="main-tab"><span>珠海</span>><span>详情</span></div> -->
+
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { reactive, ref, toRefs, onBeforeMount, onMounted, nextTick, watch, defineEmits, defineProps } from "vue";
+import { useRoute } from "vue-router";
+const emits = defineEmits(["close"]);
+const route = useRoute();
+console.error(route)
+const back = () => {
+  if (props.isClose) {
+    emits("close");
+    return;
+  }
+  if (props.path) {
+    router.replace(`/${props.path}`);
+    return;
+  }
+  router.replace("/");
+};
+const props = defineProps({
+  title: {
+    type: String,
+    default: "",
+  },
+  path: {
+    type: String,
+    default: "",
+  },
+  isClose: {
+    type: Boolean,
+    default: false,
+  },
+  // isBack: {
+  //   type: Boolean,
+  //   default: false,
+  // },
+});
+const handleSelect = (key) => {
+  console.log(key);
+};
+const options = ref([
+  {
+    label: "珠海",
+    key: "marina bay sands",
+    disabled: true,
+  },
+  {
+    label: "中山",
+    key: "brown's hotel, london",
+  },
+]);
+onMounted(() => {});
+</script>
+<style lang="scss" scoped>
+.header {
+  .main-nav-container {
+    margin-bottom: 20px;
+    background: #f5f5f6;
+    box-shadow: 0 1px 0 0 #eee;
+    border-top: 1px solid #eee;
+    padding-bottom: 4px;
+    .main-nav {
+      width: 1150px;
+      margin: 0 auto;
+      padding-bottom: 9px;
+      display: flex;
+      margin-top: 22px;
+      .nav-left {
+        display: flex;
+        align-items: center;
+        margin-right: 50px;
+        .nav-border {
+          width: 1px;
+          height: 28px;
+          margin: 0 16px;
+          background: #ccc;
+          display: inline-block;
+          vertical-align: middle;
+        }
+        .nav-city {
+          .s-city {
+            height: 26px;
+            padding: 0 8px;
+            line-height: 26px;
+            border: 1px solid #dfdfdf;
+            border-radius: 2px;
+            text-decoration: none;
+            font-size: 14px;
+            color: #666;
+            cursor: pointer;
+            &:after {
+              content: " ";
+              display: inline-block;
+              width: 0;
+              height: 0;
+              margin: 2px 0 0 8px;
+              border: 4px solid transparent;
+              border-top-color: #999;
+              vertical-align: middle;
+            }
+          }
+        }
+      }
+      .nav-right {
+        display: flex;
+        align-items: center;
+        .search-wrapper {
+          position: relative;
+          width: 450px;
+          height: 46px;
+          line-height: 46px;
+          margin-right: 10px;
+        }
+      }
+    }
+    .main-tab {
+      width: 1150px;
+      margin: 0 auto;
+      padding-bottom: 9px;
+      display: flex;
+      .tab-item {
+        margin-right: 35px;
+        padding: 16px 0 10px;
+        font-size: 16px;
+        color: #101d37;
+        font-weight: 700;
+        cursor: pointer;
+        &.active {
+          color: #3072f6;
+          border-bottom: 2px solid #3072f6;
+        }
+      }
+    }
+  }
+}
+</style>

+ 95 - 0
src/components/toast/index.vue

@@ -0,0 +1,95 @@
+<template>
+  <transition name="fade">
+    <div v-if="visible" class="my-toast" :class="type">
+      <div class="toast-content">
+        <iconpark-icon v-if="type !== 'default'" :name="iconName" size="16px" :class="['toast-icon', type]" />
+        <span>{{ message }}</span>
+      </div>
+    </div>
+  </transition>
+</template>
+<script setup>
+import { nextTick, onMounted, ref, watch, computed, onBeforeUnmount, defineEmits } from "vue";
+// 组件逻辑实现
+const props = defineProps({
+  message: String,
+  type: String,
+  duration: {
+    type: Number,
+    default: 2000,
+  },
+});
+const emits = defineEmits(["closeToast"]);
+const visible = ref(false);
+let timer = null;
+// 显示方法
+const show = () => {
+  if (timer) {
+    clearTimeout(timer);
+    timer = null;
+  }
+  visible.value = true;
+  if (props.duration > 0) {
+    timer = setTimeout(() => {
+      visible.value = false;
+      timer = null;
+      emits("closeToast");
+    }, props.duration);
+  }
+};
+onMounted(() => {
+  show();
+});
+// 资源清理
+onBeforeUnmount(() => {
+  if (timer) {
+    clearTimeout(timer);
+    timer = null;
+  }
+});
+</script>
+<style lang="scss" scoped>
+.my-toast {
+  position: fixed;
+  top: 50%;
+  left: 50%;
+  transform: translate(-50%, -50%);
+  z-index: 100001;
+  padding: 8px;
+  border-radius: 4px;
+  background: rgba(0, 0, 0, 0.7);
+  color: #fff;
+  font-size: 14px;
+  line-height: 20px;
+  max-width: 80%;
+
+  .toast-content {
+    display: flex;
+    align-items: center;
+    gap: 4px;
+  }
+
+  .success {
+    color: #52c41a;
+  }
+
+  .error {
+    color: #ff4d4f;
+  }
+
+  .warning {
+    color: #faad14;
+  }
+}
+
+// 动画效果
+.fade-enter-active,
+.fade-leave-active {
+  transition: opacity 0.3s ease;
+}
+
+.fade-enter-from,
+.fade-leave-to {
+  opacity: 0;
+}
+</style>

+ 16 - 0
src/main.js

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

+ 30 - 0
src/router/index.js

@@ -0,0 +1,30 @@
+import { createRouter, createWebHistory, createWebHashHistory } from "vue-router";
+
+const router = createRouter({
+  // history: createWebHistory(import.meta.env.BASE_URL),
+  history: createWebHashHistory(),
+  routes: [
+    {
+      path: "/",
+      name: "home",
+      component: () => import("../views/home/index.vue"),
+    },
+    {
+      path: "/details",
+      name: "details",
+      component: () => import("../views/details/index.vue"),
+    },
+
+    {
+      path: "/data",
+      name: "data",
+      component: () => import("../views/data/index.vue"),
+    },
+  ],
+});
+router.beforeEach(async (to, from, next) => {
+  next();
+});
+
+router.afterEach((to, from, failure) => {});
+export default router;

+ 20 - 0
src/stores/audio.js

@@ -0,0 +1,20 @@
+// stores/counter.js
+import { nextTick, onMounted, ref } from "vue";
+import { defineStore } from "pinia";
+
+export const useAudioStore = defineStore("audio", {
+  state: () => {
+    return { audioStatus: false };
+  },
+  // 也可以定义为
+  // state: () => ({ count: 0 })
+  actions: {
+    setState(payload) {
+      if (payload) {
+        this.audioStatus = payload.status;
+        return;
+      }
+      this.audioStatus = !this.audioStatus;
+    },
+  },
+});

+ 16 - 0
src/stores/line.js

@@ -0,0 +1,16 @@
+// stores/counter.js
+import { nextTick, onMounted, ref } from "vue";
+import { defineStore } from "pinia";
+
+export const useLineStore = defineStore("line", {
+  state: () => {
+    return { lineTime: null };
+  },
+  // 也可以定义为
+  // state: () => ({ count: 0 })
+  actions: {
+    initLineTime(payload) {
+      this.lineTime = payload;
+    },
+  },
+});

+ 164 - 0
src/views/data/index.vue

@@ -0,0 +1,164 @@
+vue3
+<!--  -->
+<template>
+  <p>点位</p>
+  <input type="file" @change="readFile" accept=".xlsx" />
+ 
+</template>
+
+<script setup>
+import { reactive, ref, toRefs, onBeforeMount, onMounted, nextTick, watch } from "vue";
+import * as XLSX from "xlsx";
+const readFile = (e) => {
+  const files = e.target.files;
+  // 如果没有文件名
+  if (files.length <= 0) {
+    return false;
+  } else if (!/\.(xls|xlsx)$/.test(files[0].name.toLowerCase())) {
+    this.$Message.error("上传格式不正确,请上传xls或者xlsx格式");
+    return false;
+  }
+
+  const fileReader = new FileReader();
+  fileReader.onload = (ev) => {
+    try {
+      const data = ev.target.result;
+      // 切换为新的调用方式
+      const workbook = XLSX.read(data, {
+        type: "binary",
+      });
+
+      let table = workbook.Sheets;
+      let keys = workbook.SheetNames;
+      console.log(workbook);
+      let array = [];
+
+      keys.forEach((item) => {
+        let obj = {};
+        obj.name = item;
+        // obj.list = XLSX.utils.sheet_to_json(table[item]);
+        obj.list = procressData(
+          XLSX.utils.sheet_to_json(table[item], {
+            /** Default value for null/undefined values */
+            defval: "", //给defval赋值为空的字符串
+          })
+        );
+        array.push(obj);
+      });
+    } catch (e) {
+      return false;
+    }
+  };
+  fileReader.readAsBinaryString(files[0]);
+};
+// 表格时间转换
+function excelToJsDate(serialNumber) {
+  var date = new Date(1900, 0, serialNumber - 1);
+
+  const year = date.getFullYear();
+  const month = date.getMonth() + 1;
+  const day = date.getDate();
+
+  return year + "年" + month + "月" + day + "日";
+}
+const filterName = (name) => {
+  if (!name) return "";
+  console.log(name.split("."));
+  return name.split(".")[1] || name.split(".")[0];
+};
+const procressData = (list) => {
+  let array = [];
+  list = list.filter((item) => item["提供日期"]);
+
+  console.error(list);
+  list.forEach((item, index) => {
+    let obj = {};
+    obj.id = index + 1;
+    for (let key in item) {
+      switch (key) {
+        case "地图上分类":
+          obj["title"] = item[key];
+          if (item[key] === "历史文化") {
+            obj["type"] = 1;
+          }
+          if (item[key] === "自然探索") {
+            obj["type"] = 2;
+          }
+          if (item[key] === "亲子休闲") {
+            obj["type"] = 3;
+          }
+          if (item[key] === "城市烟火") {
+            obj["type"] = 4;
+          }
+          if (item[key] === "交通住宿") {
+            obj["type"] = 5;
+          }
+          if (item[key] === "政府驻地") {
+            obj["type"] = 6;
+          }
+
+          break;
+        case "景点名称":
+          obj["name"] = item[key];
+
+          break;
+        case "坐标(以游客中心坐标为准)":
+          obj["location"] = item[key];
+
+          break;
+        case "大场景链接":
+          if (item[key]) {
+            obj["vrLink"] = item[key];
+          }
+
+          break;
+        case "三维模型":
+          if (item[key]) {
+            obj["modelLink"] = item[key];
+          }
+
+          break;
+        case "普通图片":
+          obj["images"] = item[key].replaceAll(",", ",").split(",");
+
+          break;
+        case "景区等级":
+          if (item[key]) {
+            obj["rate"] = item[key];
+          }
+
+          break;
+        case "简介":
+          obj["desc"] = item[key];
+
+          break;
+        case "音频名称":
+          if (item[key]) {
+            obj["audio"] = item[key] + ".MP3";
+          }
+
+          break;
+        case "县市区":
+          obj["address"] = item[key];
+          // case "图标大小":
+          //   if (item[key] && item[key] != "需补充") {
+          //     obj["iconSize"] = item[key].split("*");
+          //   } else {
+          //     obj["iconSize"] = null;
+          //   }
+
+          break;
+      }
+    }
+    // obj.vrLink = "https://www.4dkankan.com/panorama/show.html?id=WK1768236336482967552&vr=fd720_NZ0i3ASIq&lang=zh"
+    array.push(obj);
+  });
+
+  console.error(array);
+  return array;
+};
+
+onMounted(() => {});
+</script>
+<style lang="scss" scoped></style>
+<style></style>

+ 547 - 0
src/views/details/index.vue

@@ -0,0 +1,547 @@
+<!--  -->
+<template>
+  <NConfigProvider :theme-overrides="themeOverrides">
+    <navheader></navheader>
+    <div class="details">
+      <div class="content">
+        <div class="details-title">
+          <div class="name-box">
+            <div class="name">
+              <span>招商云璟揽阅</span>
+              <div class="label-wrap">
+                <div class="label zs">在售</div>
+                <div class="label zz">住宅</div>
+              </div>
+            </div>
+            <div class="other-name">别名:璟云雅苑</div>
+          </div>
+          <div class="tel-num">咨询电话: 4008315585转04817</div>
+        </div>
+        <div class="details-top">
+          <div class="banner-box">
+            <div class="swiper-wraper">
+              <div class="swiper-wrapper">
+                <div class="swiper-slide" v-for="i in 4"><img src="" alt="" /></div>
+
+                <!-- 更多幻灯片 -->
+              </div>
+              <!-- 如果需要分页器 -->
+              <!-- <div class="swiper-pagination"></div> -->
+              <div class="swiper-button-prev"></div>
+              <div class="swiper-button-next"></div>
+            </div>
+            <div class="image-box">
+              <div class="item-image"><img src="" alt="" /><span class="name">VR(4)</span></div>
+              <div class="item-image"><img src="" alt="" /><span class="name">效果图(4)</span></div>
+              <div class="item-image"><img src="" alt="" /><span class="name">样板间(4)</span></div>
+              <div class="item-image"><img src="" alt="" /><span class="name">区位(4)</span></div>
+              <div class="item-image"><img src="" alt="" /><span class="name">小区配套(4)</span></div>
+            </div>
+          </div>
+          <div class="info-box">
+            <div class="top-info">
+              <span class="title">参考均价</span>
+              <span class="price-number">60000</span>
+              <span class="price-unit">元/平(单价) </span>
+              <span class="price-number">480-765</span>
+              <span class="price-unit">(万/套)(总价)</span>
+            </div>
+            <div class="info-tag">
+              <div class="tag-item">2025北京房展</div>
+              <div class="tag-item">近地铁</div>
+              <div class="tag-item">公交直达</div>
+              <div class="tag-item">综合商场</div>
+            </div>
+            <div class="info-desc">
+              <div class="desc-item">
+                <div class="title">项目地址</div>
+                <div class="text">北京市通州区云景南大街辅路</div>
+              </div>
+              <div class="desc-item">
+                <div class="title">最新开盘</div>
+                <div class="text">2025-03-02</div>
+              </div>
+              <div class="desc-item">
+                <div class="title">楼盘户型</div>
+                <div class="text">
+                  <span>三居室({{ 1 }})</span>
+                  <span>四居室({{ 2 }})</span>
+                </div>
+              </div>
+            </div>
+          </div>
+        </div>
+        <div class="details-bottom">
+          <div class="cad-box">
+            <h2 class="title">户型介绍</h2>
+            <div class="cad-tab">
+              <div class="tab-item active"><span>三居室(3)</span></div>
+              <div class="tab-item"><span>四居室(1)</span></div>
+            </div>
+            <div class="cad-list">
+              <div class="cad-item" v-for="i in 3">
+                <div class="cad-img"><img src="" alt="" /></div>
+                <div class="cad-info">
+                  <div class="cad-title">
+                    <span> 3室2厅2卫 </span>
+                    <div class="label-wrap">
+                      <div class="label zs">在售</div>
+                      <div class="label zz">住宅</div>
+                    </div>
+                  </div>
+                  <div class="area">建面 108m² (南,北朝向)</div>
+                  <div class="price">
+                    约<span>645</span>
+                    万/套
+                  </div>
+                </div>
+              </div>
+            </div>
+          </div>
+          <div class="bottom-info-box">
+            <div class="name-info">
+              <h3>招商云璟揽阅</h3>
+              <div class="label-wrap">
+                <div class="label zs">在售</div>
+                <div class="label zz">住宅</div>
+              </div>
+            </div>
+            <div class="top-info">
+              <span class="title">参考均价</span>
+              <span class="price-number">60000</span>
+              <span class="price-unit">元/平(单价) </span>
+            </div>
+
+            <div class="info-desc">
+              <div class="desc-item">
+                <div class="title">项目地址</div>
+                <div class="text">北京市通州区云景南大街辅路</div>
+              </div>
+              <div class="desc-item">
+                <div class="title">最新开盘</div>
+                <div class="text">2025-03-02</div>
+              </div>
+              <div class="desc-item">
+                <div class="title">楼盘户型</div>
+                <div class="text">
+                  <span>三居室({{ 1 }})</span>
+                  <span>四居室({{ 2 }})</span>
+                </div>
+              </div>
+            </div>
+          </div>
+        </div>
+        <div class="google-map">
+          <gmp-map center="43.4142989,-124.2301242" zoom="4" map-id="DEMO_MAP_ID" style="height: 400px">
+            <gmp-advanced-marker position="37.4220656,-122.0840897" title="Mountain View, CA"></gmp-advanced-marker>
+            <gmp-advanced-marker position="47.648994,-122.3503845" title="Seattle, WA"></gmp-advanced-marker>
+          </gmp-map>
+        </div>
+      </div>
+    </div>
+    <copyRight
+  /></NConfigProvider>
+</template>
+
+<script setup>
+import router from "@/router";
+import navheader from "@/components/header/index.vue";
+import copyRight from "@/components/copyRight/index.vue";
+import { reactive, ref, toRefs, onBeforeMount, onMounted, computed } from "vue";
+import Swiper from "swiper";
+import "swiper/css/swiper.css";
+import { NConfigProvider } from "naive-ui";
+
+const themeOverrides = {
+  common: {
+    primaryColor: "#3072f6",
+    primaryColorHover: "#3072f6",
+  },
+
+  // ...
+};
+let swiper = null;
+const initTab = () => {
+  swiper = new Swiper(".swiper-wraper", {
+    navigation: {
+      nextEl: ".swiper-button-next",
+      prevEl: ".swiper-button-prev",
+      // disabledClass: "my-button-disabled",
+    },
+    // Swiper选项...
+    loop: false,
+    on: {
+      slideChange: (e) => {},
+      init: () => {},
+    },
+  });
+};
+onMounted(() => {
+  initTab();
+});
+</script>
+<style lang="scss" scoped>
+.label-wrap {
+  display: flex;
+  .label {
+    width: auto;
+    height: 24px;
+    line-height: 24px;
+    padding: 0 6px;
+    box-sizing: border-box;
+    display: inline-block;
+    font-size: 14px;
+    border-radius: 2px;
+    &.zs {
+      margin-left: 7px;
+      color: #fff;
+      background-image: linear-gradient(-135deg, #6b99f6, #3072f6);
+    }
+    &.zz {
+      margin-left: 5px;
+      font-size: 12px;
+      color: #849aad;
+      background: #f2f4f6;
+    }
+  }
+}
+.content {
+  width: 1182px;
+  margin: 0 auto;
+
+  .details-title {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    width: 100%;
+    .name-box {
+      display: flex;
+      flex-direction: column;
+      .name {
+        display: flex;
+        align-items: center;
+        justify-content: flex-start;
+        > span {
+          padding-bottom: 0;
+          font-size: 26px;
+          color: #101d37;
+          font-weight: 600;
+          display: inline-block;
+          vertical-align: -4px;
+        }
+      }
+      .other-name {
+        padding-top: 7px;
+        font-size: 13px;
+        color: #9399a5;
+        font-weight: 400;
+        vertical-align: unset;
+        padding-bottom: unset;
+        margin-top: unset;
+      }
+    }
+    .tel-num {
+      position: relative;
+      display: inline-block;
+      color: #101d37;
+      font-weight: bold;
+      height: 22px;
+      overflow: visible;
+      font-size: 16px;
+    }
+  }
+  .details-top {
+    display: flex;
+    align-items: flex-start;
+    justify-content: flex-start;
+    .banner-box {
+      display: inline-block;
+      width: 524px;
+      position: relative;
+      vertical-align: top;
+
+      .swiper-wraper {
+        width: 526px;
+        height: 295px;
+        overflow: hidden;
+        .swiper-wrapper {
+          .swiper-slide {
+            img {
+              width: 100%;
+              height: 100%;
+              object-fit: cover;
+            }
+          }
+        }
+      }
+      .image-box {
+        width: auto;
+        height: 67px;
+        overflow: hidden;
+        margin-top: 10px;
+        display: flex;
+        align-items: center;
+        justify-content: space-between;
+        .item-image {
+          width: 100px;
+          height: 67px;
+          border: none;
+          // margin-right: 6px;
+          display: inline-block;
+          cursor: pointer;
+          background: red;
+          position: relative;
+          .name {
+            position: absolute;
+            bottom: 0;
+            right: 0;
+            left: 0;
+            height: 26px;
+            line-height: 26px;
+            text-align: center;
+            font-size: 12px;
+            color: #fff;
+            letter-spacing: 0;
+            background: rgba(0, 0, 0, 0.5);
+          }
+        }
+      }
+    }
+    .info-box {
+      width: 586px;
+      margin-left: 60px;
+      padding: 20px 30px;
+      box-sizing: border-box;
+      border-radius: 4px;
+      background: #fff;
+      box-shadow: 0 4px 10px 0 rgba(0, 0, 0, 0.06);
+      -webkit-box-shadow: 0 4px 10px 0 rgba(0, 0, 0, 0.06);
+
+      .top-info {
+        color: #fe615a;
+        .title {
+          font-family: PingFangSC-Regular;
+          font-size: 14px;
+          color: #9399a5;
+          display: inline-block;
+          margin-right: 10px;
+        }
+        .price-number {
+          font-family: Tahoma-Bold;
+          font-size: 30px;
+        }
+        .price-unit {
+          font-family: HiraginoSansGB-W6;
+          font-size: 16px;
+          margin-right: 20px;
+        }
+      }
+      .info-tag {
+        margin-top: 20px;
+        border-bottom: 1px solid #e4e6f0;
+        padding-bottom: 15.5px;
+        .tag-item {
+          display: inline-block;
+          height: 30px;
+          margin-right: 10px;
+          padding: 0 12px;
+          line-height: 30px;
+          font-size: 12px;
+          color: #849aae;
+          background: rgba(132, 154, 174, 0.1);
+        }
+      }
+      .info-desc {
+        border-bottom: 1px solid #e4e6f0;
+        padding-bottom: 15.5px;
+        .desc-item {
+          display: flex;
+          align-items: center;
+          justify-content: flex-start;
+          margin-top: 13px;
+          .title {
+            font-family: PingFangSC-Regular;
+            font-size: 14px;
+            color: #9399a5;
+          }
+          .text {
+            font-family: PingFangSC-Regular;
+            font-weight: 300;
+            font-size: 14px;
+            color: #101d37;
+            margin-right: 4px;
+            overflow: hidden;
+            text-overflow: ellipsis;
+            white-space: nowrap;
+            vertical-align: bottom;
+            margin-left: 10px;
+            > span {
+              margin-right: 10px;
+            }
+          }
+        }
+      }
+    }
+  }
+
+  .details-bottom {
+    display: flex;
+    align-items: flex-start;
+    justify-content: space-between;
+    margin-top: 50px;
+    .cad-box {
+      .title {
+        font-size: 24px;
+        font-weight: 600;
+        color: #101d37;
+      }
+      .cad-tab {
+        display: flex;
+        align-items: center;
+        justify-content: flex-start;
+        .tab-item {
+          padding: 10px 20px;
+          float: left;
+
+          span {
+            color: #101d37;
+            cursor: pointer;
+            position: relative;
+          }
+
+          &.active {
+            span {
+              color: #3072f6;
+              &::after {
+                content: " ";
+                background: #3072f6;
+                bottom: -11px;
+                height: 1px;
+                left: 0;
+                position: absolute;
+                width: 100%;
+              }
+            }
+          }
+        }
+      }
+      .cad-list {
+        margin-right: 0;
+        width: 724px;
+        display: inline-block;
+        overflow: hidden;
+        display: flex;
+
+        .cad-item {
+          position: relative;
+          width: 230px;
+          // height: 338px;
+          display: inline-block;
+          margin-right: 12px;
+          border: 1px solid #e4e6f0;
+          border-radius: 2px;
+          vertical-align: top;
+
+          .cad-img {
+            height: 169px;
+            width: 100%;
+            border-bottom: 1px solid #e4e6f0;
+            position: relative;
+            img {
+              width: 100%;
+              height: 100%;
+              object-fit: contain;
+            }
+          }
+          .cad-info {
+            padding: 20px 10px 20px;
+            .cad-title {
+              display: flex;
+              align-items: center;
+              span {
+                font-weight: bold;
+                color: #333;
+                font-size: 14px;
+              }
+            }
+            .area {
+              color: #9399a5;
+              font-size: 14px;
+              margin-bottom: 10px;
+            }
+            .price {
+              margin-bottom: 5px;
+              font-size: 16px;
+              color: #fa5741;
+              > span {
+              }
+            }
+          }
+        }
+      }
+    }
+    .bottom-info-box {
+      width: 400px;
+      position: relative;
+      padding: 20px 30px;
+      box-sizing: border-box;
+      border-radius: 4px;
+      background: #fff;
+      box-shadow: 0 4px 10px 0 rgba(0, 0, 0, 0.06);
+      .name-info {
+        display: flex;
+        font-size: 26px;
+        align-items: center;
+      }
+      .top-info {
+        color: #fe615a;
+        .title {
+          font-family: PingFangSC-Regular;
+          font-size: 14px;
+          color: #9399a5;
+          display: inline-block;
+          margin-right: 10px;
+        }
+        .price-number {
+          font-family: Tahoma-Bold;
+          font-size: 30px;
+        }
+        .price-unit {
+          font-family: HiraginoSansGB-W6;
+          font-size: 16px;
+          margin-right: 20px;
+        }
+      }
+
+      .info-desc {
+        .desc-item {
+          display: flex;
+          align-items: center;
+          justify-content: flex-start;
+          margin-top: 13px;
+          .title {
+            font-family: PingFangSC-Regular;
+            font-size: 14px;
+            color: #9399a5;
+          }
+          .text {
+            font-family: PingFangSC-Regular;
+            font-weight: 300;
+            font-size: 14px;
+            color: #101d37;
+            margin-right: 4px;
+            overflow: hidden;
+            text-overflow: ellipsis;
+            white-space: nowrap;
+            vertical-align: bottom;
+            margin-left: 10px;
+            > span {
+              margin-right: 10px;
+            }
+          }
+        }
+      }
+    }
+  }
+}
+</style>

+ 293 - 0
src/views/home/index.vue

@@ -0,0 +1,293 @@
+<!--  -->
+<template>
+  <NConfigProvider :theme-overrides="themeOverrides">
+    <navheader></navheader>
+    <div class="home">
+      <div class="list">
+        <div class="list-left">
+          <div class="left-item" v-for="i in 10">
+            <div class="item-info">
+              <div class="item-cover">
+                <img src="https://ke-image.ljcdn.com/newhouse-user-image/a2734ab4bfa1481cf8027a8f54b39d40.jpg.592x432.jpg" alt="" />
+              </div>
+              <div class="item-text">
+                <div class="title">
+                  <span class="name">招商云璟揽阅</span>
+                  <div class="label-box">
+                    <div class="label-item zs">在售</div>
+                    <div class="label-item zz">住宅</div>
+                    <div class="label-item dmzz">低密住宅</div>
+                  </div>
+                </div>
+                <div class="location"><span>地址:</span><span>北京市昌平区回龙观镇史各庄街道生命科学园三期</span></div>
+                <div class="house"><span>户型:</span><span>3室/4室</span> <span>建面: </span><span>120-196㎡</span></div>
+                <div class="role">新房顾问:高治科</div>
+                <div class="tag">
+                  <div class="tag-item">2025北京房展</div>
+                  <div class="tag-item">近地铁</div>
+                  <div class="tag-item">公交直达</div>
+                  <div class="tag-item">综合商场</div>
+                </div>
+              </div>
+            </div>
+            <div class="item-price">
+              <div class="unit-price"><span>60000</span> 元/㎡(均价)</div>
+              <div class="total-price">总价480-765(万/套)</div>
+            </div>
+          </div>
+        </div>
+        <div class="list-right">
+          <p class="title">热门楼盘</p>
+          <div class="hot-list">
+            <div class="hot-item" v-for="i in 5">
+              <div class="hot-cover"><img src="" alt="" /></div>
+              <div class="hot-text">
+                <div class="hot-name">
+                  <span>顺鑫颐和天璟</span>
+                  <div class="hot-label">
+                    <div class="item zs">在售</div>
+                    <div class="item zz">低密住宅</div>
+                  </div>
+                </div>
+
+                <div class="price-wrapper">
+                  <div class="hot-price">900-950</div>
+                  <div class="hot-unit">(万/套)</div>
+                </div>
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+    <copyRight
+  /></NConfigProvider>
+</template>
+
+<script setup>
+import router from "@/router";
+import navheader from "@/components/header/index.vue";
+import copyRight from "@/components/copyRight/index.vue";
+import { reactive, ref, toRefs, onBeforeMount, onMounted, computed } from "vue";
+
+import { NConfigProvider } from "naive-ui";
+
+const themeOverrides = {
+  common: {
+    primaryColor: "#3072f6",
+    primaryColorHover: "#3072f6",
+  },
+
+  // ...
+};
+
+onMounted(() => {});
+</script>
+<style lang="scss" scoped>
+.home {
+  .list {
+    position: relative;
+    width: 1174px;
+    // min-height: 1200px;
+    margin: 20px auto 0;
+    display: flex;
+    justify-content: space-between;
+    align-items: flex-start;
+    .list-left {
+      width: 932px;
+      .left-item {
+        position: relative;
+        padding: 30px 0;
+        border-bottom: 1px solid #f1f1f1;
+        display: flex;
+        align-items: center;
+        justify-content: space-between;
+        cursor: pointer;
+        &:last-of-type {
+          border: none;
+        }
+        .item-info {
+          display: flex;
+          align-items: flex-start;
+          justify-content: flex-start;
+
+          .item-cover {
+            // position: absolute;
+            width: 236px;
+            height: 178px;
+            background: #f5f5f6;
+            // background-image: url(../../common/img/default_icon.png?4d16840…);
+            background-size: 40px 38px;
+            background-repeat: no-repeat;
+            background-position: 50%;
+            img {
+              width: 236px;
+              height: 178px;
+              background: transparent;
+            }
+          }
+          .item-text {
+            margin-left: 40px;
+            height: 178px;
+            display: flex;
+            flex-direction: column;
+            justify-content: space-between;
+            align-items: flex-start;
+            .title {
+              display: flex;
+              align-items: flex-start;
+              justify-content: flex-start;
+
+              .name {
+                display: inline-block;
+                margin-right: 7px;
+                font-size: 22px;
+                color: #101d37;
+                line-height: 22px;
+                vertical-align: middle;
+                font-weight: 700;
+                white-space: nowrap;
+                overflow: hidden;
+                text-overflow: ellipsis;
+              }
+              .label-box {
+                .label-item {
+                  display: inline-block;
+                  padding: 6px 7px;
+                  margin-right: 7px;
+                  line-height: 12px;
+                  font-size: 12px;
+                  vertical-align: middle;
+                  border-radius: 2px;
+                  letter-spacing: -0.27px;
+                  text-align: center;
+                  font-weight: 400;
+                  color: #fff;
+
+                  &.zs {
+                    background: #5f94ff;
+                  }
+                  &.zz {
+                    background: #fb9252;
+                  }
+                  &.dmzz {
+                    background: #8fd24e;
+                  }
+                }
+              }
+            }
+            .location,
+            .house,
+            .role {
+              font-size: 14px;
+              color: #9399a5;
+            }
+            .tag {
+              .tag-item {
+                display: inline-block;
+                height: 30px;
+                margin-right: 10px;
+                padding: 0 12px;
+                line-height: 30px;
+                font-size: 12px;
+                color: #849aae;
+                background: rgba(132, 154, 174, 0.1);
+              }
+            }
+          }
+        }
+        .item-price {
+          .unit-price {
+            color: #fe615a;
+            span {
+              font-size: 28px;
+              line-height: 28px;
+              vertical-align: bottom;
+            }
+          }
+          .total-price {
+            margin-top: 10px;
+            font-size: 12px;
+            color: #9399a5;
+            text-align: right;
+          }
+        }
+      }
+    }
+    .list-right {
+      .title {
+        font-size: 16px;
+        color: #101d37;
+        line-height: 22px;
+        font-weight: 700;
+      }
+      .hot-list {
+        margin-top: 20px;
+        .hot-item {
+          cursor: pointer;
+          width: 184px;
+          margin-top: 20px;
+          .hot-cover {
+            width: 100%;
+            height: 128px;
+            img {
+              width: 100%;
+              height: 128px;
+            }
+          }
+          .hot-text {
+            .hot-name {
+              display: flex;
+              align-items: center;
+              justify-content: flex-start;
+              > span {
+                display: inline-block;
+                font-size: 14px;
+                color: #18253e;
+                max-width: 82px;
+                overflow: hidden;
+                white-space: nowrap;
+                text-overflow: ellipsis;
+              }
+              .hot-label {
+                display: flex;
+                .item {
+                  height: 16px;
+                  font-size: 12px;
+                  line-height: 16px;
+                  padding: 0 4px;
+                  box-sizing: border-box;
+                  border-radius: 2px;
+                  &.zs {
+                    color: #3072f6;
+                    background: rgba(107, 153, 246, 0.11);
+                    margin-right: 4px;
+                  }
+                  &.zz {
+                    border: 1px solid #ced2d6;
+                    color: #9399a5;
+                    line-height: 14px;
+                  }
+                }
+              }
+            }
+            .price-wrapper {
+              display: flex;
+              .hot-price {
+                font-size: 14px;
+                color: #fe615a;
+                font-weight: 700;
+              }
+              .hot-unit {
+                font-size: 14px;
+                color: #000019;
+                font-weight: 700;
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+}
+</style>

+ 38 - 0
vite.config.js

@@ -0,0 +1,38 @@
+import { fileURLToPath, URL } from "node:url";
+import { defineConfig } from "vite";
+import vue from "@vitejs/plugin-vue";
+
+const isDev = process.env.NODE_ENV === "development";
+// import vueDevTools from 'vite-plugin-vue-devtools'
+
+// https://vite.dev/config/
+export default defineConfig({
+  base: "./",
+  build: {
+    // assetsDir: "assets",
+    // outDir: 'dist', // 输出目录
+    // emptyOutDir: true,
+    copyPublicDir: false,
+  },
+  plugins: [
+    vue(),
+    // vueDevTools(),
+  ],
+  server: {
+    host: "0.0.0.0",
+    proxy: {
+      "/api": {
+        // target: 'https://sit-yueyangbwg.4dage.com', // 后端服务地址
+        // target: "https://txiaobo.qiweiwangguo.com", // 后端服务地址
+        target: "https://hn3dreal.org.cn", // 后端服务地址
+        changeOrigin: true, // 是否改变源地址
+        // rewrite: (path) => path.replace(/^\/api/, ""),
+      },
+    },
+  },
+  resolve: {
+    alias: {
+      "@": fileURLToPath(new URL("./src", import.meta.url)),
+    },
+  },
+});

Dosya farkı çok büyük olduğundan ihmal edildi
+ 1950 - 0
yarn.lock