qinyan 1 year ago
parent
commit
e99911d98f

BIN
src/assets/logo_header.1.png


BIN
src/assets/logo_header.png


+ 4 - 0
src/modules/module/article/https.ts

@@ -7,4 +7,8 @@ export const https = {
       params: query,
     });
   },
+
+  searchArticle(query: any) {
+    return request("/article/search", { method: "GET", params: query });
+  },
 };

+ 7 - 0
src/modules/module/article/index.ts

@@ -102,5 +102,12 @@ export const useArticle = defineStore("article", {
       }
       return res.result;
     },
+    async searchArticle(query: any) {
+      const res = await https.searchArticle(query);
+      if (res.errorNo != 200) {
+        return false;
+      }
+      return res.result;
+    },
   },
 });

+ 1 - 1
src/styles/base.css

@@ -8,7 +8,7 @@
   --vt-c-black-soft: #222222;
   --vt-c-black-mute: #282828;
 
-  --vt-c-indigo: #2c3e50;
+  --vt-c-indigo: #333;
 
   --vt-c-primary: #41969c;
 

+ 9 - 0
src/utils/image.ts

@@ -2,3 +2,12 @@ export function getImageUrl(path: string) {
   const url = new URL(`/src/assets/${path}`, import.meta.url);
   return url.href;
 }
+
+export const isImage = (url: string) => {
+  const last = url.lastIndexOf(".");
+  const fileExt = url.substring(last + 1);
+  if (["jpg", "jpeg", "png", "gif", "webp"].includes(fileExt)) {
+    return true;
+  }
+  return false;
+};

+ 94 - 0
src/views/website/Search/index.tsx

@@ -0,0 +1,94 @@
+import { useArticle } from "@/modules";
+import { SearchOutlined } from "@ant-design/icons-vue";
+import { Input, List, Pagination } from "ant-design-vue";
+import { defineComponent, onMounted, reactive, watch } from "vue";
+import { useRoute, useRouter } from "vue-router";
+import Item from "./item";
+
+const PAGE_SIZE = 10;
+
+export default defineComponent({
+  setup() {
+    const route = useRoute();
+    const router = useRouter();
+    const { query } = route;
+
+    const storeArt = useArticle();
+
+    const state = reactive({
+      list: [],
+      total: 0,
+      keyword: query.q as string,
+      value: query.q as string,
+    });
+
+    const getData = async (page = 1) => {
+      if (!state.keyword) return;
+      const query = {
+        page: page,
+        size: PAGE_SIZE,
+        query: JSON.stringify({ key: state.keyword }),
+      };
+      const res = await storeArt.searchArticle(query);
+      const list = res.list || [];
+      state.list = list;
+      state.total = res.total;
+    };
+
+    const search = (e: any) => {
+      router.push({ path: "/search", query: { q: e.target.value } });
+    };
+
+    onMounted(() => getData());
+
+    watch(
+      () => route.query.q,
+      () => {
+        state.keyword = route.query.q as string;
+        state.value = route.query.q as string;
+        getData();
+      }
+    );
+
+    return () => {
+      return (
+        <div class="detail_page_w">
+          <div class="flex items-center mt-60px mb-50px py-10px pr-17px px-10px bg-light-50 <md:(mt-30px mb-25px)">
+            <div class="flex-1">
+              <Input
+                class="!text-16px"
+                placeholder="请输入关键词"
+                bordered={false}
+                value={state.value}
+                onPressEnter={search}
+                onChange={(e: any) => (state.value = e.target.value)}
+              />
+            </div>
+            <SearchOutlined class="text-18px cursor-pointer !text-gray-600 hover:opacity-80 active:opacity-60" />
+          </div>
+          <div class="mt-50px <md:mt-25px">
+            <div class="my-15px text-16px !leading-normal <md:(my-10px text-14px)">
+              检索完成,共有{state.total}项结果符合您检索的关键词:
+              <span class="font-bold">{state.keyword}</span>
+            </div>
+            <List dataSource={state.list} class="bg-light-50">
+              {{
+                renderItem: ({ item }: any) => <Item record={item} />,
+              }}
+            </List>
+            {state.list.length > 0 && (
+              <div class="my-50px text-center">
+                <Pagination
+                  total={state.total}
+                  pageSize={PAGE_SIZE}
+                  showSizeChanger={false}
+                  onChange={getData}
+                />
+              </div>
+            )}
+          </div>
+        </div>
+      );
+    };
+  },
+});

