chengziding преди 1 месец
ревизия
b2d04a7f28
променени са 100 файла, в които са добавени 9728 реда и са изтрити 0 реда
  1. 25 0
      .gitignore
  2. 0 0
      .tgitconfig
  3. 76 0
      README.md
  4. 3 0
      app.config.ts
  5. 250 0
      app.vue
  6. 89 0
      app/router.options.ts
  7. 209 0
      assets/bqfl/iconfont.css
  8. 1 0
      assets/bqfl/iconfont.js
  9. BIN
      assets/bqfl/iconfont.ttf
  10. BIN
      assets/bqfl/iconfont.woff
  11. BIN
      assets/bqfl/iconfont.woff2
  12. 146 0
      assets/bqfl1/iconfont.css
  13. 1 0
      assets/bqfl1/iconfont.js
  14. BIN
      assets/bqfl1/iconfont.ttf
  15. BIN
      assets/bqfl1/iconfont.woff
  16. BIN
      assets/bqfl1/iconfont.woff2
  17. 3282 0
      assets/css/animate.min.css
  18. 224 0
      assets/css/common.scss
  19. 0 0
      assets/css/naive.css
  20. 1007 0
      assets/css/root.scss
  21. 24 0
      assets/css/tool.scss
  22. 139 0
      assets/font/iconfont.css
  23. 1 0
      assets/font/iconfont.js
  24. 226 0
      assets/font/iconfont.json
  25. BIN
      assets/font/iconfont.ttf
  26. BIN
      assets/font/iconfont.woff
  27. BIN
      assets/font/iconfont.woff2
  28. BIN
      assets/images/bg1.png
  29. BIN
      assets/images/companyProfile/img01.png
  30. BIN
      assets/images/companyProfile/img02.png
  31. BIN
      assets/images/companyProfile/img03.png
  32. BIN
      assets/images/companyProfile/img04.png
  33. BIN
      assets/images/companyProfile/img05.png
  34. BIN
      assets/images/companyProfile/img06.png
  35. BIN
      assets/images/companyProfile/img07.png
  36. BIN
      assets/images/companyProfile/img08.png
  37. BIN
      assets/images/companyProfile/img09.png
  38. BIN
      assets/images/companyProfile/img10.png
  39. BIN
      assets/images/companyProfile/img11.png
  40. BIN
      assets/images/companyProfile/img12.png
  41. BIN
      assets/images/customersServed/img1.png
  42. BIN
      assets/images/customersServed/img2.png
  43. BIN
      assets/images/customersServed/img3.png
  44. BIN
      assets/images/customersServed/img4.png
  45. BIN
      assets/images/customersServed/img5.png
  46. BIN
      assets/images/customersServed/img6.png
  47. BIN
      assets/images/img1.png
  48. BIN
      assets/images/img10.png
  49. BIN
      assets/images/img11.png
  50. BIN
      assets/images/img12.png
  51. BIN
      assets/images/img13.png
  52. BIN
      assets/images/img14.png
  53. BIN
      assets/images/img15.png
  54. BIN
      assets/images/img16.png
  55. BIN
      assets/images/img17.png
  56. BIN
      assets/images/img2.png
  57. BIN
      assets/images/img3.png
  58. BIN
      assets/images/img4.png
  59. BIN
      assets/images/img5.png
  60. BIN
      assets/images/img6.png
  61. BIN
      assets/images/img7.png
  62. BIN
      assets/images/img8.png
  63. BIN
      assets/images/img9.png
  64. BIN
      assets/images/researchAdvantages/img1.png
  65. BIN
      assets/images/researchAdvantages/img1_a.png
  66. BIN
      assets/images/researchAdvantages/img2.png
  67. BIN
      assets/images/researchAdvantages/img2_a.png
  68. BIN
      assets/images/researchAdvantages/img3.png
  69. BIN
      assets/images/researchAdvantages/img3_a.png
  70. BIN
      assets/images/researchAdvantages/img4.png
  71. BIN
      assets/images/researchAdvantages/img4_a.png
  72. BIN
      assets/images/researchAdvantages/img5.png
  73. BIN
      assets/images/researchAdvantages/img5_a.png
  74. BIN
      assets/images/researchAdvantages/img6.png
  75. BIN
      assets/images/researchAdvantages/img6_a.png
  76. 10 0
      assets/js/index.js
  77. 3 0
      assets/js/wow.min.js
  78. 9 0
      auto-imports.d.ts
  79. 43 0
      components.d.ts
  80. 242 0
      components/Demand.vue
  81. 184 0
      components/Footer/index.vue
  82. 570 0
      components/Header/index.vue
  83. 107 0
      components/Login/forgot.vue
  84. 169 0
      components/Login/index.vue
  85. 214 0
      components/Login/register.vue
  86. 117 0
      components/Login/updatePwd.vue
  87. 34 0
      components/PdfPreview/index.vue
  88. 176 0
      composables/api.ts
  89. 26 0
      composables/cache.ts
  90. 55 0
      composables/cipher.ts
  91. 106 0
      composables/index.ts
  92. 34 0
      composables/storage.ts
  93. 92 0
      composables/useHttp.ts
  94. 163 0
      composables/validate.ts
  95. 781 0
      lang/en-us.ts
  96. 13 0
      lang/i18n.ts
  97. 671 0
      lang/zh-cn.ts
  98. 126 0
      nuxt.config.ts
  99. 80 0
      nuxt.config.ts.bak
  100. 0 0
      package-lock.json

+ 25 - 0
.gitignore

@@ -0,0 +1,25 @@
+# Nuxt dev/build outputs
+.output
+.data
+.nuxt
+.nitro
+.cache
+dist
+
+# Node dependencies
+node_modules
+
+# Logs
+logs
+*.log
+
+# Misc
+.DS_Store
+.fleet
+.idea
+
+# Local env files
+.history
+.env
+.env.*
+!.env.example

+ 0 - 0
.tgitconfig


+ 76 - 0
README.md

