Kaynağa Gözat

add: update search

gemercheung 6 ay önce
ebeveyn
işleme
5723ef701b

+ 1 - 1
packages/frontend/.env.production

@@ -1,5 +1,5 @@
 # 是否使用Hash路由
-VITE_USE_HASH = 'false'
+VITE_USE_HASH = 'true'
 
 # 资源公共路径,需要以 /开头和结尾
 VITE_PUBLIC_PATH = '/admin/'

+ 0 - 9
packages/frontend/src/components/common/TheFooter.vue

@@ -1,12 +1,3 @@
-<!--------------------------------
- - @Description: 底部
- - @Author: Ronnie Zhang
- - @LastEditor: Ronnie Zhang
- - @LastEditTime: 2023/12/04 22:42:33
- - @Email: zclzone@outlook.com
- - Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
- --------------------------------->
-
 <template>
   <footer class="f-c-c text-14 text-gray-500">
     <p>

+ 0 - 8
packages/frontend/src/views/demo/upload/index.vue

@@ -1,11 +1,3 @@
-<!--------------------------------
- - @Author: Ronnie Zhang
- - @LastEditor: Ronnie Zhang
- - @LastEditTime: 2023/12/05 21:28:02
- - @Email: zclzone@outlook.com
- - Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
- --------------------------------->
-
 <template>
   <CommonPage>
     <n-upload

+ 52 - 58
packages/frontend/src/views/menu/list.vue

@@ -19,118 +19,92 @@
         </n-input>
       </MeQueryItem>
       <MeQueryItem label="状态" :label-width="50">
-        <n-select
+        <n-select
           v-model:value="queryItems.enable" clearable :options="[
             { label: '启用', value: 1 },
             { label: '停用', value: 0 },
-          ]"
+          ]"
         />
       </MeQueryItem>
     </MeCrud>
 
     <MeModal ref="modalRef" width="520px">
       <n-form ref="modalFormRef" label-placement="left" label-align="left" :label-width="95" :model="modalForm">
-        <n-form-item
+        <n-form-item
           label="名称" path="title" :rule="{
             required: true,
             message: '请输入名称',
             trigger: ['input', 'blur'],
-          }"
+          }"
         >
           <n-input v-model:value="modalForm.title" />
         </n-form-item>
 
-        <n-form-item
+        <n-form-item
           label="描述" path="description" :rule="{
             required: false,
             message: '请输入描述',
             trigger: ['input', 'blur'],
-          }"
+          }"
         >
           <n-input v-model:value="modalForm.description" type="textarea" />
         </n-form-item>
         <n-form-item v-if="modalForm.level !== 0" label="封面" path="cover">
-          <n-upload
-            :multiple="false"
-            :default-upload="true"
-            list-type="image-card"
-            :custom-request="uploadCover"
-            :max="1"
-            :default-file-list="previewFileList"
-            @preview="handlePreview"
-            @remove="handleCoverRemove"
+          <n-upload
+            :multiple="false" :default-upload="true" list-type="image-card" :custom-request="uploadCover"
+            :max="1" :default-file-list="previewFileList" @preview="handlePreview" @remove="handleCoverRemove"
           />
-          <n-modal
-            v-model:show="showModal"
-            preset="card"
-            style="width: 600px"
-            title=""
-          >
+          <n-modal v-model:show="showModal" preset="card" style="width: 600px" title="">
             <img :src="previewImageUrl" style="width: 100%">
           </n-modal>
         </n-form-item>
-        <n-form-item
+        <n-form-item
           label="分类" path="categoryId" :rule="{
             required: modalForm.level === 0 ? false : true,
             type: 'number',
             trigger: ['change', 'blur'],
             message: '请输入分类',
-          }"
+          }"
         >
-          <n-tree-select
-            v-model:value="modalForm.categoryId"
-            :options="allCategory"
-            label-field="title"
-            key-field="id"
-            placeholder="根分类"
-            clearable
+          <n-tree-select
+            v-model:value="modalForm.categoryId" :options="allCategory" label-field="title" key-field="id"
+            placeholder="根分类" clearable
           />
         </n-form-item>
 
-        <n-form-item
-          v-if="modalForm.level === 0"
-          label="样式类型" path="styleType" :rule="{
+        <n-form-item
+          v-if="modalForm.level === 0" label="样式类型" path="styleType" :rule="{
             required: true,
             type: 'number',
             message: '请输入样式类型',
             trigger: ['input', 'blur'],
-          }"
+          }"
         >
-          <n-select
-            v-model:value="modalForm.styleType" :options="styleEnum" clearable filterable tag
-          />
+          <n-select v-model:value="modalForm.styleType" :options="styleEnum" clearable filterable tag />
         </n-form-item>
 