+ 77 - 0
src/views/website/Search/item.tsx

@@ -0,0 +1,77 @@
+import { isImage } from "@/utils";
+import { css, cx } from "@linaria/core";
+import { Button } from "ant-design-vue";
+import dayjs from "dayjs";
+import { defineComponent } from "vue";
+import { any } from "vue-types";
+
+export default defineComponent({
+  props: {
+    record: any(),
+  },
+  setup(props) {
+    const getLink = (record: any) => {
+      switch (record.type) {
+        case "article":
+          return `/article/${record.cid}?aid=${record._id}`;
+        case "detail":
+          return `/detail/${record.cid}`;
+        default:
+          return `/article/${record.cid}?aid=${record._id}`;
+      }
+    };
+
+    return () => {
+      const { record } = props;
+
+      const isImg = isImage(record.summary || "");
+
+      return (
+        <div
+          class={cx(
+            itemStyles,
+            "flex items-center py-20px px-25px overflow-hidden <md:(py-15px px-20px)"
+          )}
+        >
+          <div class="mr-20px text-16px text-gray-500 <md:(mr-10px text-12px)">
+            {dayjs(record.createTime).format("YYYY.MM.DD")}
+          </div>
+          <div class="flex-1 truncate text-16px <md:(text-14px) ">
+            {record.type == "download" ? (
+              record.title
+            ) : (
+              <router-link
+                target="_blank"
+                class="text-gray-800 hover:(text-primary underline)"
+                to={getLink(record)}
+              >
+                {record.title}
+              </router-link>
+            )}
+          </div>
+          {record.type == "download" && (
+            <>
+              {isImg ? (
+                <a href={record.summary} target="_blank" class="ml-20px">
+                  <Button type="primary" size="small" ghost>
+                    下载
+                  </Button>
+                </a>
+              ) : (
+                <a href={record.summary} class="ml-20px">
+                  <Button type="primary" size="small" ghost>
+                    下载
+                  </Button>
+                </a>
+              )}
+            </>
+          )}
+        </div>
+      );
+    };
+  },
+});
+
+const itemStyles = css`
+  border-bottom: 1px solid #f5f5f5;
+`;

+ 3 - 3
src/views/website/components/layout/Footer.tsx

