Ryuiso 7 månader sedan
förälder
incheckning
86de17ef97

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 236 - 489
web/package-lock.json


+ 3 - 1
web/package.json

@@ -13,6 +13,8 @@
     "format": "prettier --write src/"
   },
   "dependencies": {
+    "@element-plus/icons-vue": "^2.3.1",
+    "element-plus": "^2.9.8",
     "pinia": "^3.0.1",
     "vue": "^3.5.13",
     "vue-router": "^4.5.0"
@@ -31,7 +33,7 @@
     "prettier": "3.5.3",
     "sass": "^1.86.0",
     "typescript": "~5.8.0",
-    "vite": "^6.2.1",
+    "vite": "6.2.6",
     "vite-plugin-vue-devtools": "^7.7.2",
     "vue-tsc": "^2.2.8"
   }

+ 18 - 0
web/src/api/contact.ts

@@ -0,0 +1,18 @@
+
+import type { ContactFormData } from '@/types/contact'
+
+interface ContactFormResponse {
+  success: boolean
+}
+
+export const submitContactForm = async (data: ContactFormData): Promise<ContactFormResponse> => {
+  // 实际项目中这里调用邮件服务API
+  console.log('Form submitted:', data)
+
+  // 模拟API请求延迟
+  return new Promise((resolve) => {
+    setTimeout(() => {
+      resolve({ success: true })
+    }, 1000)
+  })
+}

+ 397 - 0
web/src/components/ContactForm/index.vue