-        <n-form-item
+        <n-form-item
           label="文章链接" path="articleId" :rule="{
             required: false,
             type: 'number',
             trigger: ['change', 'blur'],
             message: '请输入文章链接',
-          }"
+          }"
         >
-          <n-select
-            v-model:value="modalForm.articleId" :options="allArticle"
-            clearable filterable tag
-          />
+          <n-select v-model:value="modalForm.articleId" :options="allArticle" clearable filterable tag />
         </n-form-item>
 
-        <n-form-item
-          v-if="modalForm.level === 0"
-          label="一行显示数" path="grid"
-        >
+        <n-form-item v-if="modalForm.level === 0" label="一行显示数" path="grid">
           <n-input-number v-model:value="modalForm.grid" style="width:100%" />
         </n-form-item>
 
-        <n-form-item
-          label="排序"
-          path="order"
-          :rule="{
+        <n-form-item
+          label="排序" path="order" :rule="{
             type: 'number',
             required: true,
             message: '此为必填项',
             trigger: ['blur', 'change'],
-          }"
+          }"
         >
           <n-input-number v-model:value="modalForm.order" />
         </n-form-item>
@@ -138,16 +112,25 @@
         <n-tabs type="line" animated>
           <template v-for="(lang, index) in langs" :key="lang">
             <n-tab-pane :name="lang" :tab="langLabel[lang]" :index="index">
-              {{ lang }}
-              <n-form-item
+              <n-form-item
                 label="文章名称" path="title" :rule="{
                   required: true,
                   message: '请输入文章名称',
                   trigger: ['input', 'blur'],
-                }"
+                }"
               >
                 <n-input v-model:value="modalForm.translations[index].title" />
               </n-form-item>
+
+              <n-form-item
+                label="描述" path="description" :rule="{
+                  required: false,
+                  message: '请输入描述',
+                  trigger: ['input', 'blur'],
+                }"
+              >
+                <n-input v-model:value="modalForm.translations[index].description" type="textarea" />
+              </n-form-item>
             </n-tab-pane>
           </template>
         </n-tabs>
@@ -185,6 +168,7 @@ import { formatDateTime } from '@/utils'
 import { styleEnum } from '@/utils/enum.js'
 import { initTranslations, langLabel, langs } from '@/utils/translations'
 import { NButton, NImage, NSwitch } from 'naive-ui'
+import { ref, watchEffect } from 'vue'
 import { useRoute, useRouter } from 'vue-router'
 import articleApi from '../article/api'
 import categoryApi from '../category/api'