@@ -38,9 +38,9 @@ export default defineComponent(() => {
               </div>
               <div class={"contact_view"}>
                 <p class={"title"}>关注我们</p>
-                <p>电话 : 028-87723068</p>
-                <p>传真 : 028-87723068</p>
-                <p>中国四川省出都市西华大学艺术大楼</p>
+                <p>电话(传真):028-87723068</p>
+                <p>招生就业咨询:028-87721839</p>
+                <p>中国•四川•成都市 西华大学 艺术大楼( 610039 )</p>
                 <p class={"qrcode"}>
                   <img src={getImageUrl("footer_qrcode.png")} />
                 </p>

+ 27 - 16
src/views/website/components/layout/Header.tsx

@@ -1,10 +1,12 @@
-import { css } from "@linaria/core";
+import { useCategory } from "@/modules";
 import { MenuOutlined } from "@ant-design/icons-vue";
+import { css } from "@linaria/core";
+import { Drawer } from "ant-design-vue";
 import { defineComponent, reactive } from "vue";
 import Menu from "./Menu";
-import { useCategory } from "@/modules";
-import { Drawer } from "ant-design-vue";
 import MobileMenu from "./MobileMenu";
+import MobileSearch from "./MobileSearch";
+import Search from "./Search";
 
 export default defineComponent(() => {
   const storeCategory = useCategory();
@@ -20,8 +22,10 @@ export default defineComponent(() => {
     });
     return (
       <div class={HeaderLayout}>
-        <div class={"lay_top flex align-center justify-between"}>
-          <div class={"tips"}>立足四川、面向全国,贴近行业,服务社会。</div>
+        <div class="lay_top flex align-center justify-between">
+          <div class="tips hidden">
+            立足四川、面向全国,贴近行业,服务社会。
+          </div>
           <div class={"top_links"}>
             {topCate.map((e) => {
               return (
@@ -36,15 +40,19 @@ export default defineComponent(() => {
               <img src={getImageUrl("logo_header.png")} />
             </router-link>
           </div>
-          <div class={"menu flex-1"}>
+          <div class="menu relative flex-1 flex items-center justify-end ml-50px">
             <Menu data={mainCate} />
+            <Search />
           </div>
-          <div class={"mobileMenu"}>
-            <MenuOutlined
-              onClick={() => {
-                state.visible = true;
-              }}
-            />
+          <div class="mobile_header overflow-hidden">
+            <MobileSearch />
+            <div class="mobileMenu ml-25px">
+              <MenuOutlined
+                onClick={() => {
+                  state.visible = true;
+                }}
+              />
+            </div>
           </div>
           <Drawer
             width={200}
@@ -94,7 +102,7 @@ const HeaderLayout = css`
   .lay_menu {
     align-items: center;
     padding: 16px 30px;
-    background: var(--vt-c-primary);
+    background: var(--vt-c-white);
     .menu_logo {
       img {
         max-width: 300px;
@@ -102,7 +110,7 @@ const HeaderLayout = css`
         object-fit: contain;
       }
     }
-    .mobileMenu {
+    .mobile_header {
       display: none;
     }
     .menu {
@@ -124,9 +132,12 @@ const HeaderLayout = css`
       .menu {
         display: none;
       }
+      .mobile_header {
+        display: flex;
+        align-items: center;
+      }
       .mobileMenu {
-        display: block;
-        color: #fff;
+        color: var(--vt-c-primary);
         cursor: pointer;
         font-size: 28px;
       }

+ 4 - 12
src/views/website/components/layout/Menu.tsx

@@ -54,9 +54,6 @@ const Menus = defineComponent({
   },
 });
 const MenuLayout = css`
-  /* display: flex;
-  align-items: center;
-  justify-content: flex-end; */
   text-align: right;
   width: 100%;
   overflow: hidden;
@@ -70,9 +67,9 @@ const MenuLayout = css`
     font-size: 16px;
     text-align: center;
     white-space: nowrap;
-    color: rgba(255, 255, 255, 0.8);
+    color: #767676;
     &:hover {
-      color: rgba(255, 255, 255, 1);
+      color: var(--vt-c-primary);
       &::before {
         width: 24px;
       }
@@ -85,7 +82,7 @@ const MenuLayout = css`
       transform: translateX(-50%);
       width: 0;
       height: 2px;
-      background-color: rgba(255, 255, 255, 1);
+      background-color: var(--vt-c-primary);
       transition: width 0.3s ease-in-out;
     }
     &::after {
@@ -94,14 +91,9 @@ const MenuLayout = css`
       top: 0;
       right: 0;
       height: 16px;
-      border-right: 1px solid rgba(255, 255, 255, 0.3);
+      border-right: 1px solid rgba(132, 132, 132, 20%);
       transform: skewX(-15deg);
     }
-    &:last-child {
-      &::after {
-        display: none;
-      }
-    }
   }
 `;
 const DropMenu = css`

+ 62 - 0
src/views/website/components/layout/MobileSearch.tsx

@@ -0,0 +1,62 @@
+import { CloseOutlined, SearchOutlined } from "@ant-design/icons-vue";
+import { cx } from "@linaria/core";
+import { Input } from "ant-design-vue";
+import { defineComponent, reactive, ref } from "vue";
+import { useRouter } from "vue-router";
+
+export default defineComponent({
+  setup() {
+    const router = useRouter();
+
+    const searchInputRef = ref();
+
+    const state = reactive({
+      visible: false,
+      showSearch: false,
+      showSearchOverlay: false,
+    });
+
+    return () => (
+      <>
+        <SearchOutlined
+          class="text-24px !text-primary"
+          onClick={() => {
+            state.showSearchOverlay = true;
+            searchInputRef.value.focus();
+          }}
+        />
+        <div
+          class={cx(
+            state.showSearchOverlay ? "h-1/1" : "h-0",
+            "fixed mobile_search bg-primary bg-opacity-95 w-1/1 left-0 top-0 z-20 transition-all ease-in-out overflow-hidden"
+          )}
+        >
+          <div class="flex justify-end">
+            <CloseOutlined
+              onClick={() => (state.showSearchOverlay = false)}
+              class="mr-20px mt-40px text-22px !text-light-50"
+            />
+          </div>
+          <div class="flex items-center mx-30px mt-40px">
+            <SearchOutlined class="text-24px !text-light-50" />
+            <Input
+              autofocus
+              ref={searchInputRef}
+              bordered={false}
+              placeholder="请输入关键字"
+              class="!text-18px !text-light-50"
+              onPressEnter={(e: any) => {
+                searchInputRef.value.blur();
+                router.push({
+                  path: "/search",
+                  query: { q: e.target.value },
+                });
+                state.showSearchOverlay = false;
+              }}
+            />
+          </div>
+        </div>
+      </>
+    );
+  },
+});

+ 57 - 0
src/views/website/components/layout/Search.tsx

@@ -0,0 +1,57 @@
+import { CloseOutlined, SearchOutlined } from "@ant-design/icons-vue";
+import { css, cx } from "@linaria/core";
+import { Input } from "ant-design-vue";
+import { defineComponent, reactive, ref } from "vue";
+import { useRouter } from "vue-router";
+
+export default defineComponent({
+  setup() {
+    const router = useRouter();
+
+    const searchInputRef = ref();
+
+    const state = reactive({
+      showSearch: false,
+    });
+
+    return () => (
+      <>
+        <div class="text-gray-500">
+          <SearchOutlined
+            class="ml-40px text-20px cursor-pointer hover:text-primary"
+            onClick={() => {
+              state.showSearch = true;
+              searchInputRef.value.focus();
+            }}
+          />
+        </div>
+        <div
+          class={cx(
+            state.showSearch ? "w-85/100 opacity-100" : "w-0 opacity-0",
+            "absolute h-1/1 flex items-center bg-light-50 overflow-hidden transition-all duration-500"
+          )}
+        >
+          <div class="flex-1 flex items-center border-primary border-1 border-solid pl-10px py-5px">
+            <SearchOutlined class="text-18px !text-gray-600" />
+            <Input
+              bordered={false}
+              placeholder="请输入关键词"
+              class="w-1/1"
+              onPressEnter={(e: any) => {
+                router.push({
+                  path: "/search",
+                  query: { q: e.target.value },
+                });
+                state.showSearch = false;
+              }}
+            />
+          </div>
+          <CloseOutlined
+            onClick={() => (state.showSearch = false)}
+            class="ml-20px text-20px cursor-pointer !text-gray-600 hover:opacity-60 transition-all"
+          />
+        </div>
+      </>
+    );
+  },
+});

+ 2 - 8
src/views/website/detail/DownloadPage.tsx

@@ -1,5 +1,6 @@
 import loading from "@/components/Provider/Loading";
 import { useArticle } from "@/modules";
+import { isImage } from "@/utils";
 import { css } from "@linaria/core";
 import { List } from "ant-design-vue";
 import dayjs from "dayjs";
@@ -27,14 +28,7 @@ export default defineComponent({
       await artStore.listController.loadPage(1);
       loading.hidden();
     };
-    const isImage = (url: string) => {
-      const last = url.lastIndexOf(".");
-      const fileExt = url.substring(last + 1);
-      if (["jpg", "jpeg", "png", "gif", "webp"].includes(fileExt)) {
-        return true;
-      }
-      return false;
-    };
+
     return () => {
       return (
         <div class={page}>

+ 5 - 0
src/views/website/router/index.ts

@@ -18,6 +18,11 @@ const router = createRouter({
       name: "article",
       component: () => import("../detail/article"),
     },
+    {
+      path: "/search",
+      name: "search",
+      component: () => import("../search"),
+    },
     {
       path: "/404",
       name: "404",

+ 3 - 0
vite.config.ts

@@ -64,6 +64,9 @@ export default defineConfig({
     preprocessorOptions: {
       less: {
         javascriptEnabled: true,
+        modifyVars: {
+          "primary-color": "#41969c",
+        },
       },
     },
   },

+ 11 - 0
windi.config.ts

@@ -0,0 +1,11 @@
+import { defineConfig } from "windicss/helpers";
+
+export default defineConfig({
+  theme: {
+    extend: {
+      colors: {
+        primary: "#41969c",
+      },
+    },
+  },
+});