@@ -0,0 +1,397 @@
+
+<script setup lang="ts">
+import { ref } from 'vue'
+import { submitContactForm } from '@/api/contact'
+
+const formData = ref({
+  name: '',
+  tel: '',
+  company: '',
+  industry: '',
+  city: '',
+  email: '',
+  message: '',
+  subscribe: false,
+  terms: false
+})
+
+const errors = ref<Record<string, string>>({})
+const isSubmitting = ref(false)
+
+const industries = [
+  { value: 'surveying', label: '测绘' },
+  { value: 'electricalnetwork', label: '电网' },
+  { value: 'energy', label: '新能源' },
+  { value: 'firefighting', label: '消防应急' },
+  { value: 'environment-protect', label: '环保' },
+  { value: 'traffic', label: '交通' },
+  { value: 'science-education', label: '科教' },
+  { value: 'forestry', label: '林业' },
+  { value: 'hydraulic', label: '水利' },
+  { value: 'judicial', label: '司法' },
+  { value: 'law-enforcement', label: '公共安全' },
+  { value: 'government', label: '政府单位' },
+  { value: 'engineering-construction', label: '工程建筑' },
+  { value: 'city-management', label: '城市管理执法' },
+  { value: 'other', label: '其他' }
+]
+
+const validateForm = () => {
+  errors.value = {}
+
+  if (!formData.value.name) errors.value.name = '请输入姓名'
+  if (!formData.value.tel) errors.value.tel = '请输入电话'
+  if (!formData.value.company) errors.value.company = '请输入公司信息'
+  if (!formData.value.industry) errors.value.industry = '请选择行业'
+  if (!formData.value.email) errors.value.email = '请输入邮箱'
+  if (!formData.value.terms) errors.value.terms = '请接受条款'
+
+  return Object.keys(errors.value).length === 0
+}
+
+const submitForm = async () => {
+  if (!validateForm()) return
+
+  isSubmitting.value = true
+
+  try {
+    const result = await submitContactForm(formData.value)
+
+    if (result.success) {
+      alert('提交成功!我们将尽快与您联系。')
+      // 重置表单
+      formData.value = {
+        name: '',
+        tel: '',
+        company: '',
+        industry: '',
+        city: '上海市 上海市',
+        email: '',
+        message: '',
+        subscribe: false,
+        terms: false
+      }
+    }
+  } catch (error) {
+    alert('提交失败,请稍后再试')
+    console.error(error)
+  } finally {
+    isSubmitting.value = false
+  }
+}
+</script>
+
+<template>
+  <section id="contact" class="contact-section">
+    <div class="text-box">
+      <h1 class="title">马上订购 —— One Myriad<span class="nowrap">开放平台</span></h1>
+      <p class="desc">
+        如需申请产品演示或软件试用,请留下您的联系信息,我们的专属顾问会在 3 个工作日内和您联系。
+      </p>
+    </div>
+
+    <div class="form-box">
+      <form @submit.prevent="submitForm" autocomplete="off">
+        <!-- 表单字段实现 -->
+        <div class="form-group">
+          <div class="form-item">
+            <input
+              v-model="formData.name"
+              type="text"
+              id="name"
+              required
+            >
+            <label for="name">姓名 *</label>
+          </div>
+          <div class="form-item">
+            <input
+              v-model="formData.tel"
+              type="tel"
+              id="tel"
+              required
+            >
+            <label for="tel">电话 *</label>
+          </div>
+        </div>
+
+        <div class="form-group">
+          <div class="form-item">
+            <input
+              v-model="formData.company"
+              type="text"
+              id="company"
+              required
+            >
+            <label for="company">公司 *</label>
+          </div>
+        </div>
+
+        <div class="form-group">
+          <div class="form-item">
+            <select
+              v-model="formData.industry"
+              id="industry"
+              required
+            >
+              <option value="" disabled selected>请选择行业</option>
+              <option
+                v-for="item in industries"
+                :key="item.value"
+                :value="item.value"
+              >
+                {{ item.label }}
+              </option>
+            </select>
+            <label for="industry">行业 *</label>
+          </div>
+          <div class="form-item">
+            <input
+              v-model="formData.city"
+              type="text"
+              id="city"
+              required
+            >
+            <label for="city">城市</label>
+          </div>
+        </div>
+
+        <div class="form-group">
+          <div class="form-item">
+            <input
+              v-model="formData.email"
+              type="email"
+              id="email"
+              required
+            >
+            <label for="email">邮箱 *</label>
+          </div>
+        </div>
+
+        <div class="form-group">
+          <div class="form-item">
+            <textarea
+              v-model="formData.message"
+              id="message"
+            ></textarea>
+            <label for="message">请输入您的需求...</label>
+          </div>
+        </div>
+
+        <div class="form-group checkbox-group">
+          <div class="checkbox-item">
+            <input
+              type="checkbox"
+              id="subscribe"
+              v-model="formData.subscribe"
+            >
+            <label for="subscribe">订阅产品资讯</label>
+          </div>
+          <!--<div class="checkbox-item">-->
+          <!--  <input-->
+          <!--    type="checkbox"-->
+          <!--    id="terms"-->
+          <!--    v-model="formData.terms"-->
+          <!--    required-->
+          <!--  >-->
+          <!--  <label for="terms">我已阅读并同意<a href="#">用户协议</a></label>-->
+          <!--</div>-->
+        </div>
+
+        <div class="form-group">
+          <button type="submit" class="submit-btn" :disabled="isSubmitting">
+            <span v-if="!isSubmitting">提交</span>
+            <span v-else>提交中...</span>
+          </button>
+        </div>
+      </form>
+    </div>
+  </section>
+</template>
+
+<style scoped lang="scss">
+.contact-section {
+  padding: 64px 0;
+  background-color: #f9fafb;
+
+  .text-box {
+    text-align: center;
+    margin-bottom: 40px;
+    max-width: 800px;
+    margin-left: auto;
+    margin-right: auto;
+
+    .title {
+      font-size: 32px;
+      margin-bottom: 16px;
+      color: #1f2937;
+      font-weight: 600;
+    }
+
+    .desc {
+      font-size: 16px;
+      color: #6b7280;
+      line-height: 1.6;
+    }
+  }
+
+  .form-box {
+    max-width: 700px;
+    margin: 0 auto;
+    background: white;
+    padding: 40px;
+    border-radius: 8px;
+    box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05);
+
+      .form-group {
+        margin-bottom: 24px;
+        display: flex;
+        flex-wrap: wrap;
+        gap: 16px;
+
+        .form-item {
+          position: relative;
+          margin-bottom: 16px;
+          flex: 1 1 calc(50% - 8px);
+
+          @media (max-width: 768px) {
+            flex: 1 1 100%;
+          }
+
+        &.has-error {
+          input, select, textarea {
+            border-color: #ef4444;
+          }
+        }
+
+        input, select, textarea {
+          width: 100%;
+          padding: 12px 16px;
+          border: 1px solid #dcdfe6;
+          border-radius: 4px;
+          font-size: 14px;
+          transition: border-color 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
+
+          &:focus {
+            outline: none;
+            border-color: #409eff;
+            box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.2);
+          }
+        }
+
+        input[type="email"] {
+          &:hover {
+            border-color: #c0c4cc;
+          }
+          &:focus {
+            border-color: #409eff;
+          }
+        }
+
+        select {
+          appearance: none;
+          background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cpolyline points='6 9 12 15 18 9'%3e%3c/polyline%3e%3c/svg%3e");
+          background-repeat: no-repeat;
+          background-position: right 12px center;
+          background-size: 16px;
+        }
+
+        textarea {
+          resize: vertical;
+          min-height: 100px;
+        }
+
+        label {
+          position: absolute;
+          left: 16px;
+          top: 12px;
+          color: #9ca3af;
+          font-size: 14px;
+          transition: all 0.2s;
+          pointer-events: none;
+          background: white;
+          padding: 0 4px;
+        }
+
+        input:focus + label,
+        input:not(:placeholder-shown) + label,
+        select:focus + label,
+        select:not([value=""]) + label,
+        textarea:focus + label,
+        textarea:not(:placeholder-shown) + label {
+          top: -8px;
+          left: 12px;
+          font-size: 12px;
+          color: #2563eb;
+          background: white;
+        }
+      }
+
+      &.checkbox-group {
+        margin-top: 32px;
+
+        .checkbox-item {
+          display: flex;
+          align-items: center;
+          margin-bottom: 12px;
+
+          input[type="checkbox"] {
+            width: auto;
+            margin-right: 8px;
+          }
+
+          label {
+            position: static;
+            color: #4b5563;
+            font-size: 14px;
+
+            a {
+              color: #2563eb;
+              text-decoration: none;
+
+              &:hover {
+                text-decoration: underline;
+              }
+            }
+          }
+
+          &.has-error label {
+            color: #ef4444;
+          }
+        }
+      }
+
+      .error-message {
+        color: #ef4444;
+        font-size: 12px;
+        margin-top: 4px;
+      }
+    }
+
+    .submit-btn {
+      width: 100%;
+      background: #2563eb;
+      color: white;
+      padding: 12px;
+      border: none;
+      border-radius: 6px;
+      font-size: 16px;
+      font-weight: 500;
+      cursor: pointer;
+      transition: background-color 0.2s;
+
+      &:hover {
+        background: #1d4ed8;
+      }
+
+      &:disabled {
+        background: #93c5fd;
+        cursor: not-allowed;
+      }
+    }
+  }
+}
+
+.nowrap {
+  white-space: nowrap;
+}
+</style>