@@ -211,7 +195,10 @@ const detail = ref({
 const queryItems = ref({
   parentId: route.params.id,
 })
-const { modalRef, modalFormRef, modalAction, modalForm, handleAdd, handleDelete, handleEdit }
+
+const modalForm = ref({})
+
+const { modalRef, modalFormRef, modalAction, modalForm: newmodalForm, handleAdd, handleDelete, handleEdit }
   = useCrud({
     name: '子菜单',
     doCreate: api.create,
@@ -221,8 +208,15 @@ const { modalRef, modalFormRef, modalAction, modalForm, handleAdd, handleDelete,
     refresh: (_, keepCurrentPage) => $table.value?.handleSearch(keepCurrentPage),
   })
 
-initTranslations(modalForm.value, ['title', 'remark'])
-
+watchEffect(() => {
+  if (newmodalForm.value) {
+    modalForm.value = {
+      ...newmodalForm.value,
+    }
+    initTranslations(modalForm.value, ['title', 'description'])
+    console.log('modalForm', modalForm)
+  }
+})
 async function getMenuDetail() {
   const { data } = await api.getOne(route.params.id)
   if (data) {

+ 0 - 8
packages/frontend/src/views/profile/api.js

@@ -1,11 +1,3 @@
-/**********************************
- * @Author: Ronnie Zhang
- * @LastEditor: Ronnie Zhang
- * @LastEditTime: 2023/12/05 21:30:03
- * @Email: zclzone@outlook.com
- * Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
- **********************************/
-
 import { request } from '@/utils'
 
 export default {

+ 1 - 1
packages/web/index.html

@@ -4,7 +4,7 @@
     <meta charset="UTF-8" />
     <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
     <meta name="viewport" content="width=device-width, initial-scale=1.0" />
-    <title>helper-center</title>
+    <title>帮忙中心</title>
   </head>
   <body>
     <div id="app"></div>

+ 1 - 0
packages/web/package.json

@@ -24,6 +24,7 @@
     "@unocss/core": "^65.4.0",
     "@unocss/vite": "^65.4.0",
     "@vicons/ionicons5": "^0.13.0",
+    "@vueuse/core": "^12.5.0",
     "axios": "^1.7.9",
     "dayjs": "^1.11.13",
     "naive-ui": "^2.40.3",

+ 1 - 1
packages/web/src/api/article.ts

@@ -23,7 +23,7 @@ export const getArticleDetail = (id: number): Promise<ResultData<ArticleDetailTy
 export const getArticleCount = (id: number): Promise<ResultData<boolean>> =>
   request.get(`web/article/count/${id}`)
 
-export const getArticleSearch = (keyword: number): Promise<ResultData<ArticleDetailType[]>> =>
+export const getArticleSearch = (keyword: string): Promise<ResultData<ArticleDetailType[]>> =>
   request.get(`web/search`, { key: keyword })
 
 export const getArticlesByCateId = (cid: number): Promise<ResultData<ArticleDetailType[]>> =>

+ 47 - 4
packages/web/src/components/header.vue

@@ -13,29 +13,36 @@
 
     <div class="search-box w-full flex justify-center items-center flex-col">
       <n-h1 class="font-size-[48px] font-500">{{ $t('helperTip') }}</n-h1>
-      <n-input
+   
+      <n-auto-complete
         v-model:value="keySearch"
+        :options="searchOptions"
         class="max-w-[640px]"
         size="large"
         round
         placeholder="输入关键字"
+        blur-after-select
+        @select="(index:number)=>handleAutoSelect(index,searchOptions)"
         @keydown.enter="handleSearch"
       >
         <template #prefix>
           <n-icon :component="SearchOutline" />
         </template>
-      </n-input>
+      </n-auto-complete>
     </div>
   </div>
 </template>
 <script setup lang="ts">
-import { NH1, NInput, NIcon, NDropdown, NButton } from 'naive-ui'
+import { NH1, NInput, NIcon, NDropdown, NAutoComplete,NButton } from 'naive-ui'
 import { SearchOutline } from '@vicons/ionicons5'
+import { computedAsync } from '@vueuse/core'
+import { type ArticleDetailType, getArticleSearch } from '@/api'
 
 const { t, locale } = useI18n()
 
 const keySearch = ref('')
 const router = useRouter()
+
 const options = ref([
   {
     label: t('zh'),
@@ -47,10 +54,46 @@ const options = ref([
   },
 ])
 
+
+
+const searchOptions = computedAsync(
+  async () => {
+    if(keySearch.value){
+      const list = await getArticleSearch(keySearch.value)
+      console.log('list',list.data)
+      return Array.from(list.data ||[]).map((suffix) => {
+          return {
+            label: suffix.title,
+            value: suffix.id
+          }
+        })
+    }
+ 
+  },
+  null, // initial state
+)
+
+// const searchOptions = computed(()=>{
+
+//   return ['@gmail.com', '@163.com', '@qq.com'].map((suffix) => {
+//           const value = keySearch.value === null ? '' : keySearch.value
+//           const prefix = value.split('@')[0]
+//           return {
+//             label: prefix + suffix,
+//             value: prefix + suffix
+//           }
+//         })
+// })
+
+
 const handleSelect = (key: string) => {
   locale.value = key
 }
-
+const handleAutoSelect =(index:number,list: any[])=>{
+  const item = list[index-1]
+  console.log('handleAutoUpdate',item)
+  router.push({ path: `/showdoc/${item.value}` })
+}
 const handleSearch = () => {
   console.log('keySearch', keySearch.value)
   router.push({ path: '/search', query: { key: keySearch.value } })

+ 7 - 4
pnpm-lock.yaml

@@ -245,7 +245,7 @@ importers:
     devDependencies:
       '@antfu/eslint-config':
         specifier: ^3.12.0
-        version: 3.16.0(@typescript-eslint/utils@8.21.0(eslint@9.18.0(jiti@2.4.2))(typescript@5.7.3))(@unocss/eslint-plugin@0.65.4(eslint@9.18.0(jiti@2.4.2))(typescript@5.7.3))(@vue/compiler-sfc@3.5.13)(eslint-plugin-format@0.1.3(eslint@9.18.0(jiti@2.4.2)))(eslint@9.18.0(jiti@2.4.2))(typescript@5.7.3)(vitest@2.1.8(@types/node@22.10.10)(jsdom@25.0.1)(sass@1.83.4)(terser@5.37.0))
+        version: 3.16.0(@typescript-eslint/utils@8.21.0(eslint@9.18.0(jiti@2.4.2))(typescript@5.7.3))(@unocss/eslint-plugin@0.65.4(eslint@9.18.0(jiti@2.4.2))(typescript@5.7.3))(@vue/compiler-sfc@3.5.13)(eslint-plugin-format@0.1.3(eslint@9.18.0(jiti@2.4.2)))(eslint@9.18.0(jiti@2.4.2))(typescript@5.7.3)(vitest@2.1.8(@types/node@22.10.10)(sass@1.83.4)(terser@5.37.0))
       '@iconify/json':
         specifier: ^2.2.282
         version: 2.2.299
@@ -547,6 +547,9 @@ importers:
       '@vicons/ionicons5':
         specifier: ^0.13.0
         version: 0.13.0
+      '@vueuse/core':
+        specifier: ^12.5.0
+        version: 12.5.0(typescript@5.7.3)
       axios:
         specifier: ^1.7.9
         version: 1.7.9(debug@4.4.0)
@@ -8661,7 +8664,7 @@ snapshots:
     transitivePeerDependencies:
       - chokidar
 
-  '@antfu/eslint-config@3.16.0(@typescript-eslint/utils@8.21.0(eslint@9.18.0(jiti@2.4.2))(typescript@5.7.3))(@unocss/eslint-plugin@0.65.4(eslint@9.18.0(jiti@2.4.2))(typescript@5.7.3))(@vue/compiler-sfc@3.5.13)(eslint-plugin-format@0.1.3(eslint@9.18.0(jiti@2.4.2)))(eslint@9.18.0(jiti@2.4.2))(typescript@5.7.3)(vitest@2.1.8(@types/node@22.10.10)(jsdom@25.0.1)(sass@1.83.4)(terser@5.37.0))':
+  '@antfu/eslint-config@3.16.0(@typescript-eslint/utils@8.21.0(eslint@9.18.0(jiti@2.4.2))(typescript@5.7.3))(@unocss/eslint-plugin@0.65.4(eslint@9.18.0(jiti@2.4.2))(typescript@5.7.3))(@vue/compiler-sfc@3.5.13)(eslint-plugin-format@0.1.3(eslint@9.18.0(jiti@2.4.2)))(eslint@9.18.0(jiti@2.4.2))(typescript@5.7.3)(vitest@2.1.8(@types/node@22.10.10)(sass@1.83.4)(terser@5.37.0))':
     dependencies:
       '@antfu/install-pkg': 1.0.0
       '@clack/prompts': 0.9.1
@@ -8670,7 +8673,7 @@ snapshots:
       '@stylistic/eslint-plugin': 2.13.0(eslint@9.18.0(jiti@2.4.2))(typescript@5.7.3)
       '@typescript-eslint/eslint-plugin': 8.21.0(@typescript-eslint/parser@8.21.0(eslint@9.18.0(jiti@2.4.2))(typescript@5.7.3))(eslint@9.18.0(jiti@2.4.2))(typescript@5.7.3)
       '@typescript-eslint/parser': 8.21.0(eslint@9.18.0(jiti@2.4.2))(typescript@5.7.3)
-      '@vitest/eslint-plugin': 1.1.25(@typescript-eslint/utils@8.21.0(eslint@9.18.0(jiti@2.4.2))(typescript@5.7.3))(eslint@9.18.0(jiti@2.4.2))(typescript@5.7.3)(vitest@2.1.8(@types/node@22.10.10)(jsdom@25.0.1)(sass@1.83.4)(terser@5.37.0))
+      '@vitest/eslint-plugin': 1.1.25(@typescript-eslint/utils@8.21.0(eslint@9.18.0(jiti@2.4.2))(typescript@5.7.3))(eslint@9.18.0(jiti@2.4.2))(typescript@5.7.3)(vitest@2.1.8(@types/node@22.10.10)(sass@1.83.4)(terser@5.37.0))
       eslint: 9.18.0(jiti@2.4.2)
       eslint-config-flat-gitignore: 1.0.0(eslint@9.18.0(jiti@2.4.2))
       eslint-flat-config-utils: 1.1.0
@@ -11065,7 +11068,7 @@ snapshots:
       typescript: 5.7.3
       vitest: 2.1.8(@types/node@22.10.10)(jsdom@25.0.1)(sass@1.83.4)(terser@5.37.0)
 
-  '@vitest/eslint-plugin@1.1.25(@typescript-eslint/utils@8.21.0(eslint@9.18.0(jiti@2.4.2))(typescript@5.7.3))(eslint@9.18.0(jiti@2.4.2))(typescript@5.7.3)(vitest@2.1.8(@types/node@22.10.10)(jsdom@25.0.1)(sass@1.83.4)(terser@5.37.0))':
+  '@vitest/eslint-plugin@1.1.25(@typescript-eslint/utils@8.21.0(eslint@9.18.0(jiti@2.4.2))(typescript@5.7.3))(eslint@9.18.0(jiti@2.4.2))(typescript@5.7.3)(vitest@2.1.8(@types/node@22.10.10)(sass@1.83.4)(terser@5.37.0))':
     dependencies:
       '@typescript-eslint/utils': 8.21.0(eslint@9.18.0(jiti@2.4.2))(typescript@5.7.3)
       eslint: 9.18.0(jiti@2.4.2)