@@ -0,0 +1,76 @@
+# Nuxt 3 Minimal Starter
+
+Look at the [Nuxt 3 documentation](https://nuxt.com/docs/getting-started/introduction) to learn more.
+
+## Setup
+
+Make sure to install the dependencies:
+
+```bash
+# npm
+npm install
+
+# pnpm
+pnpm install
+
+# yarn
+yarn install
+
+# bun
+bun install
+```
+
+## Development Server
+
+Start the development server on `http://localhost:3000`:
+
+```bash
+# npm
+npm run dev
+
+# pnpm
+pnpm run dev
+
+# yarn
+yarn dev
+
+# bun
+bun run dev
+```
+
+## Production
+
+Build the application for production:
+
+ 打包时路径不能有中文
+```bash
+# npm
+npm run build
+
+# pnpm
+pnpm run build
+
+# yarn
+yarn build
+
+# bun
+bun run build
+```
+
+Locally preview production build:
+
+```bash
+# npm
+npm run preview
+
+# pnpm
+pnpm run preview
+
+# yarn
+yarn preview
+
+# bun
+bun run preview
+```
+
+Check out the [deployment documentation](https://nuxt.com/docs/getting-started/deployment) for more information.

+ 3 - 0
app.config.ts

@@ -0,0 +1,3 @@
+export default defineAppConfig({
+    title: 'Hello Nuxt'
+})

+ 250 - 0
app.vue

@@ -0,0 +1,250 @@
+<template>
+  <div id="layout-wrapper" class="layout-wrapper" :class="inverse">
+    <Header></Header>
+    <div class="main-container">
+      <!-- <NuxtPage /> -->
+      <router-view></router-view>
+      <!-- <template v-if="language == ''">
+        <div class="layout-link">
+          <div class="email">
+            <n-popover trigger="hover" placement="left-start">
+              <template #trigger>
+                <div class="email-icon link-icon">
+                  <n-icon :component="IosCall" :size="30" color="#fff" />
+                </div>
+              </template>
+              <div class="email-cnt link-content">
+                <div class="email-title link-title">
+                  {{ t('common.link.phone') }}
+                </div>
+                <div class="link-way">
+                  <a href="tel:86-027-85566566">86-027-85307885</a>
+                </div>
+              </div>
+            </n-popover>
+          </div>
+          <div class="telphone">
+            <n-popover trigger="hover" placement="left-start">
+              <template #trigger>
+                <div class="email-icon link-icon">
+                  <n-icon :component="IosPhonePortrait" :size="30" color="#fff" />
+                </div>
+              </template>
+              <div class="link-content">
+                <div class="link-title">{{ t('common.link.telphone') }}</div>
+                <div class="link-way">
+                  <a href="tel:17320528525">{{ t('common.link.servicer01') }} 17320528525</a>
+                  <a href="tel:17320528335">{{ t('common.link.servicer02') }} 17320528335</a>
+                </div>
+              </div>
+            </n-popover>
+          </div>
+          <div class="wx">
+            <n-popover trigger="hover" placement="left-start">
+              <template #trigger>
+                <div class="email-icon link-icon">
+                  <n-icon :component="IosChatbubbles" :size="30" color="#fff" />
+                </div>
+              </template>
+              <div class="link-content">
+                <div class="link-title">{{ t('common.wx.service') }}</div>
+                <div class="link-way">
+                  <img src="@/assets/images/wx-02.jpg" />
+                </div>
+              </div>
+            </n-popover>
+          </div>
+
+        </div>
+      </template> -->
+    </div>
+    <Footer v-if="showFooter"></Footer>
+
+    <n-back-top :right="5" :bottom="150" color="#fff" icon-color="#fff" :theme-overrides="{ iconColor: '#fff' }" style="width: 45px; height: 45px; background-color: #18a058; color: #fff; border-radius: 100%" />
+  </div>
+</template>
+
+<script lang="ts" setup>
+import { ref, onMounted, watch, onUnmounted } from "vue";
+import {
+  IosMail,
+  IosPhonePortrait,
+  IosChatbubbles,
+  IosCall,
+} from "@vicons/ionicons4";
+import { useRoute } from "vue-router";
+import { useI18n } from "#imports";
+import { useUserStore } from "@/store/user";
+const { t } = useI18n();
+const route = useRoute();
+const inverse = ref<string>("");
+const code = ref<string>("none"); // 二维码
+const closeStatus = ref<string>("");
+const scrollTop = ref<number>(0); // 滚动距离
+const color = ref<string>("#fff"); // 导航栏字体颜色
+const showFooter = ref<boolean>(true);
+const userStore = useUserStore();
+const language = ref("");
+const nuxtApp = useNuxtApp();
+onMounted(async () => {
+  language.value = userStore.getLang;
+  renderNavbar(route.path);
+  window.addEventListener("scroll", onScroll);
+  try {
+    const script = document.createElement("script");
+    script.src = "/wow.js";
+    script.onload = () => {
+      if (window.WOW) {
+        new window.WOW().init();
+        window.__WOW_INITIALIZED__ = true; // 设置标志防止重复初始化
+      }
+    };
+    document.head.appendChild(script);
+  } catch (error) {
+    console.log(error);
+  }
+});
+
+onUnmounted(() => {
+  window.removeEventListener("scroll", onScroll);
+});
+
+watch(() => route.name, renderNavbar);
+
+// 导航栏切换
+function renderNavbar(routePath: any | null | undefined) {
+  if (
+    (routePath === "/" || "/market" === route.path) &&
+    scrollTop.value === 0
+  ) {
+    inverse.value = "inverse";
+    code.value = "none";
+    color.value = "#fff";
+  } else {
+    inverse.value = "";
+    code.value = "block";
+    color.value = "#000";
+  }
+  showFooter.value = ["mineUpdatePwd", "payBack"].includes(routePath)
+    ? false
+    : true;
+  ["reports", "newsCategories", "bulletinThinkTank"].includes(routePath)
+    ? userStore.setSearchType(routePath)
+    : userStore.setSearchType("");
+}
+
+// 滚动条事件
+function onScroll() {
+  scrollTop.value = document.documentElement.scrollTop;
+  if (
+    scrollTop.value === 0 &&
+    (route.name === "index___zh" ||
+      route.name === "index___en" ||
+      "/market" === route.path)
+  ) {
+    inverse.value = "inverse";
+    code.value = "none";
+    color.value = "#fff";
+  } else {
+    inverse.value = "";
+    if (closeStatus.value != "click") {
+      code.value = "block";
+    }
+    color.value = "#000";
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.main-container {
+  margin-top: 140px;
+  min-height: calc(100vh - 140px);
+  //background-color: #edf0f5;
+  .limit-width {
+    width: 1220px;
+    margin: 0 auto;
+  }
+}
+// .inverse :deep(.app-header > div) {
+//   background-color: transparent !important;
+// }
+.main-container {
+  margin-top: 0;
+}
+
+.layout-link {
+  position: fixed;
+  bottom: 200px;
+  right: 5px;
+  z-index: 3;
+  display: v-bind("code");
+  > div {
+    padding-bottom: 5px;
+    cursor: pointer;
+  }
+  .link-icon {
+    width: 45px;
+    height: 45px;
+    border-radius: 5px;
+    background-color: #18a058;
+    .n-icon {
+      line-height: 55px;
+      width: 100%;
+      text-align: center;
+    }
+  }
+}
+
+.link-content {
+  padding: 8px;
+  text-align: center;
+  border: 1px solid #18a058;
+  font-size: 16px;
+  .link-title {
+    border-bottom: 1px solid #18a058;
+    padding-bottom: 4px;
+    color: #18a058;
+  }
+  .link-way {
+    font-weight: 600;
+    padding-top: 4px;
+    > img {
+      width: 150px;
+      height: 150px;
+    }
+    > a {
+      display: block;
+    }
+  }
+}
+.v-binder-follower-content {
+  top: 23px;
+}
+</style>
+<style lang="scss">
+#nav-item .txt,
+.home-tel,
+.lang-select,
+.lang .n-icon,
+.userInfo {
+  color: v-bind("color");
+}
+.home-tel {
+  .n-icon {
+    border: 1px solid v-bind("color");
+  }
+}
+
+#nav-item {
+  &.router-link-active {
+    &::after {
+      background-color: v-bind("color");
+    }
+  }
+  &:hover {
+    &::after {
+      background-color: v-bind("color");
+    }
+  }
+}
+</style>

+ 89 - 0
app/router.options.ts

@@ -0,0 +1,89 @@
+import { RouterConfig } from '@nuxt/schema'
+
+export default <RouterConfig>{
+  routes: (_routes) => {
+    return [
+      {
+        name: 'reports',
+        path: '/:lang?/report-industries/:category?/:keyword?', // 自定义路由地址
+        component: () => import('~/pages/report-industries/index.vue'), // 指向你的首页组件
+        meta: { title: '研究报告', titleEn: 'Research Report', activeIndex: 1 }
+      },
+      {
+        name: 'reportDetail',
+        path: '/:lang?/reports/:webTitle?',
+        component: () => import('~/pages/report-industries/detail.vue'),
+        meta: { title: '研究报告详情', titleEn: 'Research Report Detail', activeIndex: 1 }
+      },
+      {
+        name: 'bulletinThinkTank',
+        path: '/bulletin/bulletin-think-tank/:marketType?/:keyword?',
+        component: () => import('~/pages/bulletin/index.vue'),
+        meta: { title: '行研简报', activeIndex: 3 }
+      },
+      {
+        name: 'bulletinDetail',
+        path: '/bulletin/bulletin-detail/:webTitle?',
+        component: () => import('~/pages/bulletin/detail.vue'),
+        meta: { title: '行研简报详情', activeIndex: 3 }
+      },
+
+      {
+        name: 'newsCategories',
+        path: '/:lang?/news-categories/:marketType?/:keyword?',
+        component: () => import('~/pages/news-categories/index.vue'),
+        meta: { title: '行业资讯', titleEn: 'Industry News', activeIndex: 4 }
+      },
+      {
+        name: 'newsDetail',
+        path: '/:lang?/news/:webTitle?',
+        component: () => import('~/pages/news-categories/detail.vue'),
+        meta: { title: '行业资讯详情', titleEn: 'Industry News Detail', activeIndex: 4 }
+      },
+
+      {
+        name: 'about',
+        path: '/:lang?/about',
+        component: () => import('~/pages/about/index.vue'),
+        meta: { title: '公司简介', titleEn: 'Company Profile', activeIndex: 5 }
+      },
+      {
+        name: 'link',
+        path: '/:lang?/link',
+        component: () => import('~/pages/about/link.vue'),
+        meta: { title: '研究方法', titleEn: 'Research Method', activeIndex: 5 }
+      },
+      {
+        name: 'order',
+        path: '/:lang?/order',
+        component: () => import('~/pages/about/order.vue'),
+        meta: { title: '关于订购', titleEn: 'Order Process', activeIndex: 5 }
+      },
+      {
+        name: 'term',
+        path: '/:lang?/term',
+        component: () => import('~/pages/about/term.vue'),
+        meta: { title: '相关条款', titleEn: 'Terms and Conditions', activeIndex: 5 }
+      },
+      {
+        name: 'qualify',
+        path: '/:lang?/qualify',
+        component: () => import('~/pages/about/qualify.vue'),
+        meta: { title: '企业资质', titleEn: 'Enterprise Qualifications', activeIndex: 5 }
+      },
+      {
+        name: 'contactUsIndex',
+        path: '/:lang?/contactUs',
+        component: () => import('~/pages/contact/index.vue'),
+        meta: { title: '企业资质', titleEn: 'Enterprise Qualifications', activeIndex: 5 }
+      },
+      {
+        name: 'mine',
+        path: '/:lang?/mine/mineCenter',
+        component: () => import('~/pages/mine/index.vue'),
+        meta: { title: '个人中心' }
+      },
+      ..._routes, // 保留默认的路由配置
+    ]
+  }
+}

+ 209 - 0
assets/bqfl/iconfont.css

@@ -0,0 +1,209 @@
+@font-face {
+  font-family: "bqfl-iconfont";
+  /* Project id 4790853 */
+  src: url('iconfont.woff2?t=1736326505362') format('woff2'),
+    url('iconfont.woff?t=1736326505362') format('woff'),
+    url('iconfont.ttf?t=1736326505362') format('truetype');
+}
+
+.bqfl-iconfont {
+  font-family: "bqfl-iconfont" !important;
+  font-size: 20px;
+  font-style: normal;
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+  color: #18a058;
+}
+
+.cursor {
+  color: #00B1FF;
+  cursor: pointer;
+}
+
+.color80 {
+  color: #808080;
+}
+
+.color99 {
+  color: #999999;
+}
+
+.color00C800 {
+  color: #00C800;
+}
+
+.color1296DB {
+  color: #1296DB;
+}
+
+.color18A058 {
+  color: #18A058;
+}
+
+.f30 {
+  font-size: 30px
+}
+
+.f16 {
+  font-size: 16px
+}
+
+.mr5 {
+  margin-right: 5px;
+}
+
+.ml5 {
+  margin-left: 5px;
+}
+
+.mr10 {
+  margin-right: 10px;
+}
+
+.icon-shouqi:before {
+  content: "\e601";
+}
+
+.icon-IPO:before {
+  content: "\e665";
+}
+
+.icon-tags:before {
+  content: "\e806";
+}
+
+.icon-yincangmima:before {
+  content: "\e612";
+}
+
+.icon-a-yincangyulanmimayincang_24:before {
+  content: "\e694";
+}
+
+.icon-xiugai:before {
+  content: "\e610";
+}
+
+.icon-anonymous-iconfont:before {
+  content: "\e6ba";
+}
+
+.icon-item-04:before {
+  content: "\e600";
+}
+
+.icon-cheliangguanli:before {
+  content: "\e854";
+}
+
+.icon-jixieshebei:before {
+  content: "\e71c";
+}
+
+.icon-bandaotichanye_fenliqijian:before {
+  content: "\e632";
+}
+
+.icon-hangkonghangtian:before {
+  content: "\e93c";
+}
+
+.icon-ruanjian:before {
+  content: "\e75b";
+}
+
+.icon-nongye:before {
+  content: "\e63b";
+}
+
+.icon-yiyao:before {
+  content: "\e70b";
+}
+
+.icon-zhaopinqushi-xiaofeipinhangye:before {
+  content: "\e66c";
+}
+
+.icon-yiliao-shebei:before {
+  content: "\e6d9";
+}
+
+.icon-globeo24:before {
+  content: "\e73d";
+}
+
+.icon-arrowRight-copy-copy-copy:before {
+  content: "\e67c";
+}
+
+.icon-zhifubao:before {
+  content: "\e636";
+}
+
+.icon-yonghuming:before {
+  content: "\e643";
+}
+
+.icon-shichangtiaochabiao:before {
+  content: "\e6de";
+}
+
+.icon-shangshiqiye:before {
+  content: "\e64c";
+}
+
+.icon-iconyanjiubaogao01:before {
+  content: "\e640";
+}
+
+.icon-weixinzhifu:before {
+  content: "\e650";
+}
+
+.icon-icon:before {
+  content: "\e65f";
+}
+
+.icon-dkw_guanbi-:before {
+  content: "\e602";
+}
+
+.icon-shouye:before {
+  content: "\e65b";
+}
+
+.icon-quanjuyifasong-icon:before {
+  content: "\e619";
+}
+
+.icon-zhinengjianbao:before {
+  content: "\e604";
+}
+
+.icon-yanjiubaogao:before {
+  content: "\e61c";
+}
+
+.icon-yanzhengma:before {
+  content: "\e6bf";
+}
+
+.icon-mima:before {
+  content: "\e617";
+}
+
+.icon-sousuo:before {
+  content: "\e6d3";
+}
+
+.icon-shangyeqihuashu:before {
+  content: "\e61a";
+}
+
+.icon-dingzhifuwu:before {
+  content: "\e622";
+}
+
+.icon-hangyefenlei:before {
+  content: "\e710";
+}

Файловите разлики са ограничени, защото са твърде много
+ 1 - 0
assets/bqfl/iconfont.js


BIN
assets/bqfl/iconfont.ttf


BIN
assets/bqfl/iconfont.woff


BIN
assets/bqfl/iconfont.woff2


+ 146 - 0
assets/bqfl1/iconfont.css

@@ -0,0 +1,146 @@
+@font-face {
+  font-family: "iconfont"; /* Project id 3952127 */
+  src: url('iconfont.woff2?t=1691745933487') format('woff2'),
+       url('iconfont.woff?t=1691745933487') format('woff'),
+       url('iconfont.ttf?t=1691745933487') format('truetype');
+}
+
+.iconfont {
+  font-family: "iconfont" !important;
+  font-size: 20px;
+  font-style: normal;
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+  /* color: #18a058; */
+}
+
+.f30{font-size: 30px;}
+.f50{font-size: 50px;}
+.f70{font-size: 70px;}
+.f100{font-size: 100px;}
+
+
+.icon-chaxun:before {
+  content: "\e610";
+}
+
+.icon-gongsi:before {
+  content: "\e769";
+}
+
+.icon-a-dingdanguanli2x:before {
+  content: "\e6f0";
+}
+
+.icon-iconset0435:before {
+  content: "\e73a";
+}
+
+.icon-ruanjiankaifabao:before {
+  content: "\ec2e";
+}
+
+.icon-shipinleimu:before {
+  content: "\e6f2";
+}
+
+.icon-baozhuang:before {
+  content: "\e633";
+}
+
+.icon-a-ziyuan609:before {
+  content: "\e793";
+}
+
+.icon-shichangdongtai:before {
+  content: "\e6ee";
+}
+
+.icon-zhengcejiedu:before {
+  content: "\e607";
+}
+
+.icon-ziyuan:before {
+  content: "\e617";
+}
+
+.icon-chanyefenxi:before {
+  content: "\e747";
+}
+
+.icon-other:before {
+  content: "\e62d";
+}
+
+.icon-shichang:before {
+  content: "\e60d";
+}
+
+.icon-channel:before {
+  content: "\e61e";
+}
+
+.icon-shejiyukaifa-:before {
+  content: "\eaed";
+}
+
+.icon-tuandui:before {
+  content: "\e604";
+}
+
+.icon-chanyelian:before {
+  content: "\e605";
+}
+
+.icon-zhihang:before {
+  content: "\e816";
+}
+
+.icon-fenxi:before {
+  content: "\e618";
+}
+
+.icon-qudaofenxi:before {
+  content: "\e606";
+}
+
+.icon-yaopin:before {
+  content: "\e649";
+}
+
+.icon-dianzixue-:before {
+  content: "\e69c";
+}
+
+.icon-fengshan:before {
+  content: "\e612";
+}
+
+.icon-qiche:before {
+  content: "\ec6d";
+}
+
+.icon-gl-network:before {
+  content: "\e99b";
+}
+
+.icon-yiliaoqixie:before {
+  content: "\e7c8";
+}
+
+.icon-hangtiantubiao-:before {
+  content: "\e600";
+}
+
+.icon-shangpin:before {
+  content: "\e634";
+}
+
+.icon-jixiebi:before {
+  content: "\e67b";
+}
+
+.icon-shiyanhuaxue:before {
+  content: "\e653";
+}
+

Файловите разлики са ограничени, защото са твърде много
+ 1 - 0
assets/bqfl1/iconfont.js


BIN
assets/bqfl1/iconfont.ttf


BIN
assets/bqfl1/iconfont.woff


BIN
assets/bqfl1/iconfont.woff2


Файловите разлики са ограничени, защото са твърде много
+ 3282 - 0
assets/css/animate.min.css


+ 224 - 0
assets/css/common.scss

@@ -0,0 +1,224 @@
+@import "~/assets/css/tool.scss";
+// @import "~/assets/css/font/iconfont.css";
+@import "~/assets/bqfl1/iconfont.css";
+@import "~/assets/font/iconfont.css";
+
+// .mywow {
+//   //    display: none;
+//   transform: translateX(-10000000px);
+// }
+
+* {
+  // padding: 0;
+  // margin: 0;
+  box-sizing: border-box;
+}
+
+a {
+  text-decoration: none;
+  /* 去除默认的下划线 */
+  color: inherit;
+  /* 去除默认的颜色和点击后变化的颜色 */
+}
+
+@include responseTo("pc") {
+
+  html,
+  body {
+    // min-width: 1500px;
+    color: var(--color-01);
+    font-weight: var(--weight-400);
+    font-family: var(--family-01);
+    margin: 0;
+  }
+
+  .container {
+    // padding-top: 170px;
+  }
+
+  body {
+    max-width: 1920px;
+    margin: auto;
+  }
+
+  .center1200 {
+    width: 1200px !important;
+    margin: 0 auto !important;
+  }
+
+  .center1440 {
+    width: 1440px !important;
+    margin: 0 auto !important;
+  }
+
+  .center1670 {
+    width: 1670px !important;
+    margin: 0 auto !important;
+  }
+}
+
+img {
+  line-height: 1;
+}
+
+.one-row {
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
+}
+
+.bg {
+  background-repeat: no-repeat;
+  background-position: center center;
+  background-size: cover;
+}
+
+.center-btn {
+  width: auto;
+  min-width: var(--size-178);
+  min-height: var(--size-50);
+  background: linear-gradient(-45deg, #2cbac0 0%, #006efe 100%), #006efe;
+  border-radius: 4px;
+  display: inline-flex;
+  justify-content: center;
+  align-items: center;
+  font-size: var(--size-18);
+  font-family: var(--family-01);
+  font-weight: var(--weight-400);
+  color: #ffffff;
+}
+
+.btn-01 {
+  width: 1px;
+  display: inline-flex;
+  justify-content: center;
+  align-items: center;
+  flex-wrap: nowrap;
+  min-width: var(--size-178);
+  min-height: var(--size-50);
+  padding: 0 var(--size-20);
+  background: #ffffff;
+  border: 1px solid var(--color-04);
+  border-radius: var(--size-4);
+  font-size: var(--size-18);
+  font-family: var(--family-01);
+  font-weight: var(--weight-400);
+  color: var(--color-04);
+}
+
+.load {
+  position: fixed;
+  top: 45%;
+  left: 50%;
+
+  // .n-spin-description {
+  //   color: #b10000;
+  // }
+
+  // .n-base-loading__icon {
+  //   color: #b10000 !important;
+  // }
+}
+
+.n-dropdown-menu.en .n-dropdown-option .n-dropdown-option-body .n-dropdown-option-body__label {
+  font-size: 0.85em;
+}
+
+/** 个人中心共用样式 */
+.personal-center {
+  padding: var(--size-220) var(--size-220) var(--size-40) var(--size-220);
+  background-color: #f5f5f5;
+
+  .center-component {
+    width: 100%;
+    min-height: var(--size-220);
+    // background-color: #ffffff;
+    border-radius: var(--size-16);
+    overflow: hidden;
+    display: flex;
+    background-color: #ffffff;
+
+    .center-component-right {
+      flex: 1;
+
+      .component-right-head {
+        width: 100%;
+        display: flex;
+        align-items: center;
+        justify-content: space-between;
+        border-bottom: var(--size-3) solid #f5f5f5;
+        padding: var(--size-40) var(--size-30) var(--size-14) var(--size-54);
+
+        .head-title {
+          flex: 1;
+          color: #1a1a1a;
+          margin: 0;
+          font-size: var(--size-24);
+          font-weight: 700;
+        }
+      }
+
+      .component-right-body {
+        padding: var(--size-40) var(--size-30) var(--size-14) var(--size-54);
+      }
+    }
+  }
+}
+
+// 表格样式
+.table-style {
+  box-sizing: border-box;
+
+  .th-sort {
+    display: flex;
+    justify-content: center;
+
+    .iconfont {
+      color: #ff6a00;
+      margin-right: var(--size-4);
+    }
+
+    span {
+      min-width: var(--size-70);
+    }
+  }
+
+  th {
+    background-color: #fff7f1;
+    font-weight: 700;
+    font-size: var(--size-16);
+    box-sizing: border-box;
+  }
+
+  td,
+  th {
+    text-align: center;
+    word-wrap: break-word;
+    /* 或者使用 overflow-wrap: break-word; */
+    white-space: normal;
+    /* 默认值,允许文本换行 */
+  }
+
+  tr:nth-of-type(even) td {
+    background-color: #f7f7f7 !important;
+  }
+
+  .highlight {
+    display: block;
+    color: #0090ff;
+    cursor: pointer;
+  }
+
+  .highlight.ver {
+    color: #06a71b;
+    margin-top: var(--size-10);
+  }
+}
+
+// 表格分页样式
+.table-pagination {
+  display: flex;
+  justify-content: center;
+  margin-top: var(--size-50);
+  padding-bottom: var(--size-20);
+}

+ 0 - 0
assets/css/naive.css


Файловите разлики са ограничени, защото са твърде много
+ 1007 - 0
assets/css/root.scss


+ 24 - 0
assets/css/tool.scss

@@ -0,0 +1,24 @@
+$breakpoints: (
+    "phone": (0px,
+        1399px),
+    'pc1':(1400px,
+        1905px),
+    "pc": 1905px,
+
+);
+
+@mixin responseTo($breakname) {
+    $bp: map-get($breakpoints, $breakname);
+
+    @if type-of($bp)=="list" {
+        @media (min-width: nth($bp, 1)) and (max-width: nth($bp, 2)) {
+            @content;
+        }
+    }
+
+    @else {
+        @media (min-width: $bp) {
+            @content;
+        }
+    }
+}

+ 139 - 0
assets/font/iconfont.css

@@ -0,0 +1,139 @@
+@font-face {
+  font-family: "iconfont"; /* Project id 4953057 */
+  src: url('iconfont.woff2?t=1750406138425') format('woff2'),
+       url('iconfont.woff?t=1750406138425') format('woff'),
+       url('iconfont.ttf?t=1750406138425') format('truetype');
+}
+
+.iconfont {
+  font-family: "iconfont" !important;
+  font-size: 16px;
+  font-style: normal;
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+}
+
+.icon-24gf-phoneLoudspeaker:before {
+  content: "\e969";
+}
+
+.icon-lianxidianhua:before {
+  content: "\e634";
+}
+
+.icon-jiantou24:before {
+  content: "\e72c";
+}
+
+.icon-dkw_guanbi-:before {
+  content: "\e603";
+}
+
+.icon-bumenguanli:before {
+  content: "\e698";
+}
+
+.icon-wodedingdan-dingdan:before {
+  content: "\e627";
+}
+
+.icon-sandingzhineng:before {
+  content: "\e60f";
+}
+
+.icon-wo_wodexinxi:before {
+  content: "\e60d";
+}
+
+.icon-tianjiadailishang:before {
+  content: "\e646";
+}
+
+.icon-wodexiazai:before {
+  content: "\e68a";
+}
+
+.icon-gongyingshang:before {
+  content: "\e61f";
+}
+
+.icon-baobiaotongji_caozuoyuanbaobiaodefuben:before {
+  content: "\e6a0";
+}
+
+.icon-jishufuzeren:before {
+  content: "\e6fe";
+}
+
+.icon-duigou:before {
+  content: "\ebe6";
+}
+
+.icon-shouqi:before {
+  content: "\e688";
+}
+
+.icon-yangbenqueren-:before {
+  content: "\e751";
+}
+
+.icon-dinghuoshujufenxi:before {
+  content: "\e602";
+}
+
+.icon-symbol9:before {
+  content: "\e712";
+}
+
+.icon-weidenglu:before {
+  content: "\e6a3";
+}
+
+.icon-noun_invoice_:before {
+  content: "\e61a";
+}
+
+.icon-yinhuanzhili-qiyezichayinhuan:before {
+  content: "\e63d";
+}
+
+.icon-youxiang:before {
+  content: "\e70c";
+}
+
+.icon-shouqi1:before {
+  content: "\e626";
+}
+
+.icon-analytics:before {
+  content: "\e604";
+}
+
+.icon-jiaofuguihua:before {
+  content: "\e63a";
+}
+
+.icon-25:before {
+  content: "\e681";
+}
+
+.icon-shouhoufuwu:before {
+  content: "\e6d3";
+}
+
+.icon-youxiang1:before {
+  content: "\e630";
+}
+
+.icon-a-209yangbenguanli:before {
+  content: "\e610";
+}
+
+.icon-xuqiudongcha:before {
+  content: "\e7e1";
+}
+
+.icon-chengxin:before {
+  content: "\e61e";
+}
+

Файловите разлики са ограничени, защото са твърде много
+ 1 - 0
assets/font/iconfont.js


+ 226 - 0
assets/font/iconfont.json

@@ -0,0 +1,226 @@
+{
+  "id": "4953057",
+  "name": "百谏英文",
+  "font_family": "iconfont",
+  "css_prefix_text": "icon-",
+  "description": "",
+  "glyphs": [
+    {
+      "icon_id": "7568878",
+      "name": "24gf-phoneLoudspeaker",
+      "font_class": "24gf-phoneLoudspeaker",
+      "unicode": "e969",
+      "unicode_decimal": 59753
+    },
+    {
+      "icon_id": "1782678",
+      "name": "联系电话",
+      "font_class": "lianxidianhua",
+      "unicode": "e634",
+      "unicode_decimal": 58932
+    },
+    {
+      "icon_id": "844630",
+      "name": "箭头2-4",
+      "font_class": "jiantou24",
+      "unicode": "e72c",
+      "unicode_decimal": 59180
+    },
+    {
+      "icon_id": "2078798",
+      "name": "dkw_箭头",
+      "font_class": "dkw_guanbi-",
+      "unicode": "e603",
+      "unicode_decimal": 58883
+    },
+    {
+      "icon_id": "1139978",
+      "name": "部门管理",
+      "font_class": "bumenguanli",
+      "unicode": "e698",
+      "unicode_decimal": 59032
+    },
+    {
+      "icon_id": "2651004",
+      "name": "我的订单-订单",
+      "font_class": "wodedingdan-dingdan",
+      "unicode": "e627",
+      "unicode_decimal": 58919
+    },
+    {
+      "icon_id": "6849120",
+      "name": "三定职能",
+      "font_class": "sandingzhineng",
+      "unicode": "e60f",
+      "unicode_decimal": 58895
+    },
+    {
+      "icon_id": "11258811",
+      "name": "我_我的信息",
+      "font_class": "wo_wodexinxi",
+      "unicode": "e60d",
+      "unicode_decimal": 58893
+    },
+    {
+      "icon_id": "12136734",
+      "name": "添加代理商",
+      "font_class": "tianjiadailishang",
+      "unicode": "e646",
+      "unicode_decimal": 58950
+    },
+    {
+      "icon_id": "15392349",
+      "name": "我的下载",
+      "font_class": "wodexiazai",
+      "unicode": "e68a",
+      "unicode_decimal": 59018
+    },
+    {
+      "icon_id": "15419387",
+      "name": "门店设置 base/setting/shop ",
+      "font_class": "gongyingshang",
+      "unicode": "e61f",
+      "unicode_decimal": 58911
+    },
+    {
+      "icon_id": "22717747",
+      "name": "报表统计_操作员报表的副本",
+      "font_class": "baobiaotongji_caozuoyuanbaobiaodefuben",
+      "unicode": "e6a0",
+      "unicode_decimal": 59040
+    },
+    {
+      "icon_id": "38875326",
+      "name": "技术负责人",
+      "font_class": "jishufuzeren",
+      "unicode": "e6fe",
+      "unicode_decimal": 59134
+    },
+    {
+      "icon_id": "4611765",
+      "name": "对勾",
+      "font_class": "duigou",
+      "unicode": "ebe6",
+      "unicode_decimal": 60390
+    },
+    {
+      "icon_id": "5651512",
+      "name": "收起",
+      "font_class": "shouqi",
+      "unicode": "e688",
+      "unicode_decimal": 59016
+    },
+    {
+      "icon_id": "5845625",
+      "name": "I端-样本确认",
+      "font_class": "yangbenqueren-",
+      "unicode": "e751",
+      "unicode_decimal": 59217
+    },
+    {
+      "icon_id": "5912460",
+      "name": "订货数据分析",
+      "font_class": "dinghuoshujufenxi",
+      "unicode": "e602",
+      "unicode_decimal": 58882
+    },
+    {
+      "icon_id": "7342473",
+      "name": "symbol9",
+      "font_class": "symbol9",
+      "unicode": "e712",
+      "unicode_decimal": 59154
+    },
+    {
+      "icon_id": "7960199",
+      "name": "未登录",
+      "font_class": "weidenglu",
+      "unicode": "e6a3",
+      "unicode_decimal": 59043
+    },
+    {
+      "icon_id": "8201602",
+      "name": "noun_invoice_2033013",
+      "font_class": "noun_invoice_",
+      "unicode": "e61a",
+      "unicode_decimal": 58906
+    },
+    {
+      "icon_id": "8505801",
+      "name": "隐患治理-企业自查隐患",
+      "font_class": "yinhuanzhili-qiyezichayinhuan",
+      "unicode": "e63d",
+      "unicode_decimal": 58941
+    },
+    {
+      "icon_id": "9974450",
+      "name": "邮箱",
+      "font_class": "youxiang",
+      "unicode": "e70c",
+      "unicode_decimal": 59148
+    },
+    {
+      "icon_id": "12131049",
+      "name": "收起",
+      "font_class": "shouqi1",
+      "unicode": "e626",
+      "unicode_decimal": 58918
+    },
+    {
+      "icon_id": "13491844",
+      "name": "analytics",
+      "font_class": "analytics",
+      "unicode": "e604",
+      "unicode_decimal": 58884
+    },
+    {
+      "icon_id": "15486155",
+      "name": "交付规划",
+      "font_class": "jiaofuguihua",
+      "unicode": "e63a",
+      "unicode_decimal": 58938
+    },
+    {
+      "icon_id": "18922453",
+      "name": "25",
+      "font_class": "25",
+      "unicode": "e681",
+      "unicode_decimal": 59009
+    },
+    {
+      "icon_id": "19615154",
+      "name": "售后服务",
+      "font_class": "shouhoufuwu",
+      "unicode": "e6d3",
+      "unicode_decimal": 59091
+    },
+    {
+      "icon_id": "20312948",
+      "name": "邮箱",
+      "font_class": "youxiang1",
+      "unicode": "e630",
+      "unicode_decimal": 58928
+    },
+    {
+      "icon_id": "38719199",
+      "name": "数据-样本管理",
+      "font_class": "a-209yangbenguanli",
+      "unicode": "e610",
+      "unicode_decimal": 58896
+    },
+    {
+      "icon_id": "39694618",
+      "name": "需求洞察",
+      "font_class": "xuqiudongcha",
+      "unicode": "e7e1",
+      "unicode_decimal": 59361
+    },
+    {
+      "icon_id": "42455253",
+      "name": "诚信",
+      "font_class": "chengxin",
+      "unicode": "e61e",
+      "unicode_decimal": 58910
+    }
+  ]
+}

BIN
assets/font/iconfont.ttf


BIN
assets/font/iconfont.woff


BIN
assets/font/iconfont.woff2


BIN
assets/images/bg1.png


BIN
assets/images/companyProfile/img01.png


BIN
assets/images/companyProfile/img02.png


BIN
assets/images/companyProfile/img03.png


BIN
assets/images/companyProfile/img04.png


BIN
assets/images/companyProfile/img05.png


BIN
assets/images/companyProfile/img06.png


BIN
assets/images/companyProfile/img07.png


BIN
assets/images/companyProfile/img08.png


BIN
assets/images/companyProfile/img09.png


BIN
assets/images/companyProfile/img10.png


BIN
assets/images/companyProfile/img11.png


BIN
assets/images/companyProfile/img12.png


BIN
assets/images/customersServed/img1.png


BIN
assets/images/customersServed/img2.png


BIN
assets/images/customersServed/img3.png


BIN
assets/images/customersServed/img4.png


BIN
assets/images/customersServed/img5.png


BIN
assets/images/customersServed/img6.png


BIN
assets/images/img1.png


BIN
assets/images/img10.png


BIN
assets/images/img11.png


BIN
assets/images/img12.png


BIN
assets/images/img13.png


BIN
assets/images/img14.png


BIN
assets/images/img15.png


BIN
assets/images/img16.png


BIN
assets/images/img17.png


BIN
assets/images/img2.png


BIN
assets/images/img3.png


BIN
assets/images/img4.png


BIN
assets/images/img5.png


BIN
assets/images/img6.png


BIN
assets/images/img7.png


BIN
assets/images/img8.png


BIN
assets/images/img9.png


BIN
assets/images/researchAdvantages/img1.png


BIN
assets/images/researchAdvantages/img1_a.png


BIN
assets/images/researchAdvantages/img2.png


BIN
assets/images/researchAdvantages/img2_a.png


BIN
assets/images/researchAdvantages/img3.png


BIN
assets/images/researchAdvantages/img3_a.png


BIN
assets/images/researchAdvantages/img4.png


BIN
assets/images/researchAdvantages/img4_a.png


BIN
assets/images/researchAdvantages/img5.png


BIN
assets/images/researchAdvantages/img5_a.png


BIN
assets/images/researchAdvantages/img6.png


BIN
assets/images/researchAdvantages/img6_a.png


+ 10 - 0
assets/js/index.js

@@ -0,0 +1,10 @@
+
+// wow入场特效
+const wow = new WOW({
+    boxClass: "wow",
+    animateClass: "animated",
+    offset: 100,
+    mobile: true,
+    live: true
+})
+wow.init()

Файловите разлики са ограничени, защото са твърде много
+ 3 - 0
assets/js/wow.min.js


+ 9 - 0
auto-imports.d.ts

@@ -0,0 +1,9 @@
+/* eslint-disable */
+/* prettier-ignore */
+// @ts-nocheck
+// noinspection JSUnusedGlobalSymbols
+// Generated by unplugin-auto-import
+export {}
+declare global {
+  const NCarousel: typeof import('naive-ui')['NCarousel']
+}

+ 43 - 0
components.d.ts

@@ -0,0 +1,43 @@
+/* eslint-disable */
+// @ts-nocheck
+// Generated by unplugin-vue-components
+// Read more: https://github.com/vuejs/core/pull/3399
+export {}
+
+/* prettier-ignore */
+declare module 'vue' {
+  export interface GlobalComponents {
+    NBackTop: typeof import('naive-ui')['NBackTop']
+    NBreadcrumb: typeof import('naive-ui')['NBreadcrumb']
+    NBreadcrumbItem: typeof import('naive-ui')['NBreadcrumbItem']
+    NButton: typeof import('naive-ui')['NButton']
+    NCard: typeof import('naive-ui')['NCard']
+    NCarousel: typeof import('naive-ui')['NCarousel']
+    NCarouselItem: typeof import('naive-ui')['NCarouselItem']
+    NCheckbox: typeof import('naive-ui')['NCheckbox']
+    NDatePicker: typeof import('naive-ui')['NDatePicker']
+    NDropdown: typeof import('naive-ui')['NDropdown']
+    NEmpty: typeof import('naive-ui')['NEmpty']
+    NForm: typeof import('naive-ui')['NForm']
+    NFormItem: typeof import('naive-ui')['NFormItem']
+    NIcon: typeof import('naive-ui')['NIcon']
+    NInput: typeof import('naive-ui')['NInput']
+    NInputGroup: typeof import('naive-ui')['NInputGroup']
+    NInputNumber: typeof import('naive-ui')['NInputNumber']
+    NModal: typeof import('naive-ui')['NModal']
+    NNumberAnimation: typeof import('naive-ui')['NNumberAnimation']
+    NPagination: typeof import('naive-ui')['NPagination']
+    NPopover: typeof import('naive-ui')['NPopover']
+    NRadio: typeof import('naive-ui')['NRadio']
+    NRadioGroup: typeof import('naive-ui')['NRadioGroup']
+    NSelect: typeof import('naive-ui')['NSelect']
+    NSpace: typeof import('naive-ui')['NSpace']
+    NSpin: typeof import('naive-ui')['NSpin']
+    NTable: typeof import('naive-ui')['NTable']
+    NTabPane: typeof import('naive-ui')['NTabPane']
+    NTabs: typeof import('naive-ui')['NTabs']
+    NTag: typeof import('naive-ui')['NTag']
+    RouterLink: typeof import('vue-router')['RouterLink']
+    RouterView: typeof import('vue-router')['RouterView']
+  }
+}

+ 242 - 0
components/Demand.vue

@@ -0,0 +1,242 @@
+<template>
+  <div class="customer-info">
+    <n-form require-mark-placement="left" label-width="auto" ref="formRef" :model="formData" :rules="rules">
+      <!-- <n-form-item v-show="rowType === '1' && language === 'zh-CN'" > -->
+      <div class="info-tips" v-if="rowType === '1' && language === 'zh-CN'">
+        注明: 此报告为付费报告,客户可在购买前免费索取样本,样本中不包含关键数据点,仅供客户具体了解报告详细框架和细节。
+      </div>
+      <!-- </n-form-item> -->
+      <n-form-item label="版本" label-placement="left" v-if="rowType === '1' && language === 'zh-CN'" path="version">
+        <!-- <n-input :defaultValue="rowRegion === '0'?'中国':'全球与中国'" readonly/> -->
+        <n-radio-group bame="version" v-model:value="formData.version" :style="{'width':'100%'}">
+          <div class="customer-info-china">
+            <n-radio value="0">中国</n-radio>
+            <label>中国版本主要关注中国本土市场</label>
+          </div>
+          <div class="customer-info-global">
+            <n-radio value="1">全球与中国</n-radio>
+            <label class="global-title">全球及中国版本主要关注全球市场</label>
+          </div>
+        </n-radio-group>
+      </n-form-item>
+      <n-form-item :label="t('report.demand.companyName')" label-placement="left" path="companyName">
+        <n-input :placeholder="t('report.demand.companyNameTip')" v-model:value="formData.companyName" />
+      </n-form-item>
+      <n-form-item :label="t('report.demand.address')" label-placement="left" path="addr">
+        <n-input :placeholder="t('report.demand.addressTip')" v-model:value="formData.addr" />
+      </n-form-item>
+      <n-form-item :label="t('report.demand.email')" label-placement="left" path="email">
+        <n-input :placeholder="t('report.demand.emailTip')" v-model:value="formData.email" />
+      </n-form-item>
+      <n-form-item :label="t('report.demand.phone')" label-placement="left" path="contact" :show-require-mark="language ===  'zh-CN'">
+        <n-input :placeholder="t('report.demand.phoneTip')" v-model:value="formData.contact" />
+      </n-form-item>
+      <n-form-item :label="t('report.demand.person')" label-placement="left" path="name">
+        <n-input :placeholder="t('report.demand.personTip')" v-model:value="formData.name" />
+      </n-form-item>
+      <n-form-item :label="t('report.demand.desc')" label-placement="left" path="demand">
+        <n-input type="textarea" :placeholder="t('report.demand.descTip')" v-model:value="formData.demand" />
+      </n-form-item>
+      <div class="form-item-btn">
+        <n-button @click="handleClose">{{t('report.demand.cancel')}}</n-button>
+        <n-button type="primary" @click="handleSubmit">{{t('report.demand.submit')}}</n-button>
+      </div>
+    </n-form>
+  </div>
+</template>
+
+<script setup>
+//import { useLocaleStoreWithOut } from '@/store';
+import {
+  // NForm,
+  // NFormItem,
+  // NInput,
+  // NButton,
+  // FormRules,
+  createDiscreteApi,
+  // NRadio,
+  // NRadioGroup,
+} from "naive-ui";
+import { ref, reactive, onMounted } from "vue";
+import { useI18n } from "#imports";
+import { useUserStore } from "@/store/user";
+const userStore = useUserStore();
+const language = ref();
+onMounted(() => (language.value = userStore.getLang));
+
+const { t } = useI18n();
+const emit = defineEmits(["closeDialog"]);
+const prop = defineProps({
+  rowId: {
+    type: String,
+    default: "",
+  },
+  rowPrice: {
+    type: String,
+    default: null,
+  },
+  rowType: {
+    type: String,
+    default: null,
+  },
+  rowRegion: {
+    type: String,
+    default: null,
+  },
+});
+
+const message = createDiscreteApi(["message"]);
+const formRef = ref();
+const formData = reactive({
+  name: "", // 姓名
+  contact: "", // 联系方式
+  email: "", // 邮箱
+  companyName: "", // 名称
+  demand: "", // 需求
+  reportId: "", // 报告id
+  reportPrice: "", // 报告单价
+  addr: "", // 联系地址
+  type: "", //类型,1.样本,2.定制
+  version: "", //
+});
+const rules = {
+  version: [
+    {
+      required: true,
+      trigger: ["input", "blur"],
+      validator (rule, value) {
+        if (!value) {
+          return new Error("请选择版本");
+        }
+        return true;
+      },
+    },
+  ],
+  companyName: [
+    {
+      required: true,
+      trigger: ["input", "blur"],
+      validator (rule, value) {
+        if (!value) {
+          return new Error(t("report.demand.companyNameTip"));
+        }
+        return true;
+      },
+    },
+  ],
+  // addr: [{
+  //     required: true,
+  //     trigger: ['input','blur'],
+  //     validator(rule: FormItemRule, value: string){
+  //         if(!value){
+  //             return new Error(t('report.demand.addressTip'));
+  //         }
+  //         return true;
+  //     }
+  // }],
+  email: [
+    {
+      required: true,
+      trigger: ["blur"],
+      validator (rule, value) {
+        if (!value) {
+          return new Error(t("report.demand.emailTip"));
+        } else if (!isEmail(value)) {
+          return new Error(t("report.demand.emailFormat"));
+        }
+        return true;
+      },
+    },
+  ],
+  contact: [
+    {
+      required: true,
+      trigger: ["blur"],
+      validator (rule, value) {
+        if (!value && language.value == "zh-CN") {
+          return new Error(t("report.demand.phoneTip"));
+        } else if (!isPhoneNumber(value) && language.value === "zh-CN") {
+          return new Error(t("report.demand.phoneFormat"));
+        }
+        return true;
+      },
+    },
+  ],
+  name: [
+    {
+      required: true,
+      trigger: ["blur", "input"],
+      validator (rule, value) {
+        if (!value) {
+          return new Error(t("report.demand.personTip"));
+        }
+        return true;
+      },
+    },
+  ],
+  demand: [
+    {
+      required: true,
+      trigger: ["input", "blur"],
+      validator (rule, value) {
+        if (!value) {
+          return new Error(t("report.demand.descTip"));
+        }
+        return true;
+      },
+    },
+  ],
+};
+
+// const rules = {
+//     companyName: {
+//         required: true,
+//         message: '请输入公司名称',
+//         trigger: ['input','blur']
+//     }
+// }
+
+// 提交
+function handleSubmit () {
+  formRef.value?.validate(async (errors) => {
+    if (!errors) {
+      formData.reportId = prop.rowId;
+      formData.reportPrice = prop.rowPrice;
+      formData.type = prop.rowType;
+      await customerInfoSave(formData);
+      emit("closeDialog");
+      message.message.success(t("report.demand.success"));
+    }
+  });
+}
+
+// 关闭
+function handleClose () {
+  emit("closeDialog");
+}
+</script>
+
+<style lang="scss" scoped>
+.customer-info {
+  padding-top: 20px;
+  .info-tips {
+    padding-bottom: 20px;
+    color: #f00;
+  }
+  .customer-info-china,
+  .customer-info-global {
+    display: flex;
+    justify-content: space-between;
+  }
+  .customer-info-china {
+    padding-bottom: 20px;
+  }
+}
+.form-item-btn {
+  display: flex;
+  justify-content: center;
+  button:nth-child(1) {
+    margin-right: 60px;
+  }
+}
+</style>

+ 184 - 0
components/Footer/index.vue

@@ -0,0 +1,184 @@
+<template>
+  <div class="app-footer">
+    <div class="footer_l">
+      <div class="footer_l_img">
+        <img src="/assets/images/img3.png" alt="">
+      </div>
+      <div class="footer_l_text">WENKH is a global leading consulting brand for industry segments. We providesegmented market research reports, custom research, white papers and feasibilityreports for all industries, focusing on market status and forecasts, competition analysis, market prospects analysis and market positioning. WENKH usesprofessional data and deep insights to help companies make decisions andpromote win-win cooperation.</div>
+      <div class="footer_l_list">
+        <img src="/assets/images/img4.png" alt="">
+        <img src="/assets/images/img5.png" alt="">
+        <img src="/assets/images/img6.png" alt="">
+        <img src="/assets/images/img7.png" alt="">
+        <img src="/assets/images/img8.png" alt="">
+      </div>
+    </div>
+    <div class="footer_nav">
+      <div class="footer_nav_title">Quick Links</div>
+      <div class="footer_nav_list">
+        <a href="">Home</a>
+        <a href="">Reports Custom</a>
+        <a href="">Research Industry</a>
+        <a href="">News</a>
+        <a href="">About Us</a>
+        <a href="">Contact</a>
+      </div>
+      <div class="footer_nav_contactUs">
+        <div></div>
+      </div>
+    </div>
+    <div class="footer_contactUs">
+      <div class="footer_contactUs_title">Contact Us</div>
+      <div class="footer_contactUs_item">
+        <div class="footer_contactUs_item_l">
+          <span class="iconfont icon-24gf-phoneLoudspeaker"></span>
+        </div>
+        <div class="footer_contactUs_item_r">
+          <div>+1-888 422 6988 (US)</div>
+          <div>+86-191 0717 4767 (Int'l)</div>
+        </div>
+      </div>
+      <div class="footer_contactUs_item">
+        <div class="footer_contactUs_item_l">
+          <span class="iconfont icon-youxiang1"></span>
+        </div>
+        <div class="footer_contactUs_item_r">
+          <div>market@wenkh.com</div>
+        </div>
+      </div>
+    </div>
+    <div class="footer_r">
+      <div class="footer_r_img">
+        <img src="/assets/images/img11.png" alt="">
+      </div>
+      <div class="footer_r_list">
+        <img src="/assets/images/img12.png" alt="">
+        <img src="/assets/images/img13.png" alt="">
+        <img src="/assets/images/img14.png" alt="">
+        <img src="/assets/images/img15.png" alt="">
+        <img src="/assets/images/img16.png" alt="">
+        <img src="/assets/images/img17.png" alt="">
+      </div>
+    </div>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import { MdPhonePortrait, IosPin, MdMail, IosCall } from "@vicons/ionicons4";
+import { useUserStore } from "@/store/user";
+import { onMounted, ref } from "vue";
+import { useI18n } from "#imports";
+const { t } = useI18n();
+const userStore = useUserStore();
+const lang = userStore.getLang;
+const language = ref("");
+onMounted(() => {
+  language.value = userStore.getLang === "en-US" ? "en" : "";
+});
+</script>
+
+<style lang="scss" scoped>
+.app-footer {
+  padding: 0 var(--size-145);
+  background: #ffffff;
+  display: flex;
+  justify-content: space-between;
+  .footer_l {
+    width: var(--size-583);
+    padding: var(--size-40) 0 var(--size-36);
+    .footer_l_img {
+      margin-bottom: var(--size-5);
+      img {
+        width: var(--size-357);
+      }
+    }
+    .footer_l_text {
+      font-size: var(--size-16);
+      font-family: Arial, Arial-Regular;
+      font-weight: 400;
+      text-align: left;
+      color: #4d4d4d;
+    }
+    .footer_l_list {
+      margin-top: var(--size-24);
+      img {
+        width: var(--size-44);
+        height: var(--size-44);
+        margin-right: var(--size-9);
+      }
+    }
+  }
+  .footer_nav {
+    padding: var(--size-50) 0 0;
+    .footer_nav_title {
+      font-size: var(--size-18);
+      font-family: Impact, Impact-Regular;
+      font-weight: 400;
+      color: #1a1a1a;
+    }
+    .footer_nav_list {
+      a {
+        font-size: var(--size-16);
+        font-family: Arial, Arial-Regular;
+        font-weight: 400;
+        text-align: left;
+        color: #4d4d4d;
+        margin-top: var(--size-15);
+        display: block;
+      }
+    }
+  }
+  .footer_contactUs {
+    padding-top: var(--size-50);
+    .footer_contactUs_title {
+      font-size: var(--size-18);
+      font-family: Impact, Impact-Regular;
+      font-weight: 400;
+      text-align: left;
+      color: #1a1a1a;
+      margin-bottom: var(--size-20);
+    }
+    .footer_contactUs_item {
+      display: flex;
+      .footer_contactUs_item_l {
+        width: var(--size-31);
+        height: var(--size-31);
+        background: #e5e5e5;
+        border-radius: var(--size-4);
+        text-align: center;
+        margin-right: var(--size-15);
+        span {
+          font-size: var(--size-18);
+          line-height: var(--size-31);
+          color: #4d4d4d;
+        }
+      }
+      .footer_contactUs_item_r {
+        font-size: var(--size-16);
+        color: #4d4d4d;
+        margin-top: var(--size-2);
+      }
+    }
+  }
+  .footer_r {
+    padding-top: var(--size-60);
+    .footer_r_img {
+      text-align: center;
+      margin-bottom: var(--size-25);
+      img {
+        width: var(--size-113);
+      }
+    }
+    .footer_r_list {
+      img {
+        width: var(--size-52);
+        height: var(--size-31);
+        margin-right: var(--size-10);
+        &:last-child {
+          margin-right: 0;
+        }
+      }
+    }
+  }
+}
+</style>

+ 570 - 0
components/Header/index.vue

@@ -0,0 +1,570 @@
+<template>
+  <div class="app-header">
+    <div class="top">
+      <a> <span class="iconfont icon-youxiang"></span> market@aporesearch.com</a>
+      <a><span class="iconfont icon-lianxidianhua"></span> 1-332-251-9412</a>
+      <div><span class="iconfont icon-weidenglu"></span>Login/Register</div>
+    </div>
+    <div class="navBox">
+      <a class="navBox_l" href="/">
+        <img src="/assets/images/img2.png" alt="">
+      </a>
+      <div class="navBox_c">
+        <n-dropdown :options="reportOptions" trigger="hover" size="huge" @select="chooseReportMenu">
+          <a :class="{'router-link-active':(route.name=='reports')}" :href="jumpLink('/report-industries')">
+            Reports
+          </a>
+        </n-dropdown>
+        <a :class="{'router-link-active':route.path=='/market'}" :href="jumpLink('/market')">
+          Custom Researc
+        </a>
+        <n-dropdown :options="newsOptions" trigger="hover" size="huge" @select="chooseNewsMenu">
+          <a :class="{'router-link-active':(route.name=='newsCategories'||route.path=='/news-categories')}" :href="jumpLink('/news-categories')">
+            Industry News
+          </a>
+        </n-dropdown>
+        <n-dropdown :options="aboutOptions" trigger="hover" size="huge" @select="chooseAboutMenu">
+          <a :class="{'router-link-active':route.path=='/about'||route.path=='/link'||route.path=='/order'||route.path=='/term'||route.path=='/qualify'}" :href="jumpLink('/about')">
+            About Us
+          </a>
+        </n-dropdown>
+        <a :class="{'router-link-active':route.path=='/contactUs'}" :href="jumpLink('/contactUs')">Contact</a>
+      </div>
+      <div class="navBox_r">
+        <div class="search">
+          <div class="search_l">
+            <n-dropdown trigger="hover" :keyboard="false" :options="selectTypeList">
+              <div class="search_l_type">
+                Industry News
+                <span class="iconfont icon-dkw_guanbi-"></span>
+              </div>
+            </n-dropdown>
+          </div>
+          <div class="search_c">
+            <n-input v-model:value="searchVal" @keydown.enter="handleSearch()" type="text" clearable placeholder="Please Enter Keywords" />
+          </div>
+          <div class="search_r" @click="handleSearch()">
+            Search
+          </div>
+        </div>
+      </div>
+    </div>
+    <n-modal :show="signInVisible" preset="dialog" :title="signInTitle" :showIcon="false" :close-on-esc="false" :mask-closable="false" @close="signInVisible = false" :class="{ 'login-dialog': !pcShow }">
+      <SignInLogin @closeSginDialog="closeSginDialog" v-if="sginType == 'login'"></SignInLogin>
+      <SignInRegister @closeSginDialog="closeSginDialog" v-if="sginType == 'register'"></SignInRegister>
+      <SignInForgot @closeSginDialog="closeSginDialog" v-if="sginType == 'forgot'"></SignInForgot>
+    </n-modal>
+    <n-modal v-model:show="logoutVisible" preset="dialog" title="提示" :showIcon="false" content="确认退出登录吗?" positive-text="确认" negative-text="取消" :close-on-esc="false" :mask-closable="false" @positive-click="submitLogoutCallback" @negative-click="cancelLogoutCallback" :class="{ 'login-dialog': !pcShow }" />
+  </div>
+</template>
+
+<script lang="ts" setup>
+import { useI18n } from "#imports";
+import SignInLogin from "@/components/Login/index.vue";
+import SignInRegister from "@/components/Login/register.vue";
+import SignInForgot from "@/components/Login/forgot.vue";
+import { useLocaleStoreWithOut } from "@/store";
+import {
+  // Component,
+  // h,
+  onMounted,
+  ref,
+  onUnmounted,
+  onBeforeMount,
+  watch,
+  computed,
+  inject,
+  onServerPrefetch,
+} from "vue";
+import { createDiscreteApi } from "naive-ui";
+import { useRouter, useRoute } from "vue-router";
+import {
+  MdApps,
+  MdHome,
+  IosPaper,
+  IosSearch,
+  MdToday,
+  MdPeople,
+  MdSearch,
+  IosCall,
+  IosGlobe,
+} from "@vicons/ionicons4";
+import { useUserStore } from "@/store/user";
+const message = createDiscreteApi(["message"]);
+const { t, locale, setLocale } = useI18n();
+const switchLocalePath = useSwitchLocalePath();
+const router = useRouter();
+const route = useRoute();
+const userStore = useUserStore();
+const pcShow = ref(true);
+const keyword = ref<string>(""); //关键字
+userStore.setLang(locale.value == "en" ? "en-US" : "zh-CN");
+const value = userStore.getLang;
+const language = ref("");
+const langSelected = ref("zh");
+language.value = value === "en-US" ? "en" : "";
+langSelected.value = value === "en-US" ? "en" : "zh";
+const renderIcon = (icon: string) => {
+  return () => {
+    return h("span", { class: icon });
+  };
+};
+const renderIconC4 = (icon: Component) => {
+  return () => {
+    return h(NIcon, null, {
+      default: () => h(icon),
+    });
+  };
+};
+
+// const renderIconLabel = (option: DropdownOption) => {
+//   return h('a',{ onclick: () => chooseMenu, target: '_blank' },{ default: () => option.label});
+// }
+const reportOptions = [] as any; //报告列表
+const newsOptions = [] as any; //行业资讯
+const aboutOptions = [] as any; //关于我们
+const bulletinOptions = [] as any; // 简报智库
+
+const mobileMenu = [
+  { label: t("common.navigate.home"), key: "home", name: "home" },
+  {
+    label: t("common.navigate.report"),
+    key: "reports",
+    children: reportOptions,
+    name: "reports",
+  },
+  { label: t("common.navigate.market"), key: "market", name: "marketIndex" },
+  {
+    label: t("common.navigate.news"),
+    key: "newsCategories",
+    children: newsOptions,
+    name: "newsCategories",
+  },
+  {
+    label: t("common.navigate.aboutUs"),
+    key: "about",
+    children: aboutOptions,
+    name: "about",
+  },
+  {
+    label: t("common.navigate.contactUs"),
+    key: "contactUs",
+    name: "contactUsIndex",
+  },
+  // {
+  //   label: "简报智库",
+  //   key: "bulletin",
+  //   children: bulletinOptions,
+  //   name: "bulletin",
+  // },
+];
+const selectTypeValue = ref();
+const selectTypeList = ref([
+  {
+    label: t("common.navigate.report"),
+    value: "reports",
+  },
+  {
+    label: t("common.navigate.news"),
+    value: "newsCategories",
+  },
+  {
+    label: t("common.navigate.bulletin"),
+    value: "bulletinThinkTank",
+  },
+]);
+const signInVisible = ref<boolean>(false);
+const signInTitle = ref<string>("");
+const sginType = ref<string>("login");
+const logoutVisible = ref(false);
+
+const userInfo = computed(() => userStore.getUserInfo);
+const isLogin = computed(() =>
+  userStore.getToken && userStore.getUserInfo ? true : false
+);
+
+const jumpLink = (url: string) => {
+  return (language.value == "en" ? "/en" : "") + url;
+};
+watch(
+  () => userStore.getSearchType,
+  (val) => {
+    selectTypeValue.value = val || null;
+  }
+);
+
+watch(langSelected, () => {
+  if (langSelected.value == "en") {
+    selectTypeList.value = [
+      {
+        label: t("common.navigate.report"),
+        value: "reports",
+      },
+      {
+        label: t("common.navigate.news"),
+        value: "newsCategories",
+      },
+    ];
+    // userStore.resetState();
+  }
+});
+
+watch(
+  () => userStore.getShowLoginDialog,
+  (newval, oldval) => {
+    signInVisible.value = newval;
+    sginType.value = "login";
+    signInTitle.value = t("common.login.title");
+  }
+);
+
+watch(signInVisible, (val) => {
+  if (val === false) {
+    userStore.setShowLoginDialog(false);
+  }
+});
+
+// 中英文切换
+const langChange = async (lng: string) => {
+  userStore.setLang(lng == "zh" ? "zh-CN" : "en-US");
+  const path = switchLocalePath(lng);
+  setLocale(lng);
+  // router.push(path);
+  // location.reload();
+  location.href = path;
+};
+
+// 研究报告菜单切换
+function chooseReportMenu(key: string | number, _option: DropdownOption) {
+  router.push({
+    name: "reports",
+    params: { category: key, lang: language.value },
+  });
+}
+
+// 行业资讯
+function chooseNewsMenu(key: string | number) {
+  router.push({
+    name: "newsCategories",
+    params: { marketType: key, lang: language.value },
+  });
+}
+
+onBeforeMount(async () => {
+  pcShow.value = !isMobile();
+});
+
+// 关于我们跳转
+function chooseAboutMenu(key: string | number, option: DropdownOption) {
+  router.push({ name: option.name, params: { lang: language.value } });
+}
+
+// 关于简报智库
+function chooseBulletinMenu(key: string | number, _option: DropdownOption) {
+  router.push({
+    name: "bulletinThinkTank",
+    params: {
+      marketType: _option.marketType,
+    },
+  });
+}
+
+// 手机端跳转
+function chooseMenu(key: string | number, option: DropdownOption) {
+  if ("reports" === option.name) {
+    router.push({
+      name: option.name,
+      params: { category: key, lang: language.value },
+    });
+  } else if ("newsCategories" === option.name) {
+    router.push({
+      name: option.name,
+      params: { marketType: key, lang: language.value },
+    });
+  } else if ("home" === option.name) {
+    // router.push({
+    //   name: language.value === "" ? "home" : "homeEn",
+    //   params: { marketType: key, lang: language.value },
+    // });
+    jumpLink("/");
+  } else {
+    router.push({ name: option.name, params: { lang: language.value } });
+  }
+}
+
+function handleSearch() {
+  switch (selectTypeValue.value) {
+    case "reports":
+      router.push({
+        name: "reports",
+        params: { lang: language.value, keyword: keyword.value },
+      });
+      break;
+    case "newsCategories":
+      router.push({
+        name: "newsCategories",
+        params: { lang: language.value, keyword: keyword.value },
+      });
+      break;
+    case "bulletinThinkTank":
+      router.push({
+        name: "bulletinThinkTank",
+        params: { marketType: "industry-brief", title: keyword.value },
+      });
+      break;
+    default:
+      router.push({
+        name: "reports",
+        params: { lang: language.value, keyword: keyword.value },
+      });
+      break;
+  }
+}
+
+function keyDown() {
+  handleSearch();
+}
+
+const handleSignIn = () => {
+  signInVisible.value = true;
+  sginType.value = "login";
+  signInTitle.value = t("common.login.title");
+};
+
+const toMine = () => {
+  router.push({
+    name: "mine",
+  });
+};
+
+const handleLogout = () => {
+  logoutVisible.value = true;
+};
+
+const submitLogoutCallback = () => {
+  logOut_Api().then((res) => {
+    message.message.success(t("report.demand.success"));
+    userStore.resetState();
+    router.push("/");
+  });
+};
+
+const cancelLogoutCallback = () => {
+  logoutVisible.value = false;
+};
+
+const closeSginDialog = (type: string) => {
+  switch (type) {
+    case "success":
+      signInVisible.value = false;
+      break;
+    case "login":
+      signInTitle.value = t("common.login.title");
+      break;
+    case "register":
+      sginType.value = type;
+      signInTitle.value = t("common.login.register");
+      break;
+    case "forgot":
+      sginType.value = type;
+      signInTitle.value = t("common.login.forgetPasswordTitle");
+      break;
+    default:
+      break;
+  }
+};
+// onMounted(async () => {
+//window.addEventListener('scroll',onScroll);
+
+// 下拉菜单
+const getDictListData = async () => {
+  const cacheDict = await getLocalSessionReport();
+  cacheDict?.forEach(
+    (vo: { dictLabel: any; dictValue: any; dictIcon: any }) => {
+      reportOptions.push({
+        label: vo.dictLabel,
+        key: vo.dictValue,
+        icon: renderIcon(vo.dictIcon),
+        name: "reports",
+      });
+    }
+  );
+
+  const cacheDictNews = await getLocalSessionNews();
+  cacheDictNews?.forEach((vo: any) => {
+    newsOptions.push({
+      label: vo.dictLabel,
+      key: vo.dictValue,
+      icon: renderIcon(vo.dictIcon),
+      name: "newsCategories",
+    });
+  });
+  aboutOptions.push({
+    label: t("common.navAboutUs.comp"),
+    name: "about",
+    icon: renderIcon("iconfont iconfont icon-ziyuan"),
+  });
+  aboutOptions.push({
+    label: t("common.navAboutUs.method"),
+    name: "link",
+    icon: renderIcon("iconfont icon-chaxun"),
+  });
+  aboutOptions.push({
+    label: t("common.navAboutUs.order"),
+    name: "order",
+    icon: renderIcon("iconfont icon-a-dingdanguanli2x"),
+  });
+  aboutOptions.push({
+    label: t("common.navAboutUs.term"),
+    name: "term",
+    icon: renderIcon("iconfont icon-shejiyukaifa-"),
+  });
+  aboutOptions.push({
+    label: t("common.navAboutUs.qualify"),
+    name: "qualify",
+    icon: renderIcon("iconfont iconfont icon-ziyuan"),
+  });
+  bulletinOptions.push({
+    label: "行业简报",
+    name: "bulletinThinkTank",
+    marketType: "industry-brief",
+    icon: renderIcon("bqfl-iconfont bqfl-iconfont icon-zhinengjianbao"),
+  });
+  bulletinOptions.push({
+    label: "企业洞察",
+    name: "bulletinThinkTank",
+    marketType: "enterprise-hole-analysis",
+    icon: renderIcon("bqfl-iconfont bqfl-iconfont icon-yanjiubaogao"),
+  });
+  bulletinOptions.push({
+    label: "行业简报",
+    name: "bulletinThinkTank",
+    marketType: "industry-brief",
+    icon: renderIcon("bqfl-iconfont bqfl-iconfont icon-zhinengjianbao"),
+  });
+};
+watch(
+  () => route.path,
+  (val) => {
+    getDictListData();
+  }
+);
+getDictListData();
+</script>
+
+<style lang='scss' >
+.app-header {
+  z-index: 9;
+  width: 100%;
+  position: fixed;
+  left: 0;
+  top: 0;
+  .top {
+    display: flex;
+    align-items: center;
+    justify-content: end;
+    background: rgba(6, 17, 29, 0.5);
+    padding: 0 var(--size-60);
+    border-bottom: 1px solid rgba(255, 255, 255, 0.2);
+    > a,
+    > div {
+      display: inline-block;
+      font-size: var(--size-14);
+      color: #ffffff;
+      margin-left: var(--size-35);
+      height: var(--size-45);
+      line-height: var(--size-45);
+      cursor: pointer;
+      display: flex;
+      align-items: center;
+      span {
+        font-size: var(--size-19);
+        display: inline-block;
+        margin-right: var(--size-4);
+      }
+      &:last-child {
+        padding-left: var(--size-35);
+        border-left: var(--size-1) solid #fff;
+      }
+    }
+  }
+  .navBox {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    background: rgba(6, 17, 29, 0.5);
+    padding: 0 var(--size-60);
+    border-bottom: 1px solid rgba(255, 255, 255, 0.2);
+    height: var(--size-85);
+    .navBox_l {
+      font-size: var(--size-34);
+      color: #ffffff;
+      img {
+        width: var(--size-366);
+        height: var(--size-57);
+        display: block;
+      }
+    }
+    .navBox_c {
+      font-size: var(--size-18);
+      color: #ffffff;
+      display: flex;
+      cursor: pointer;
+      > a {
+        padding: var(--size-7) var(--size-43);
+        border-right: var(--size-1) solid rgba(255, 255, 255, 0.5);
+      }
+    }
+    .navBox_r {
+      .search {
+        display: flex;
+        align-items: center;
+        width: var(--size-450);
+        height: var(--size-44);
+        background: #ffffff;
+        border-radius: 8px;
+        overflow: hidden;
+        .search_l {
+          width: var(--size-148);
+          font-size: var(--size-14);
+          text-align: center;
+          cursor: pointer;
+          .search_l_type {
+            span {
+              transform: rotateZ(90deg);
+              display: inline-block;
+              color: #1a1a1a;
+            }
+          }
+        }
+        .search_c {
+          border-left: 1px solid #cccccc;
+          .n-input {
+            --n-height: var(--size-26) !important;
+            line-height: var(--size-26) !important;
+          }
+        }
+        .search_r {
+          width: var(--size-105);
+          font-size: var(--size-16);
+          color: #ffffff;
+          text-align: center;
+          height: var(--size-44);
+          background: linear-gradient(0deg, #7b9c4f 0%, #2da19d 100%), #1a1a1a;
+          line-height: var(--size-44);
+          cursor: pointer;
+        }
+      }
+    }
+  }
+}
+.n-input {
+  --n-border: none !important;
+  --n-border-hover: none !important;
+  --n-border-focus: none !important;
+  --n-box-shadow-focus: none !important;
+  --n-caret-color: #ef001f !important;
+  font-size: var(--size-16) !important;
+  --n-text-color-hover: none !important;
+}
+</style>

+ 107 - 0
components/Login/forgot.vue

@@ -0,0 +1,107 @@
+<template>
+  <div class="container">
+    <template v-if="!emailSent">
+      <n-form ref="formRef" label-placement="left" :label-width="100" :model="form" :rules="rules" size="large" require-mark-placement="left">
+        <n-form-item :label="t('common.register.email')+':'" path="email">
+          <n-input v-model:value="form.email" :placeholder="t('common.register.forgetPasswordPlaceholder')" />
+        </n-form-item>
+      </n-form>
+      <n-button class="login-btn" attr-type="button" type="info" color="#18A058" :disabled="submitLoading" @click="handleSend">
+        {{ submitLoading ? t('common.login.sending') : t('common.login.clickToSend') }}
+      </n-button>
+    </template>
+
+    <template v-else>
+      <p style="text-align: center">
+        <n-icon size="30" color="#00C849" :component="MdCheckmarkCircle" />
+      </p>
+      <p class="tip">
+        {{t('common.login.emailSendSuccess')}}
+      </p>
+      <n-button class="login-btn" attr-type="button" type="info" color="#18A058" @click="handleSure">
+        {{t('common.login.confirm')}}
+      </n-button>
+    </template>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import { useI18n } from "#imports";
+import { createDiscreteApi } from "naive-ui";
+import { MdCheckmarkCircle } from "@vicons/ionicons4";
+import { ref, reactive } from "vue";
+const { t, locale, setLocale } = useI18n();
+const emit = defineEmits(["closeSginDialog"]);
+
+const message = createDiscreteApi(["message"]);
+
+const formRef = ref();
+const submitLoading = ref(false);
+const form = reactive<object>({
+  email: "",
+});
+
+const rules: FormRules = {
+  email: [
+    {
+      required: true,
+      message: "请输入邮箱",
+      trigger: ["input", "blur"],
+    },
+    {
+      required: true,
+      validator: (rule: FormItemRule, value: string): boolean => {
+        return value && isEmail(value);
+      },
+      message: "请输入正确格式邮箱",
+      trigger: ["blur"],
+    },
+  ],
+};
+
+const emailSent = ref<boolean>(false);
+
+const handleSend = () => {
+  formRef.value?.validate(async (errors: any) => {
+    if (!errors) {
+      submitLoading.value = true;
+      const { code } = await email_Api(form);
+      if (code === 200) {
+        message.message.success("发送成功");
+        emailSent.value = true;
+      }
+      submitLoading.value = false;
+    }
+  });
+};
+
+const handleSure = () => {
+  emit("closeSginDialog", "success");
+};
+</script>
+
+<style lang="scss" scoped>
+.container {
+  width: 100%;
+  padding: 30px 20px;
+
+  .login-btn {
+    display: block;
+    width: 50%;
+    height: 50px;
+    color: #fff;
+    font-size: 18px;
+    border-radius: 8px;
+    margin: 0 auto;
+
+    :deep(.n-button__content) {
+      display: block;
+    }
+  }
+
+  .tip {
+    font-size: 16px;
+    line-height: 32px;
+  }
+}
+</style>

+ 169 - 0
components/Login/index.vue

@@ -0,0 +1,169 @@
+<template>
+  <div class="container">
+    <n-form ref="formRef" label-placement="left" :label-width="100" :model="form" :rules="rules" size="large" require-mark-placement="left">
+      <n-form-item path="loginCode">
+        <n-input v-model:value="form.loginCode" :placeholder="t('common.login.usernameTip')">
+          <template #prefix>
+            <span class="bqfl-iconfont icon-yonghuming"></span>
+          </template>
+        </n-input>
+      </n-form-item>
+      <n-form-item path="password">
+        <n-input v-model:value="form.password" type="password" show-password-on="click" :placeholder="t('common.login.passwordTip')">
+          <template #prefix>
+            <span class="bqfl-iconfont icon-mima"></span> </template></n-input>
+        <a href="#" class="forgot" @click="handleForgot"> {{t('common.login.forgetPassword')}}</a>
+      </n-form-item>
+      <n-form-item path="validCode">
+        <n-input v-model:value="form.validCode" :placeholder="t('common.login.validCodeTip')">
+          <template #prefix>
+            <span class="bqfl-iconfont icon-yanzhengma"></span>
+          </template>
+          <template #suffix>
+            <img :src="getValidCodeImg" @click="refreshValidCodeImg" class="cursor-pointer" width="100" />
+          </template>
+        </n-input>
+      </n-form-item>
+    </n-form>
+    <n-button class="login-btn" attr-type="button" type="info" color="#18A058" :disabled="submitLoading" @click="handleLogin">
+      {{ submitLoading ? t('common.login.submitting') : t('common.login.title') }}
+    </n-button>
+    <p class="register-btn">
+      {{t('common.login.noAccount')}}
+      <span @click="handleRegister">{{t('common.login.register')}}</span>
+    </p>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import { useI18n } from "#imports";
+import { ref, reactive, onMounted } from "vue";
+import { createDiscreteApi } from "naive-ui";
+import { useUserStore } from "@/store/user";
+const config = useRuntimeConfig();
+const baseUrl = ref(config.public.apiBase);
+const emit = defineEmits(["closeSginDialog"]);
+const userStore = useUserStore();
+const { t, locale, setLocale } = useI18n();
+const temptime = ref();
+
+const formRef = ref();
+const submitLoading = ref(false);
+
+const form: Login = reactive({
+  loginCode: "",
+  password: "",
+});
+
+const rules: FormRules = {
+  loginCode: [
+    {
+      required: true,
+      message: t("common.login.usernameTip"),
+      trigger: ["input", "blur"],
+    },
+  ],
+  password: [
+    {
+      required: true,
+      message: t("common.login.passwordTip"),
+      trigger: ["input", "blur"],
+    },
+  ],
+  validCode: [
+    {
+      required: true,
+      message: t("common.login.validCodeTip"),
+      trigger: ["input", "blur"],
+    },
+  ],
+};
+
+const getValidCodeImg = ref<string>("");
+
+const refreshValidCodeImg = () => {
+  temptime.value = +new Date().getTime();
+  getValidCodeImg.value =
+    baseUrl.value + `/report/websiteUser/getValidCode?unTime=${temptime.value}`;
+};
+
+refreshValidCodeImg();
+
+const message = createDiscreteApi(["message"]);
+
+const handleLogin = () => {
+  formRef.value?.validate(async (errors: any) => {
+    if (!errors) {
+      submitLoading.value = true;
+      const params = JSON.parse(JSON.stringify(form));
+      params.loginCode = encryptByBase64(params.loginCode);
+      params.password = encryptByBase64(params.password);
+      params.unTime = temptime.value;
+      try {
+        const { code, data } = await login_Api(params);
+        if (code === 200) {
+          userStore.setToken(data?.token);
+          userStore.setUserInfo(data?.user);
+          emit("closeSginDialog", "success");
+          message.message.success(t("common.login.success"));
+        } else {
+          refreshValidCodeImg();
+          form.validCode = "";
+        }
+        submitLoading.value = false;
+      } catch (error) {
+        submitLoading.value = false;
+      }
+    }
+  });
+};
+
+const handleForgot = () => {
+  emit("closeSginDialog", "forgot");
+};
+
+const handleRegister = () => {
+  emit("closeSginDialog", "register");
+};
+</script>
+
+<style lang="scss" scoped>
+.container {
+  width: 100%;
+  padding: 30px 20px;
+
+  .login-btn {
+    width: 100%;
+    height: 50px;
+    color: #fff;
+    font-size: 18px;
+    border-radius: 8px;
+  }
+  .register-btn {
+    font-size: 18px;
+    color: #808080;
+    text-align: center;
+
+    span {
+      color: #f0a226;
+      cursor: pointer;
+    }
+  }
+
+  .forgot {
+    margin-left: 10px;
+    font-size: 14px;
+    color: #00c2ff;
+    text-decoration: underline;
+    flex-shrink: 0;
+  }
+
+  .cursor-pointer {
+    cursor: pointer;
+  }
+
+  :deep(input::-ms-reveal) {
+    display: none;
+  }
+}
+</style>

+ 214 - 0
components/Login/register.vue

@@ -0,0 +1,214 @@
+<template>
+  <div class="container">
+    <n-form ref="formRef" label-placement="left" :label-width="160" :model="form" :rules="rules" size="large" require-mark-placement="left">
+      <n-form-item :label="t('common.register.username')+':'" path="loginCode">
+        <n-input v-model:value="form.loginCode" :placeholder="t('common.register.usernameTip')" />
+      </n-form-item>
+      <n-form-item :label="t('common.register.password')+':'" path="password">
+        <n-input v-model:value="form.password" @input="handlePasswordInput" type="password" show-password-on="click" :placeholder="t('common.register.passwordTip')" />
+      </n-form-item>
+      <n-form-item :label="t('common.register.confirmPassword')+':'" path="confirmNewPassword" ref="confirmPasswordRef">
+        <n-input v-model:value="form.confirmNewPassword" type="password" show-password-on="click" :placeholder="t('common.register.confirmPasswordTip')" />
+      </n-form-item>
+      <n-form-item :label="t('common.register.companyName')+':'" path="companyName">
+        <n-input v-model:value="form.companyName" :placeholder="t('common.register.companyNameTip')" />
+      </n-form-item>
+      <n-form-item :label="t('common.register.companyPosition')+':'" path="addr">
+        <n-input v-model:value="form.addr" :placeholder="t('common.register.positionTip')" />
+      </n-form-item>
+      <n-form-item :label="t('common.register.email')+':'" path="email">
+        <n-input v-model:value="form.email" :placeholder="t('common.register.emailTip')" />
+      </n-form-item>
+      <n-form-item :label="t('common.register.contactPerson')+':'" path="name">
+        <n-input v-model:value="form.name" :placeholder="t('common.register.contactPersonTip')" />
+      </n-form-item>
+      <n-form-item :label="t('common.register.phone')+':'" path="contact">
+        <n-input v-model:value="form.contact" :placeholder="t('common.register.phoneTip')" />
+      </n-form-item>
+      <!-- <n-form-item label="描述:" path="demand">
+        <n-input
+          v-model:value="form.demand"
+          type="textarea"
+          placeholder="请输入描述"
+        />
+      </n-form-item> -->
+    </n-form>
+    <n-button class="login-btn" attr-type="button" type="info" color="#18A058" :disabled="submitLoading" @click="handleRegister">
+      {{ submitLoading ? t('common.register.submitting') : t('common.register.submit') }}
+    </n-button>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import { useI18n } from "#imports";
+import { createDiscreteApi } from "naive-ui";
+import { ref, reactive } from "vue";
+import { useUserStore } from "@/store/user";
+
+const emit = defineEmits(["closeSginDialog"]);
+const userStore = useUserStore();
+const { t, locale, setLocale } = useI18n();
+const message = createDiscreteApi(["message"]);
+
+const formRef = ref();
+const submitLoading = ref(false);
+const confirmPasswordRef = ref();
+
+const form: register = reactive({
+  loginCode: "",
+  password: "",
+  confirmNewPassword: "",
+  name: "",
+  contact: "",
+  email: "",
+  companyName: "",
+  // demand: "",
+  addr: "",
+});
+
+const confirmPasswordValidate = (
+  rule: FormItemRule,
+  value: string
+): boolean => {
+  return value && value == form.password;
+};
+
+const rules: FormRules = {
+  loginCode: [
+    {
+      required: true,
+      message: t("common.register.usernameTip"),
+      trigger: ["input", "blur"],
+    },
+  ],
+  password: [
+    {
+      required: true,
+      message: t("common.register.passwordTip"),
+      trigger: ["input", "blur", "password-input"],
+    },
+  ],
+  confirmNewPassword: [
+    {
+      required: true,
+      message: t("common.register.confirmPasswordTip"),
+      trigger: ["input", "blur"],
+    },
+    {
+      required: true,
+      validator: confirmPasswordValidate,
+      message: t("common.register.passwordDoNotMatch"),
+      trigger: ["blur", "password-input"],
+    },
+  ],
+  email: [
+    {
+      required: true,
+      message: t("common.register.emailTip"),
+      trigger: ["input", "blur"],
+    },
+    {
+      required: true,
+      validator: (rule: FormItemRule, value: string): boolean => {
+        return value && isEmail(value);
+      },
+      message: t("common.register.emailTip1"),
+      trigger: ["blur"],
+    },
+  ],
+  name: [
+    {
+      required: true,
+      message: t("common.register.contactPersonTip"),
+      trigger: ["input", "blur"],
+    },
+  ],
+  contact: [
+    {
+      required: true,
+      message: t("common.register.phoneTip"),
+      trigger: ["input", "blur"],
+    },
+    {
+      required: true,
+      validator: (rule: FormItemRule, value: string): boolean => {
+        return value && isPhoneNumber(value);
+      },
+      message: t("common.register.phoneFormat"),
+      trigger: ["blur", "change"],
+    },
+  ],
+  companyName: [
+    {
+      required: true,
+      message: t("common.register.companyNameTip"),
+      trigger: ["input", "blur"],
+    },
+  ],
+  // addr: [
+  //   {
+  //     required: true,
+  //     message: "请输入公司职务",
+  //     trigger: ["input", "blur"],
+  //   },
+  // ],
+  // demand: [
+  //   {
+  //     required: true,
+  //     message: "请输入需求",
+  //     trigger: ["input", "blur"],
+  //   },
+  // ],
+};
+
+const handlePasswordInput = () => {
+  if (form.confirmPassword) {
+    confirmPasswordRef.value?.validate({ trigger: "password-input" });
+  }
+};
+
+const handleRegister = () => {
+  formRef.value?.validate(async (errors: any) => {
+    if (!errors) {
+      submitLoading.value = true;
+      const params = JSON.parse(JSON.stringify(form));
+      params.loginCode = encryptByBase64(params.loginCode);
+      params.password = encryptByBase64(params.password);
+      params.confirmNewPassword = encryptByBase64(params.confirmNewPassword);
+      const { code, data } = await register_Api(params);
+      if (code === 200) {
+        userStore.setToken(data?.token);
+        userStore.setUserInfo(data?.user);
+        message.message.success(t("common.register.success"));
+        emit("closeSginDialog", "success");
+      }
+      submitLoading.value = false;
+    }
+  });
+};
+</script>
+
+<style lang="scss" scoped>
+.container {
+  width: 100%;
+  padding: 30px 20px;
+
+  .login-btn {
+    display: block;
+    width: 50%;
+    height: 50px;
+    color: #fff;
+    font-size: 18px;
+    border-radius: 8px;
+    margin: 0 auto;
+
+    :deep(.n-button__content) {
+      display: block;
+    }
+  }
+
+  :deep(input::-ms-reveal) {
+    display: none;
+  }
+}
+</style>

+ 117 - 0
components/Login/updatePwd.vue

@@ -0,0 +1,117 @@
+<template>
+  <div class="container">
+    <n-form ref="formRef" label-placement="left" :label-width="100" :model="form" :rules="rules" size="large" require-mark-placement="left">
+      <n-form-item label="密码:" path="newPassword">
+        <n-input v-model:value="form.newPassword" @input="handlePasswordInput" type="password" show-password-on="click" placeholder="请输入密码" />
+      </n-form-item>
+      <n-form-item label="确认密码:" path="confirmNewPassword" ref="confirmPasswordRef">
+        <n-input v-model:value="form.confirmNewPassword" type="password" show-password-on="click" placeholder="请输入确认密码" />
+      </n-form-item>
+    </n-form>
+    <n-button class="login-btn" attr-type="button" type="info" color="#18A058" :disabled="submitLoading" @click="handleSure">
+      {{ submitLoading ? "提交中..." : "确认" }}
+    </n-button>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import {
+  NForm,
+  NFormItem,
+  NInput,
+  NButton,
+  FormRules,
+  FormItemRule,
+  createDiscreteApi,
+} from "naive-ui";
+import { ref, reactive } from "vue";
+
+const emit = defineEmits(["closeSginDialog"]);
+
+const message = createDiscreteApi(["message"]);
+
+const formRef = ref();
+const submitLoading = ref(false);
+const confirmPasswordRef = ref();
+
+const form = reactive({
+  newPassword: "",
+  confirmNewPassword: "",
+});
+
+const confirmPasswordValidate = (
+  rule: FormItemRule,
+  value: string
+): boolean => {
+  return value && value == form.newPassword;
+};
+
+const rules: FormRules = {
+  newPassword: [
+    {
+      required: true,
+      message: "请输入密码",
+      trigger: ["input", "blur", "password-input"],
+    },
+  ],
+  confirmNewPassword: [
+    {
+      required: true,
+      message: "请输入密码",
+      trigger: ["input", "blur"],
+    },
+    {
+      required: true,
+      validator: confirmPasswordValidate,
+      message: "两次密码输入不一致",
+      trigger: ["blur", "password-input"],
+    },
+  ],
+};
+
+const handlePasswordInput = () => {
+  if (form.confirmNewPassword) {
+    confirmPasswordRef.value?.validate({ trigger: "password-input" });
+  }
+};
+
+const handleSure = () => {
+  formRef.value?.validate(async (errors: any) => {
+    if (!errors) {
+      submitLoading.value = true;
+      const params = JSON.parse(JSON.stringify(form));
+      params.newPassword = encryptByBase64(params.newPassword);
+      params.confirmNewPassword = encryptByBase64(params.confirmNewPassword);
+      try {
+        const { code } = await updatePwd_Api(params);
+        if (code === 200) {
+          message.message.success("修改成功");
+          emit("closeSginDialog", "success");
+        }
+      } catch (error) {}
+      submitLoading.value = false;
+    }
+  });
+};
+</script>
+
+<style lang="scss" scoped>
+.container {
+  width: 100%;
+  padding: 30px 20px;
+
+  .login-btn {
+    display: block;
+    width: 50%;
+    height: 50px;
+    color: #fff;
+    font-size: 18px;
+    border-radius: 8px;
+    margin: 0 auto;
+
+    :deep(.n-button__content) {
+      display: block;
+    }
+  }
+}
+</style>

+ 34 - 0
components/PdfPreview/index.vue

@@ -0,0 +1,34 @@
+<template>
+  <vue-pdf-app :pdf="src"></vue-pdf-app>
+</template>
+<script lang="ts" setup>
+import { defineAsyncComponent } from "vue";
+// import VuePdfApp from "vue3-pdf-app";
+import "vue3-pdf-app/dist/icons/main.css";
+const props = defineProps({
+  src: {
+    type: String,
+    default: "",
+  },
+});
+const VuePdfApp = defineAsyncComponent(() => import("vue3-pdf-app"));
+</script>
+<style lang="scss" scoped>
+#vuePdfApp {
+  :deep(.toolbar) {
+    z-index: 3;
+  }
+
+  :deep(#sidebarContainer) {
+    z-index: 3;
+  }
+
+  :deep(#errorWrapper) {
+    z-index: 3;
+  }
+
+  :deep(#thumbnailView) {
+    width: 100%;
+  }
+}
+</style>

+ 176 - 0
composables/api.ts

@@ -0,0 +1,176 @@
+import httpRequest from '@/composables/useHttp'
+
+// 分类详情
+export function articleCategoryDetails (markCode: string) {
+  return httpRequest.get(`/api/cms/articleCategory/markCode/${markCode}`)
+}
+
+// 研究简报
+// 研究报告字典
+export function dictListData (data: any) {
+  return httpRequest.post(`/basedata/dict/listData`, data)
+}
+export const researchReportListData = (data: any) => {
+  return httpRequest.post('/report/researchReport/pageList', data);
+}
+// 研究报告列表
+export function researchReportPageList (data: any) {
+  return httpRequest.post(`/report/researchReport/pageList`, data)
+}
+// 研究报告详情
+export function researchReportForm (param: any) {
+  return httpRequest.get(`/report/researchReport/form`, { params: param })
+}
+
+export function configList (param: any) {
+  return httpRequest.get(`/basedata/config/findListByKeys`, { params: param })
+}
+
+export function customerInfoSave (data: any) {
+  return httpRequest.post(`/basedata/customerInfo/save`, data)
+}
+
+//行研简报
+// 行研简报类别
+export function bulletinCategory_Api (data: any) {
+  return httpRequest.post(`/report/researchBriefReport/categoryList`, data)
+}
+//  查询全部分类
+export function allClassfiy_Api (data: any) {
+  return httpRequest.post(`/report/researchBriefReport/categoryPage`, data)
+}
+
+export function bulletinPage_Api (data: any) {
+  return httpRequest.post(`/report/researchBriefReport/pageList`, data)
+}
+// 简报精选简报
+export function bulletinWinnow_Api (param: any) {
+  return httpRequest.get(`/report/researchBriefReport/choicenessReports`, { params: param })
+}
+// 简报热门报告
+export function bulletinHots_Api (param: any) {
+  return httpRequest.get(`/report/researchBriefReport/hotReports`, { params: param })
+}
+
+// 简报详情
+export const bulletinDetail_Api = (param: any) => {
+  return httpRequest.get(`/report/researchBriefReport/form`, { params: param })
+};
+// 登录
+export const login_Api = (data: any) => {
+  return httpRequest.post(`/report/websiteUser/login`, data)
+};
+// 注册
+export const register_Api = (data: any) => {
+  return httpRequest.post(`/report/websiteUser/register`, data)
+};
+
+// 下载文件
+export const getDownUrl_Api = (data: any) => {
+  return httpRequest.post(`/report/websiteUserOrder/downFile`, data)
+};
+
+// 支付宝pc端预支付 响应地址
+export const getPayOrder_Api = (data: any) => {
+  return httpRequest.post(`/report/websiteUserOrder/payOrder`, data)
+};
+
+// // 简报支付详情
+export const bulletinDetailPay_Api = (data: any) => {
+  return httpRequest.post(`/report/websiteUserOrder/orderDetail`, data)
+};
+
+// 列表
+export const carouselListData = (param: any) => {
+  return httpRequest.get(`/basedata/carousel/listData`, { params: param || {} })
+};
+// export const carouselListData = async (param: any): Promise<string> => {
+//   return await httpRequest.get('/basedata/carousel/listData', { params: param })
+// }
+
+// 登出
+export const logOut_Api = (param: any) => {
+  return httpRequest.get(`/report/websiteUser/logout`, { params: param || {} })
+};
+// 用户-找回密码的邮件
+export const email_Api = (data: any) => {
+  return httpRequest.post(`/report/websiteUser/getWuValidCode`, data)
+};
+// 研究报告和咨询
+export const reportAndMarketListData = (param: any) => {
+  return httpRequest.get(`/basedata/home/mapData`, { params: param || {} })
+};
+
+// 热门报告
+export const hotResearchList = (param: any) => {
+  return httpRequest.get(`/report/researchReport/hotReports`, { params: param || {} })
+};
+
+export const marketInfoPageList = (data: any) => {
+  return httpRequest.post(`/report/marketInfo/pageList`, data)
+};
+export const marketInfoDetailList = (param: any) => {
+  return httpRequest.get(`/report/marketInfo/detailList`, { params: param || {} })
+};
+// 修改信息
+export const updateInfo_Api = (data: any) => {
+  return httpRequest.post(`/report/websiteUser/updateUser`, data)
+};
+// 个人中心-我的信息
+export const getUser_Api = (param: any) => {
+  return httpRequest.get(`/report/websiteUser/getUserInfo`, { params: param || {} })
+};
+// 个人中心-我的订单
+export const getMyOrder_Api = (data: any) => {
+  return httpRequest.post(`/report/websiteUserOrder/orderList`, data)
+};
+// 个人中心 - 我的下载;
+export const getMyDoen_Api = (data: any) => {
+  return httpRequest.post(`/report/websiteUserOrder/downList`, data)
+};
+
+// 支付取消
+export const closePay_Api = (data: any) => {
+  return httpRequest.post("report/websiteUserOrder/cancelOrder", data);
+};
+// 修改密码
+export const updatePwd_Api = (data: any) => {
+  return httpRequest.post("/report/websiteUser/updatePwd", data);
+};
+
+// 发布日期
+export const researchReportPublishDateList = (param: any) => {
+  return httpRequest.post("/report/researchReport/publishDateList", { params: param || {} });
+};
+// 类别
+export const researchReportCategoryList = (data: any) => {
+  return httpRequest.post('/report/researchReport/categoryList', data);
+}
+// 分类详情
+export const articleCategoryDetails1 = (markCode: string) => {
+  return httpRequest.get(`/api/cms/articleCategory/markCode/${markCode}`, { params: {} })
+}
+// 用户-找回密码
+export const forget_Api = (data: string) => {
+  return httpRequest.post("/report/websiteUser/forgetPwd", data);
+}
+// 创建支付订单  英文支付
+export const createOrder_Api = (data: any) => {
+  return httpRequest.post("/api/report/websiteUserResearchOrder/payOrder", data);
+};
+// 执行扣款
+export const payOrder_Api = (data: any) => {
+  return httpRequest.post("/api/report/websiteUserResearchOrder/pay/asyncNotify", data);
+};
+// 个人中心-我的订单  英文
+export const websiteUserResearchOrderList = (data: any) => {
+  return httpRequest.post("/api/report/websiteUserResearchOrder/orderList", data);
+};
+// 取消订单
+export const websiteUserResearchCancelOrder = (data: any) => {
+  return httpRequest.post("/api/report/websiteUserResearchOrder/cancelOrder", data);
+};
+// 更换邮箱 英文支付
+export const websiteUserResearchOrderUpdateEmail = (data: any) => {
+  return httpRequest.post("/api/report/websiteUserResearchOrder/updateEmail", data);
+};

+ 26 - 0
composables/cache.ts

@@ -0,0 +1,26 @@
+
+// const lang = getLangStorage('lang');
+import { useUserStore } from '@/store/user';
+// 报告分类
+export const getLocalSessionReport = async () => {
+    let userStore = useUserStore()
+    if ('zh-CN' === userStore.getLang) {
+        let ret = await dictListData({ 'dictType': 'reports_category' });
+        return ret;
+    } else {
+        let ret = await dictListData({ 'dictType': 'reports_category_en' });
+        return ret;
+    }
+}
+
+// 市场资讯
+export const getLocalSessionNews = async () => {
+    let userStore = useUserStore()
+    if ('zh-CN' == userStore.getLang) {
+        let ret = await dictListData({ 'dictType': 'market_type' });
+        return ret;
+    } else {
+        let ret = await dictListData({ 'dictType': 'market_type_en' });
+        return ret;
+    }
+}

+ 55 - 0
composables/cipher.ts

@@ -0,0 +1,55 @@
+import { encrypt, decrypt } from "crypto-js/aes";
+import { parse } from "crypto-js/enc-utf8";
+import pkcs7 from "crypto-js/pad-pkcs7";
+import ECB from "crypto-js/mode-ecb";
+import md5 from "crypto-js/md5";
+import UTF8 from "crypto-js/enc-utf8";
+import Base64 from "crypto-js/enc-base64";
+
+export interface EncryptionParams {
+  key: string;
+  iv: string;
+}
+
+export class AesEncryption {
+  private key;
+  private iv;
+
+  constructor(opt: Partial<EncryptionParams> = {}) {
+    const { key, iv } = opt;
+    if (key) {
+      this.key = parse(key);
+    }
+    if (iv) {
+      this.iv = parse(iv);
+    }
+  }
+
+  get getOptions() {
+    return {
+      mode: ECB,
+      padding: pkcs7,
+      iv: this.iv,
+    };
+  }
+
+  encryptByAES(cipherText: string) {
+    return encrypt(cipherText, this.key, this.getOptions).toString();
+  }
+
+  decryptByAES(cipherText: string) {
+    return decrypt(cipherText, this.key, this.getOptions).toString(UTF8);
+  }
+}
+
+export function encryptByBase64(cipherText: string) {
+  return UTF8.parse(cipherText).toString(Base64);
+}
+
+export function decodeByBase64(cipherText: string) {
+  return Base64.parse(cipherText).toString(UTF8);
+}
+
+export function encryptByMd5(password: string) {
+  return md5(password).toString();
+}

+ 106 - 0
composables/index.ts

@@ -0,0 +1,106 @@
+// 下载
+export function downloadFile (url) {
+  if (typeof window !== "undefined") {
+    const userAgent = navigator.userAgent;
+    if (userAgent.indexOf("Android") > -1) {
+      window.open(url);
+    } else if (
+      userAgent.indexOf("iPhone") > -1 ||
+      userAgent.indexOf("iPad") > -1 ||
+      userAgent.indexOf("iPod") > -1
+    ) {
+      location.href = url;
+    } else {
+      window.open(url);
+    }
+  }
+}
+// js日期格式化(xxxx年xx月xx日)
+export function dateFomat (dateStr: String) {
+  var result = "";
+  if (dateStr != "") {
+    var contractdate = new Date(dateStr);
+    var contract_year = contractdate.getFullYear();
+    var contract_month = (contractdate.getMonth() + 1) < 10 ? '0' + (contractdate.getMonth() + 1) : (contractdate.getMonth() + 1);
+    var contract_day = contractdate.getDate() < 10 ? '0' + contractdate.getDate() : contractdate.getDate();
+    result = contract_year + '年' + contract_month + '月' + contract_day + '日';
+  }
+  return result;
+}
+
+// 获取标签中内容
+export function getStringContent (html: String, tagName: String) {
+  const regex = new RegExp(`<${tagName}.*?>(.*?)</${tagName}>`, 's'); // 's' 允许 . 匹配换行符
+  const match = html.match(regex);
+  return match ? match[1] : null;
+}
+export function getCookieLang () {
+  let lang = useCookie('i18n_redirected').value;
+  return lang
+}
+
+/**
+ * 日期时间格式化未字符串输出
+ * @param {(Date | string)} date
+ * @param {string} format
+ */
+export function formatDate (date: Date | string | number, format: string) {
+  if (!date) {
+    return "";
+  }
+  try {
+    const value: Date = new Date(date);
+    const yyyy = value.getFullYear();
+    const M = value.getMonth() + 1;
+    const MM =
+      value.getMonth() + 1 >= 10
+        ? value.getMonth() + 1
+        : "0" + (value.getMonth() + 1);
+    const d = value.getDate();
+    const dd = value.getDate() >= 10 ? value.getDate() : "0" + value.getDate();
+    const H = value.getHours();
+    const HH =
+      value.getHours() >= 10 ? value.getHours() : "0" + value.getHours();
+    const m = value.getMinutes();
+    const mm =
+      value.getMinutes() >= 10 ? value.getMinutes() : "0" + value.getMinutes();
+    const s = value.getSeconds();
+    const ss =
+      value.getSeconds() >= 10 ? value.getSeconds() : "0" + value.getSeconds();
+    if (format === "yyyy") {
+      return yyyy;
+    } else if (format === "yyyy-MM") {
+      return yyyy + "-" + MM;
+    } else if (format === "yyyy-M") {
+      return yyyy + "-" + M;
+    } else if (format === "yyyy-MM-dd") {
+      return yyyy + "-" + MM + "-" + dd;
+    } else if (format === "yyyy-M-d") {
+      return yyyy + "-" + M + "-" + d;
+    } else if (format === "HH:mm:ss") {
+      return HH + ":" + mm + ":" + ss;
+    } else if (format === "H:m:s") {
+      return H + ":" + m + ":" + s;
+    } else if (format === "HH:mm") {
+      return HH + ":" + mm;
+    } else if (format === "H:m") {
+      return H + ":" + m;
+    } else if (format === "yyyy-MM-dd HH:mm") {
+      return yyyy + "-" + MM + "-" + dd + " " + HH + ":" + mm;
+    } else if (format === "yyyy-MM-dd HH:mm:ss") {
+      return yyyy + "-" + MM + "-" + dd + " " + HH + ":" + mm + ":" + ss;
+    } else {
+      return yyyy + "-" + MM + "-" + dd;
+    }
+  } catch (error) {
+    return "";
+  }
+}
+
+// js日期格式化(May 10, 2025)
+export function formatDateEn (inputDate: any) {
+  let options = { year: 'numeric', month: 'long', day: 'numeric' };
+  let date = new Date(inputDate);
+  return date.toLocaleDateString('en-US', options); // 输出格式:May 10, 2025
+}
+

+ 34 - 0
composables/storage.ts

@@ -0,0 +1,34 @@
+// storage.ts
+/**
+ * 存储数据
+ */
+export const setItem = (key: string, value: any) => {
+  value = JSON.stringify(value);
+  localStorage.setItem(key, value);
+};
+
+/**
+ * 获取数据
+ */
+export const getItem = (key: string) => {
+  try {
+    let data = localStorage.getItem(key);
+    return JSON.parse(data);
+  } catch (err) {
+    return null;
+  }
+};
+
+/**
+ * 删除指定数据
+ */
+export const removeItem = (key: string) => {
+  localStorage.removeItem(key);
+};
+
+/**
+ * 删除所有数据
+ */
+export const removeAllItem = () => {
+  localStorage.clear();
+};

+ 92 - 0
composables/useHttp.ts

@@ -0,0 +1,92 @@
+import { UseFetchOptions } from "nuxt/app";
+import { createDiscreteApi } from "naive-ui";
+import { useUserStore } from "~/store/user";
+const NMessage = createDiscreteApi(["message"]);
+type Methods = "GET" | "POST";
+// const BASE_URL = 'http://192.168.0.68:8390/';
+// const BASE_URL = process.env.NUXT_PUBLIC_API_BASE
+
+export interface IResultData<T> {
+  code: number;
+  data: T;
+  msg: string;
+}
+
+class HttpRequest {
+  request<T = any> (
+    url: string,
+    method: Methods,
+    data = {},
+    options?: UseFetchOptions<T>
+  ) {
+    return new Promise((resolve, reject) => {
+      const config = useRuntimeConfig();
+      const userStore = useUserStore();
+      let baseURL = config.public.apiBase;
+      if (process.client && process.env.NODE_ENV == "development") {
+        baseURL = "/api";
+      }
+      const newOptions: UseFetchOptions<T> = {
+        baseURL: baseURL,
+        // baseURL: "https://bjfl.dirmarketresearch.com/js/a/api",
+        method: method,
+        key: url + JSON.stringify(data),
+        headers: {
+          'website-token': userStore.getToken || '',
+        },
+        ...options,
+      };
+
+      if (method === "GET") {
+        newOptions.key = url + JSON.stringify(data),
+          data.params.lang = userStore.getLang;
+        newOptions.params = data?.params;
+      }
+      if (method === "POST") {
+        data.lang = userStore.getLang;
+        newOptions.body = data
+      }
+      useFetch(url, newOptions)
+        .then((res: any) => {
+          if (!res.data.value?.code || res.data.value.code == 200) {
+            resolve(JSON.parse(JSON.stringify(res.data.value)));
+          } else {
+            reject(JSON.parse(JSON.stringify(res.data.value)));
+            switch (res.data.value.code) {
+              case -400:
+              case 5001:
+                const userStore = useUserStore();
+                NMessage.message.error("登录状态过期,请重新登陆");
+                userStore.resetState()
+                navigateTo(userStore.getLang=="en-US"?'/en':'/');
+                return;
+              case 500:
+              case 5002:
+                return NMessage.message.error(
+                  res.data.value.msg || "服务端错误"
+                );
+              default:
+                return NMessage.message.error(res.data.value.msg);
+            }
+          }
+        })
+        .catch((error) => {
+          reject(error);
+        });
+    });
+  }
+
+  // 封装常用方法
+
+  get<T = any> (url: string, params?: any, options?: UseFetchOptions<T>) {
+    return this.request(url, "GET", params, options);
+  }
+
+  post<T = any> (url: string, data: any, options?: UseFetchOptions<T>) {
+    return this.request(url, "POST", data, options);
+  }
+}
+
+const httpRequest = new HttpRequest();
+
+export default httpRequest;

+ 163 - 0
composables/validate.ts

@@ -0,0 +1,163 @@
+/**
+ * Created by PanJiaChen on 16/11/18.
+ */
+
+import { DictData } from "@/api/basedata/home"
+import { ResearchReport } from "@/api/report/report"
+import { ar } from "date-fns/locale"
+
+// 身份证校验
+export function isCardNo (card: string) {
+  const reg = /^[1-9][0-9]{5}([1][9][0-9]{2}|[2][0][0|1][0-9])([0][1-9]|[1][0|1|2])([0][1-9]|[1|2][0-9]|[3][0|1])[0-9]{3}([0-9]|[X])$/
+  return reg.test(card)
+}
+
+// 校验密码强度
+export function passwordLevel (value: string) {
+  let modes = 0
+  let level = 0
+  // 未输入
+  if (value.length < 1) {
+    level = 0
+  } else if (value.length >= 1 && value.length < 8) {
+    // 输入大于0小于8个字符
+    modes = 1
+  } else {
+    if (/\d/.test(value)) modes++ // 数字
+    if (/[a-z]/.test(value)) modes++ // 小写
+    if (/[A-Z]/.test(value)) modes++ // 大写
+    if (/\W/.test(value)) modes++ // 特殊字符
+  }
+  switch (modes) {
+    case 1:
+      level = 1
+      break
+    case 2:
+      level = 2
+      break
+    case 3:
+    case 4:
+      level = 3
+      break
+    default:
+      level = 0
+      break
+  }
+  return level
+}
+
+/**
+ * 手机号码校验
+ */
+export function isPhoneNumber (code: string) {
+  let result = false
+  // 手机
+  const mobile = /^0?(13|14|15|16|17|18|19)[0-9]{9}$/
+  // 固定电话
+  const tel = /^[0-9]{3,4}[-][0-9]{8}$/
+  const mbState = mobile.test(code)
+  const telState = tel.test(code)
+  if (mbState || telState) {
+    result = true
+  }
+  return result
+}
+
+/**
+ *
+ * @param code 邮箱校验
+ * @returns
+ */
+export function isEmail (code: string) {
+  let result = false;
+  const emailReg = /^([a-zA-Z\d][\w-]{2,})@(\w{2,})\.([a-z]{2,})(\.[a-z]{2,})?$/
+  if (emailReg.test(code)) {
+    result = true;
+  }
+  return result;
+}
+
+/**
+ * @param {string} path
+ * @returns {Boolean}
+ */
+export function isExternal (path: string): boolean {
+  return /^(https?:|mailto:|tel:)/.test(path)
+}
+
+/**
+ * @param {string} str
+ * @returns {Boolean}
+ */
+export function validUsername (str: string): boolean {
+  const validMap = ['admin', 'editor']
+  return validMap.indexOf(str.trim()) >= 0
+}
+
+/**
+ * @param {string} url
+ * @returns {Boolean}
+ */
+export function validURL (url: string): boolean {
+  const reg = /^(https?|ftp):\/\/([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&%$-]+)*@)*((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(:[0-9]+)*(\/($|[a-zA-Z0-9.,?'\\+&%$#=~_-]+))*$/
+  return reg.test(url)
+}
+
+/**
+ * @param {string} str
+ * @returns {Boolean}
+ */
+export function validLowerCase (str: string): boolean {
+  const reg = /^[a-z]+$/
+  return reg.test(str)
+}
+
+/**
+ * @param {string} str
+ * @returns {Boolean}
+ */
+export function validUpperCase (str: string): boolean {
+  const reg = /^[A-Z]+$/
+  return reg.test(str)
+}
+
+/**
+ * @param {string} str
+ * @returns {Boolean}
+ */
+export function validAlphabets (str: string): boolean {
+  const reg = /^[A-Za-z]+$/
+  return reg.test(str)
+}
+
+/**
+ *
+ * @returns { Boolean }
+ */
+export function isMobile (): boolean {
+  if (navigator.userAgent.match(/(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone)/i)) {
+    return true;
+  } else {
+    return false;
+  }
+}
+
+/**
+ * 包含项
+ * @param arr
+ * @param marketType
+ * @returns item
+ */
+export function containsType (arr: ResearchReport[], marketType: string) {
+  return arr.find(item => item.marketType === marketType);
+}
+
+/**
+ * 判断是否包含
+ * @param arr 数组对象
+ * @param marketType 值
+ * @returns true or false
+ */
+export function containsMarketType (arr: DictData[], marketType: string) {
+  return arr.some(item => item.dictValue === marketType);
+}

Файловите разлики са ограничени, защото са твърде много
+ 781 - 0
lang/en-us.ts


+ 13 - 0
lang/i18n.ts

@@ -0,0 +1,13 @@
+import zh from './zh-cn.js'//中文
+import en from './en-us.js'//英文
+
+const i18n = {
+  fallbackLocale: 'en',//回退策略,指定的locale中没有找到对应资源的情况下使用的locale
+  messages: { //要渲染的信息,有多少语言就添加多少种
+    en: en,
+    zh: zh
+  }
+
+}
+
+export default i18n

Файловите разлики са ограничени, защото са твърде много
+ 671 - 0
lang/zh-cn.ts


+ 126 - 0
nuxt.config.ts

@@ -0,0 +1,126 @@
+import AutoImport from 'unplugin-auto-import/vite'
+import Components from 'unplugin-vue-components/vite'
+import { NaiveUiResolver } from 'unplugin-vue-components/resolvers'
+import path from 'path'
+// https://nuxt.com/docs/api/configuration/nuxt-config
+
+export default defineNuxtConfig({
+  ssr: true,
+  app: {
+    head: {
+      title: '百谏方略',//网站地址栏位置的标题
+      meta: [
+        { charset: 'utf-8' },
+        { name: 'viewport', content: 'width=device-width, initial-scale=1' },
+        { name: 'format-detection', content: 'telephone=yes' },//添加移动端调用手机拨号功能
+        { name: 'viewport', content: 'width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=0' }
+      ],
+      link: [
+        // { rel: "resource", type: "application/110n", href: "/_nuxt/viewer.properties" }
+      ],
+      // <script type="text/javascript" src="/static/js/wow.js"></script>
+      script: [
+        // { type: 'text/javascript', src: "https://webapi.amap.com/maps?v=2.0&key=4784a307149172798267bd43856ba7b9" },
+        // { type: 'text/javascript', src: "wow.js" },
+      ]
+    },
+  },
+  nitro: {
+    // 代理
+    devProxy: {
+      '/api': {
+        target: 'https://www.dirmarketresearch.com/api',
+        // target: 'http://192.168.0.70:8980/js/a/api',
+        // target: 'http://192.168.0.132//js/a/api',
+        changeOrigin: true,
+        prependPath: true,
+        secure: false,
+      },
+    },
+  },
+  runtimeConfig: {
+    // apiSecret 只能在服务器端上访问
+    // apiSecret: '123',
+    // public 命名空间中定义的,在服务器端和客户端都可以普遍访问
+    public: {
+      apiBase: process.env.NUXT_PUBLIC_API_BASE,
+      baseUrl: process.env.NUXT_PUBLIC_API_BASEURL
+    }
+  },
+  devtools: { enabled: true },
+  devServer: {
+    host: "0.0.0.0"
+  },
+  plugins: [
+    { src: './public/wow.js', mode: 'client' },
+    // { src: './public/myWow.js', mode: 'client' },
+    '~/plugins/pinia.js',
+  ],
+  css: [
+    '@/assets/css/root.scss',
+    // 直接加载一个 Node.js 模块。(在这里它是一个 Sass 文件)
+    //   'bulma',
+    // 项目里要使用的 SCSS 文件
+    '@/assets/css/common.scss',
+    // 'animate.css',
+    // '@/assets/styles/animate.css',
+    'github-markdown-css/github-markdown.css'
+  ],
+
+  modules: ["nuxtjs-naive-ui", '@nuxtjs/i18n', '@pinia/nuxt'],
+  i18n: {
+    locales: ['en', 'zh'],
+    defaultLocale: 'en',
+    vueI18n: './lang/i18n.ts',
+    customRoutes: 'config',
+    // parsePages: false,
+    strategy: 'prefix_except_default',
+    detectBrowserLanguage: false, // 禁用浏览器检测
+    pages: {
+      // 'news/index': {
+      //   en: '/en/news-categories/:activeIndustryType?/:keyword?',
+      //   zh: '/news-categories/:activeIndustryType?/:keyword?'
+      // },
+      'report-industries/detail': {
+        zh: '/reports/[webTitle]',
+        en: '/reports/[webTitle]'
+      },
+      'news-categories/detail': {
+        zh: '/news/[webTitle]',
+        en: '/news/[webTitle]'
+      },
+      'contact/index': {
+        zh: '/contactUs',
+        en: '/contactUs'
+      }
+    }
+  },
+  vite: {
+    plugins: [
+      AutoImport({
+        imports: [
+          {
+            'naive-ui': [
+              'NCarousel',
+            ]
+          }
+        ]
+      }),
+      Components({
+        resolvers: [NaiveUiResolver()]
+      })
+    ],
+  },
+  build: {
+    transpile:
+      process.env.NODE_ENV === 'production'
+        ? [
+          'naive-ui',
+          'vueuc',
+          '@css-render/vue3-ssr',
+          '@juggle/resize-observer'
+        ]
+        : ['@juggle/resize-observer'],
+  },
+  compatibilityDate: '2025-02-20'
+})

+ 80 - 0
nuxt.config.ts.bak

@@ -0,0 +1,80 @@
+import AutoImport from 'unplugin-auto-import/vite'
+import Components from 'unplugin-vue-components/vite'
+import { NaiveUiResolver } from 'unplugin-vue-components/resolvers'
+// https://nuxt.com/docs/api/configuration/nuxt-config
+export default defineNuxtConfig({
+  ssr: false,
+  router: {
+    middleware: 'ssr' // 为所有页面添加SSR中间件
+  },
+  app: {
+    head: {
+      title: '问可汇',//网站地址栏位置的标题
+      meta: [
+        { charset: 'utf-8' },
+        { name: 'viewport', content: 'width=device-width, initial-scale=1' },
+        { name: 'format-detection', content: 'telephone=yes' },//添加移动端调用手机拨号功能
+        // { hid: 'description', name: 'description', content: 'My awesome app' }
+      ],
+      // link: [{ rel: 'icon', type: 'image/x-icon', href: '/logo.jpg' }],//地址栏网站icon,可以自定义修改
+      script: [
+        { type: 'text/javascript', src: "https://webapi.amap.com/maps?v=2.0&key=4784a307149172798267bd43856ba7b9" },
+      ]
+    },
+  },
+  runtimeConfig: {
+    // apiSecret 只能在服务器端上访问
+    // apiSecret: '123',
+    // public 命名空间中定义的,在服务器端和客户端都可以普遍访问
+    public: {
+      apiBase: process.env.NUXT_PUBLIC_API_BASE
+    }
+  },
+  devtools: { enabled: true },
+  devServer: {
+    host: "0.0.0.0"
+  },
+  plugins: [
+    { src: './public/myWow.js', mode: 'client' },
+  ],
+  css: [
+    '@/assets/css/root.scss',
+    // 直接加载一个 Node.js 模块。(在这里它是一个 Sass 文件)
+    //   'bulma',
+    // 项目里要使用的 SCSS 文件
+    '@/assets/css/common.scss',
+    'animate.css',
+    'github-markdown-css/github-markdown.css'
+  ],
+
+  modules: ["nuxtjs-naive-ui"],
+
+  vite: {
+    plugins: [
+      AutoImport({
+        imports: [
+          {
+            'naive-ui': [
+              'NCarousel',
+            ]
+          }
+        ]
+      }),
+      Components({
+        resolvers: [NaiveUiResolver()]
+      })
+    ]
+  },
+  build: {
+    transpile:
+      process.env.NODE_ENV === 'production'
+        ? [
+          'naive-ui',
+          'vueuc',
+          '@css-render/vue3-ssr',
+          '@juggle/resize-observer'
+        ]
+        : ['@juggle/resize-observer']
+  },
+  compatibilityDate: '2024-07-08'
+})

+ 0 - 0
package-lock.json


Някои файлове не бяха показани, защото твърде много файлове са промени