+ 2 - 0
web/src/components/DocNavigation/index.vue

@@ -1,5 +1,7 @@
 
 <script setup lang="ts">
+import { ref } from 'vue'
+
 defineProps<{
   menus: Array<{
     title: string

+ 1 - 2
web/src/layout/Header/index.vue

@@ -1,4 +1,3 @@
-
 <script setup lang="ts">
 import { RouterLink, useRoute } from 'vue-router'
 import { navigation } from '@/router'
@@ -88,7 +87,7 @@ defineProps({
 
       <!-- 右侧操作区 -->
       <div class="action-section">
-        <a href="#" class="action-link">联系我们</a>
+        <RouterLink to="/contact" class="action-link">联系我们</RouterLink>
         <a href="#" class="action-button">预览平台</a>
       </div>
     </div>

+ 10 - 2
web/src/router/index.ts

@@ -3,6 +3,7 @@ import type { RouteRecordRaw } from 'vue-router'
 import HomeView from '@/views/Home/index.vue'
 import EnterpriseView from '@/views/Enterprise/index.vue'
 import DocView from '@/views/Doc/index.vue'
+import DownloadView from '@/views/Download/index.vue'
 
 export interface NavItem extends Omit<RouteRecordRaw, 'children'> {
   title?: string
@@ -197,7 +198,7 @@ const routes: NavItem[] = [
   {
     path: '/doc',
     name: 'doc',
-    component: () => import('@/views/Doc/index.vue'),
+    component: DocView,
     title: '文档中心',
     showInNav: true,
     meta: {
@@ -207,10 +208,17 @@ const routes: NavItem[] = [
   {
     path: '/download',
     name: 'download',
-    component: () => import('@/views/Download/index.vue'),
+    component: DownloadView,
     title: '下载中心',
     showInNav: true,
   },
+  {
+    path: '/contact',
+    name: 'contact',
+    component: () => import('@/components/ContactForm/index.vue'),
+    title: '联系我们',
+    showInNav: false,
+  }
 ]
 
 const router = createRouter({

+ 12 - 0
web/src/types/contact.ts

@@ -0,0 +1,12 @@
+
+export interface ContactFormData {
+  name: string
+  tel: string
+  company: string
+  industry: string
+  city: string
+  email: string
+  message: string
+  subscribe: boolean
+  terms: boolean
+}

+ 21 - 1
web/src/views/Doc/index.vue

@@ -1,5 +1,23 @@
 <script setup lang="ts">
-const docUrl = 'https://onemyriad.apifox.cn'
+import { onMounted, ref } from 'vue'
+import { useRoute } from 'vue-router'
+
+const route = useRoute()
+const docUrl = ref('https://onemyriad.apifox.cn')
+
+onMounted(() => {
+  // 处理直接URL访问的情况
+  if (route.query.url) {
+    try {
+      const url = new URL(route.query.url as string)
+      if (url.hostname.endsWith('apifox.cn')) {
+        docUrl.value = url.toString()
+      }
+    } catch (e) {
+      console.warn('Invalid URL parameter', e)
+    }
+  }
+})
 </script>
 
 <template>
@@ -10,6 +28,8 @@ const docUrl = 'https://onemyriad.apifox.cn'
         :src="docUrl"
         class="doc-iframe"
         allowfullscreen
+        sandbox="allow-same-origin allow-scripts allow-popups allow-forms"
+        referrerpolicy="no-referrer-when-downgrade"
       ></iframe>
     </main>
 

+ 2 - 2
web/src/views/Home/index.vue

@@ -25,8 +25,8 @@ const supportedProducts = ref([
       dark
       centered
       bgImage="//devcn.djicdn.com/img/6997ab1.png"
-      title="低门槛接入三方云平台"
-      subtitle="无需重复开发APP,即可让您的设备快速接入流媒体平台"
+      title="低门槛接入本地化部署的流媒体平台"
+      subtitle="无需重复开发APP,即可让您的设备快速接入安全自主的流媒体平台"
     />
 
     <!-- 核心理念 -->

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