Browse Source

feat: home content

Carson 11 months ago
parent
commit
e2ac795de2
4 changed files with 221 additions and 5 deletions
  1. 16 1
      README.md
  2. 62 2
      components/HomeContent.vue
  3. 139 0
      components/Search/index.vue
  4. 4 2
      pages/zh-HK/index.md

+ 16 - 1
README.md

@@ -12,6 +12,21 @@ npm run docs:dev
 
 
 把需要新增的文档放在 `pages/docs/` `pages/{语言}/docs/` 下,必须为 markdown 文档
 把需要新增的文档放在 `pages/docs/` `pages/{语言}/docs/` 下,必须为 markdown 文档
 
 
+## 组件库
+
+element-ui plus
+
 ## 样式定制
 ## 样式定制
 
 
-See https://vitepress.dev/zh/guide/extending-default-theme#customizing-css
+主要是[ vitepress ](https://vitepress.dev/zh/guide/extending-default-theme#customizing-css)和[ element-ui ](https://element-plus.org/zh-CN/guide/theming.html)的样式变量覆盖
+
+
+### icons
+
+使用[ unplugin-icons ](https://github.com/unplugin/unplugin-icons?tab=readme-ov-file)和[ iconify ](https://iconify.design/docs)
+
+#### 新增icon
+
+- 导出 figma svg,放在 assets/icons 目录下,是plain还是colored看情况而定
+  - plain: 单色图标,可以通过css color属性设置颜色
+  - colored: 双色图标,固定颜色,一般是多种颜色的图标

+ 62 - 2
components/HomeContent.vue

@@ -1,18 +1,78 @@
 <script setup lang="ts">
 <script setup lang="ts">
 import { ref } from "vue";
 import { ref } from "vue";
+import Search from "./Search/index.vue";
+import { ArrowRightBold } from "@element-plus/icons-vue";
 const count = ref(0);
 const count = ref(0);
 </script>
 </script>
 <template>
 <template>
   <div class="home-content">
   <div class="home-content">
-    <span>{{ count }}</span>
-    <button @click="count++">test</button>
+    <h1>您好,需要提供什么帮助?</h1>
+    <Search />
+    <h1>新手入门</h1>
+    <section>
+      <div class="card type1">
+        <h2>
+          平台概览<el-icon><ArrowRightBold /></el-icon>
+        </h2>
+        <span class="content">TODO</span>
+      </div>
+
+      <div class="card">
+        <h2>
+          平台概览<el-icon><ArrowRightBold /></el-icon>
+        </h2>
+        <span class="content">TODO</span>
+      </div>
+    </section>
   </div>
   </div>
 </template>
 </template>
 
 
 <style lang="scss" scoped>
 <style lang="scss" scoped>
+h2 {
+  border: none;
+  margin: 0;
+  padding: 0;
+}
 .home-content {
 .home-content {
   display: flex;
   display: flex;
   flex-direction: column;
   flex-direction: column;
   align-items: center;
   align-items: center;
+  section {
+    display: flex;
+    align-items: stretch;
+    justify-content: center;
+    width: 100%;
+    gap: 20px;
+  }
+  .card {
+    flex: 1;
+    display: flex;
+    flex-direction: column;
+    align-items: stretch;
+    gap: 8px;
+    border-radius: 20px;
+    padding: 32px;
+    background: #f5f9ff;
+    border: 1px solid #e2eeff;
+    min-height: 240px;
+    &.type1 {
+      background-color: #f6fdff;
+      border: 1px solid #e3f8f4;
+    }
+    h2 {
+      display: flex;
+      align-items: center;
+      .el-icon {
+        width: 24px;
+        height: 24px;
+        margin-left: 8px;
+        color: #00000042;
+      }
+    }
+    .content {
+      font-size: 14px;
+      font-weight: 400;
+    }
+  }
 }
 }
 </style>
 </style>

+ 139 - 0
components/Search/index.vue

@@ -0,0 +1,139 @@
+<script lang="ts">
+export default {
+  name: "Search",
+};
+</script>
+<script setup lang="ts">
+import { ref, computed, watch } from "vue";
+import { Search } from "@element-plus/icons-vue";
+import { watchDebounced, useFocus } from "@vueuse/core";
+import { useI18n } from "vue-i18n";
+import _ from "lodash";
+
+const { t } = useI18n();
+
+const input = ref("");
+const input$ = ref();
+const { focused } = useFocus(computed(() => input$.value?.input));
+const suggestions = ref<unknown[]>([]);
+const loading = ref(false);
+
+const suggestionVisible = computed(() => {
+  // TEST
+  // return true
+  const isValidData = suggestions.value.length > 0;
+  return focused.value && (isValidData || loading.value);
+});
+
+watch(
+  () => input.value,
+  (val) => {
+    console.log(input$.value);
+    loading.value = !!val;
+    if (!val) {
+      suggestions.value = [];
+    }
+  }
+);
+
+const fetchSuggestions = (mock) => {
+  return new Promise((resolve, reject) => {
+    setTimeout(() => {
+      // MOCK
+      // TODO should we use local search and gpt search together?
+      resolve(["vue", "react", mock]);
+    }, 1000);
+  });
+};
+
+watchDebounced(
+  () => input.value,
+  async (taggedInput) => {
+    if (!taggedInput) {
+      return;
+    }
+    const result = await fetchSuggestions(taggedInput);
+    if (taggedInput === input.value) {
+      suggestions.value = result as unknown[];
+      loading.value = false;
+    }
+  },
+  { debounce: 500 }
+);
+</script>
+<template>
+  <div class="search-container">
+    <el-popover
+      :visible="suggestionVisible"
+      :show-arrow="false"
+      :offset="0"
+      :teleported="false"
+      width="100%"
+    >
+      <template #reference>
+        <div class="search-trigger" :class="{ 'has-content': suggestionVisible }">
+          <el-input
+            :ref="(el) => (input$ = el)"
+            v-model="input"
+            clearable
+            :prefix-icon="Search"
+            :placeholder="t('请输入关键词,如:课程、协同、AI')"
+          ></el-input>
+        </div>
+      </template>
+      <div class="search-content">
+        <template v-if="loading">
+          <span>loading</span>
+        </template>
+        <template v-else>
+          <ul>
+            <li v-for="(suggest, _index) in suggestions" :key="_index">{{ suggest }}</li>
+          </ul>
+        </template>
+      </div>
+    </el-popover>
+  </div>
+</template>
+<i18n locale="zh-HK">
+{
+  "请输入关键词,如:课程、协同、AI": "TODO",
+}
+</i18n>
+<style lang="scss" scoped>
+.search-container {
+  width: 514px;
+  position: relative;
+  .search-trigger {
+    border: 1px solid #aeccfe;
+    padding: 1px;
+    width: 100%;
+    height: 52px;
+    border-radius: 26px;
+    display: flex;
+    align-items: center;
+    padding: 0 10px;
+    overflow: hidden;
+    transition: all 0.2s;
+    :deep(.el-input) {
+      .el-input__wrapper {
+        box-shadow: none;
+      }
+    }
+    &:has(input:focus) {
+      border: none;
+      box-shadow: var(--el-box-shadow-light);
+    }
+    &.has-content {
+      border-bottom: 1px solid #e2eeff;
+      border-radius: 26px 26px 0 0;
+    }
+  }
+  :deep(.el-popover) {
+    border-radius: 0 0 26px 26px;
+    border: none;
+    clip-path: inset(0px -10px -10px -10px);
+  }
+  .search-content {
+  }
+}
+</style>

+ 4 - 2
pages/zh-HK/index.md

@@ -3,6 +3,8 @@
 layout: home
 layout: home
 ---
 ---
 
 
-### test
+<script setup lang="ts">
+import HomeContent from '@/components/HomeContent.vue'
+</script>
 
 
-help
+<HomeContent />