浏览代码

change:
seo 优化

kindring 2 年之前
父节点
当前提交
d66374b629
共有 45 个文件被更改,包括 1011 次插入374 次删除
  1. 1 0
      assets/icons/svg/news.svg
  2. 2 2
      components/header/menuDrops/menuDrop.vue
  3. 14 1
      components/public/form/inputRow.vue
  4. 1 1
      components/public/imageTable.vue
  5. 13 0
      components/public/toolTip.vue
  6. 1 1
      components/search/searchBox.vue
  7. 14 0
      layouts/error.vue
  8. 1 1
      map/adminSideBar.js
  9. 3 0
      map/apiMap.js
  10. 129 15
      nuxt.config.js
  11. 1 0
      package.json
  12. 1 1
      pages/index.vue
  13. 1 1
      pages/manger/Login.vue
  14. 13 0
      pages/manger/error.vue
  15. 1 1
      pages/manger/index.vue
  16. 1 1
      pages/manger/index/carousel.vue
  17. 1 1
      pages/manger/news.vue
  18. 198 7
      pages/manger/news/add.vue
  19. 1 1
      pages/manger/news/index.vue
  20. 1 1
      pages/manger/product.vue
  21. 1 1
      pages/manger/product/index.vue
  22. 1 1
      pages/news/_type.vue
  23. 1 1
      pages/news/index.vue
  24. 1 1
      pages/news/info/_type.vue
  25. 1 1
      pages/news/info/index.vue
  26. 53 4
      pages/product/_type.vue
  27. 26 3
      pages/product/index.vue
  28. 10 1
      pages/product/info/_type.vue
  29. 1 1
      pages/product/info/index.vue
  30. 1 1
      pages/solution/_type.vue
  31. 10 1
      pages/solution/index.vue
  32. 1 1
      pages/solution/info/_type.vue
  33. 1 1
      pages/solution/info/index.vue
  34. 10 1
      pages/support/index.vue
  35. 6 5
      server/control/c_base.js
  36. 1 0
      server/database/d_base.js
  37. 2 1
      server/database/d_product.js
  38. 4 2
      server/router/r_base.js
  39. 16 0
      server/router/r_news.js
  40. 30 0
      server/tools/fieldHandle.js
  41. 8 0
      static/robots.txt
  42. 1 1
      store/index.js
  43. 94 0
      until/formTool.js
  44. 24 2
      until/handle.js
  45. 309 309
      yarn.lock

+ 1 - 0
assets/icons/svg/news.svg

@@ -0,0 +1 @@
+<svg t="1692069803365" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4041" width="200" height="200"><path d="M928 0l-704 0c-53.024 0-96 42.944-96 96l0 64-32 0c-53.024 0-96 42.944-96 96l0 640c0 70.688 57.312 128 128 128l768 0c70.688 0 128-57.312 128-128l0-800c0-53.056-43.008-96-96-96zM960 896c0 35.264-28.736 64-64 64l-768 0c-35.296 0-64-28.736-64-64l0-640c0-17.664 14.336-32 32-32l32 0 0 640c0 17.696 14.304 32 32 32s32-14.304 32-32l0-768c0-17.664 14.336-32 32-32l704 0c17.632 0 32 14.336 32 32l0 800zM623.936 416.16c-8.8 0-16-7.168-16-16s7.2-16 16-16l256 0c8.864 0 16 7.168 16 16s-7.136 16-16 16l-256 0zM623.936 320.16c-8.8 0-16-7.168-16-16s7.2-16 16-16l256 0c8.864 0 16 7.168 16 16s-7.136 16-16 16l-256 0zM623.936 224.16c-8.8 0-16-7.168-16-16s7.2-16 16-16l256 0c8.864 0 16 7.168 16 16s-7.136 16-16 16l-256 0zM528 864.128c8.832 0 16 7.2 16 16 0 8.864-7.2 16-16 16l-256 0c-8.832 0-16-7.136-16-16 0-8.8 7.168-16 16-16l256 0zM528 768.128c8.832 0 16 7.2 16 16 0 8.864-7.2 16-16 16l-256 0c-8.832 0-16-7.136-16-16 0-8.8 7.168-16 16-16l256 0zM528 672.128c8.832 0 16 7.2 16 16 0 8.864-7.2 16-16 16l-256 0c-8.832 0-16-7.136-16-16 0-8.8 7.168-16 16-16l256 0zM880 864.128c8.8 0 16 7.2 16 16 0 8.864-7.2 16-16 16l-256 0c-8.864 0-16-7.136-16-16 0-8.8 7.136-16 16-16l256 0zM880 768.128c8.8 0 16 7.2 16 16 0 8.864-7.2 16-16 16l-256 0c-8.864 0-16-7.136-16-16 0-8.8 7.136-16 16-16l256 0zM880 672.128c8.8 0 16 7.2 16 16 0 8.864-7.2 16-16 16l-256 0c-8.864 0-16-7.136-16-16 0-8.8 7.136-16 16-16l256 0zM880 480.128c8.8 0 16 7.168 16 16s-7.2 16-16 16l-608 0c-8.832 0-16-7.168-16-16s7.168-16 16-16l608 0zM880 576.128c8.8 0 16 7.2 16 16 0 8.864-7.2 16-16 16l-608 0c-8.832 0-16-7.136-16-16 0-8.8 7.168-16 16-16l608 0zM288 416l224 0c17.696 0 32-14.304 32-32l0-223.872c0-17.696-14.304-32-32-32l-224 0c-17.696 0-32 14.304-32 32l0 223.872c0 17.664 14.304 32 32 32zM320 192l160 0 0 160-160 0 0-160z" fill="#444444" p-id="4042"></path></svg>

+ 2 - 2
components/header/menuDrops/menuDrop.vue

@@ -34,7 +34,7 @@
 
 <script>
 import langMap from "~/map/langMap";
-import handle from "~/until/handle";
+import {handle} from "~/until/handle";
 import {indexTypes} from "../../../store";
 import {mapGetters} from "vuex";
 import {productMenus} from "~/map/productMap";
@@ -158,7 +158,7 @@ export default {
         // 尝试加载特定静态页面
         window.location.href = item.source
       }else{
-        window.location.href = `${this.pHref}/item/${this.nowMenuKey}?id=${item.id}`
+        window.location.href = `${this.pHref}/info/${this.nowMenuKey}?id=${item.id}`
       }
     }
   }

+ 14 - 1
components/public/form/inputRow.vue

@@ -21,6 +21,10 @@ export default {
     msg: {
       type: String,
       default: ''
+    },
+    remark: {
+      type: String,
+      default: ''
     }
   },
   data(){
@@ -35,7 +39,16 @@ export default {
 <template>
   <div class="w-full px-1.5 flex">
     <div class="w-4/12 flex items-center u px-1">
-      <p class="w-full text-justify">{{ label }}</p>
+      <p class="w-full text-justify">
+        {{ label }}
+<!--        显示一个问号,悬浮时显示remark, 不使用 a toolTip -->
+        <a-tooltip v-if="remark" placement="topLeft">
+          <template slot="title">
+            {{remark}}
+          </template>
+          <a-icon type="question-circle" />
+        </a-tooltip>
+      </p>
     </div>
     <div class="w-8/12">
       <slot></slot>

+ 1 - 1
components/public/imageTable.vue

@@ -92,7 +92,7 @@
 </template>
 
 <script>
-import handle from "~/until/handle";
+import {handle} from "~/until/handle";
 import Loading from "~/components/public/loading";
 import {db_base} from "../../map/dbField_esm";
 import {rCode} from "../../map/rcodeMap_esm";

+ 13 - 0
components/public/toolTip.vue

@@ -0,0 +1,13 @@
+<script>
+ export default {
+   name: 'toolTip'
+ }
+</script>
+
+<template>
+
+</template>
+
+<style scoped>
+
+</style>

+ 1 - 1
components/search/searchBox.vue

@@ -1,5 +1,5 @@
 <script>
-import handle from "../../until/handle";
+import {handle} from "../../until/handle";
 import {rCode} from "../../map/rcodeMap_esm";
 import Loading from "../public/loading.vue";
 import HideScroll from "../public/hideScroll.vue";

+ 14 - 0
layouts/error.vue

@@ -0,0 +1,14 @@
+<script setup>
+
+</script>
+
+<template>
+<!--  404 -->
+<div class="">
+  当前页面正在开发中,请稍后再试
+</div>
+</template>
+
+<style scoped>
+
+</style>

+ 1 - 1
map/adminSideBar.js

@@ -48,7 +48,7 @@ export const adminMenus = [
   },
   {
     title: '文章管理',
-    icon: 'news-setting',
+    icon: 'news',
     child: [
       {
         key: 'searchPages',

+ 3 - 0
map/apiMap.js

@@ -45,6 +45,9 @@ export const apiMap = {
     // 新闻与解决方案都是文章,不多做接口
     path: `/api/solution`
   },
+  newsAdd: {
+    path: `/api/news/add`
+  },
   downloads: {
     path: `/api/download/list`
   },

+ 129 - 15
nuxt.config.js

@@ -1,6 +1,10 @@
 import * as path from "path";
+import axios from "axios";
+import {handleAll} from "./until/handle";
+import {apiMap} from "./map/apiMap";
 const devPort = 3000;
 const serverPort = 4201;
+
 const env = {
     dev: {
       MODE: 'development',
@@ -8,7 +12,7 @@ const env = {
     },
     pro: {
       MODE: 'production',
-      ENV_API: `http://127.0.0.1:${serverPort}`  // 正式服务器地址
+      ENV_API: `http://szhfy.com.cn:${serverPort}`  // 正式服务器地址
     }
 }
 function loadSvgConfig(config, ctx) {
@@ -25,14 +29,123 @@ function loadSvgConfig(config, ctx) {
     ]
   });
 }
+
+/**
+ * 获取seo数据
+ * @returns {Promise<*[]>}
+ */
+async function seoDataLoad(){
+  // 静态路由
+  let routes = [];
+  // 根据生产环境判断baseUrl
+  // 请求数据
+  let err, productRes, newsRes, solutionRes;
+  let baseUrl = process.env.NODE_ENV === 'production' ? env.pro.ENV_API : env.dev.ENV_API;
+  let productDataUrl = baseUrl + apiMap.searchProduct.path;
+  let solutionDataUrl = baseUrl + apiMap.searchSolution.path;
+  let newsDataUrl = baseUrl + apiMap.searchNews.path;
+  productDataUrl += `?type=all&p=1&l=1000`;
+  solutionDataUrl += `?type=all&p=1&l=1000`;
+  newsDataUrl += `?type=all&p=1&l=1000`;
+  console.log(`productDataUrl:${productDataUrl}`);
+  console.log(`solutionDataUrl:${solutionDataUrl}`);
+  console.log(`newsDataUrl:${newsDataUrl}`);
+  [err, productRes ,newsRes, solutionRes] =
+    await handleAll(
+      axios.get(productDataUrl),
+      axios.get(solutionDataUrl),
+      axios.get(newsDataUrl),
+    );
+  if(err){
+    console.log(err.message);
+    console.log(err);
+    return routes;
+  }else{
+    let productData = productRes.data,
+      solutionData = solutionRes.data,
+      newsData = newsRes.data;
+    if(productData.code === 1){
+      let productArr = productData.data;
+      productArr.forEach(item=>{
+        routes.push(
+          {
+            url: `/product/info?id=${item.id}`,
+            changefreq: 'daily',
+            priority: 0.9,
+          },
+          {
+            url: `/product/info/${item.type_key}?id=${item.id}`,
+            changefreq: 'daily',
+            priority: 0.9,
+          }
+        );
+      })
+    }
+    if(solutionData.code === 1){
+      let solutionArr = solutionData.data;
+      solutionArr.forEach(item=>{
+        routes.push(
+          {
+            url: `/solution/info?id=${item.id}`,
+            changefreq: 'daily',
+            priority: 0.8,
+          },
+          {
+            url: `/solution/info/${item.type_key}?id=${item.id}`,
+            changefreq: 'daily',
+            priority: 0.8,
+          }
+        );
+
+      })
+    }
+    if(newsData.code === 1){
+      let newsArr = newsData.data;
+      newsArr.forEach(item=>{
+        routes.push(
+          {
+            url: `/news/info?id=${item.id}`,
+            changefreq: 'daily',
+            priority: 0.7,
+          },
+          {
+            url: `/news/info/${item.type_key}?id=${item.id}`,
+            changefreq: 'daily',
+            priority: 0.7,
+          }
+        );
+      })
+    }
+  }
+  console.log(routes);
+  return routes
+}
+let seoOption = {
+  hostname: "http://szhfy.com.cn/", // 你的网站地址
+  cacheTime: 1000 * 60 * 60 * 24, //一天的更新频率,只在generate:false有用
+  gzip: true, //用gzip压缩优化生成的 sitemap.xml  文件
+  generate: false,
+  exclude: [
+    '/manger',
+    '/manger/*',
+    '/manger/**',
+  ], //排除不需要收录的页面,这里的路径是相对于hostname, 例如: exclude: ['/404',...]
+  // 每个对象代表一个页面,这是默认的
+  defaults: {
+    changefred: "always", // 收录变化的时间,always 一直,daily 每天
+    lastmod: new Date(), // 生成该页面站点地图时的时间
+    priority: 1, // 网页的权重  1是100%最高,一般给最重要的页面,不重要的页面0.7-0.9之间
+  },
+}
 export default {
     // Global page headers (https://go.nuxtjs.dev/config-head)
     head: {
-        title: '合方圆-合天地方圆',
+        title: '深圳市合方圆科技',
         meta: [
             { charset: 'utf-8' },
             { name: 'viewport', content: 'width=device-width, initial-scale=1' },
-            { hid: 'description', name: 'description', content: '' }
+          { hid: 'keywords', name: 'keywords',  content: '合方圆,深圳合方圆,深圳合方圆科技,合方圆科技,4G低功耗'},
+          { hid: 'description', name: 'description', content: '网站描述' },
         ],
         link: [
             { rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' },
@@ -56,12 +169,24 @@ export default {
     modules: [
         // https://go.nuxtjs.dev/axios
         '@nuxtjs/axios',
+        '@nuxtjs/sitemap'
         // '@nuxtjs/style-resources',
     ],
+    sitemap: [
+      {
+        path: "/sitemap.xml", //生成的文件路径
+        ...seoOption,
+        routes: seoDataLoad,
+      },
+      {
+        path: "/sitemap.html", //生成的文件路径
+        ...seoOption,
+        routes: seoDataLoad,
+      }
+    ],
     // 加入axios 插件
     plugins: [
       "@plugins/svg-icon.js",
-
       { src: "@plugins/ckeditor.js", mode: "client" ,ssr: false}
       // {src: '~/plugins/vue-pdf.js', ssr: false}
     ],
@@ -121,23 +246,12 @@ export default {
           '/product/sm',
           '/product/low',
           '/product/cam',
-          '/product/info/m2m',
-          '/product/info/aiCam',
-          '/product/info/sm',
-          '/product/info/low',
-          '/product/info/cam',
           '/solution/sol',
           '/solution/acs',
           '/solution/epower',
-          '/solution/info/sol',
-          '/solution/info/acs',
-          '/solution/info/epower',
           '/news/com',
           '/news/pa',
           '/news/in',
-          '/news/info/com',
-          '/news/info/pa',
-          '/news/info/in',
         ]
     }
 }

+ 1 - 0
package.json

@@ -12,6 +12,7 @@
     "@ckeditor/ckeditor5-build-classic": "^39.0.0",
     "@ckeditor/ckeditor5-vue2": "^3.0.1",
     "@nuxtjs/axios": "^5.12.2",
+    "@nuxtjs/sitemap": "^2.4.0",
     "ant-design-vue": "^1.6.5",
     "body-parser": "^1.20.2",
     "core-js": "^3.6.5",

+ 1 - 1
pages/index.vue

@@ -26,7 +26,7 @@ import showingStand from "~/components/showingStand";
 import newCenter from "@/components/newCenter";
 import defaultFooter from "~/components/footer/defaultFooter";
 import langMap from "~/map/langMap";
-import handle from "~/until/handle";
+import {handle} from "~/until/handle";
 import {isMediaView} from "@/until/mediaView";
 export default {
   components: { lucencyHeader,banner,showingStand,defaultFooter,productCenter,newCenter },

+ 1 - 1
pages/manger/Login.vue

@@ -95,7 +95,7 @@ import { message } from 'ant-design-vue';
 import { Modal } from 'ant-design-vue';
 
 // import apis from '@/apis/apis'
-import handle from "~/until/handle";
+import {handle} from "~/until/handle";
 import fieldIsAllow from "~/until/fieldIsAllow"
 
 import Captcha from "~/components/public/captcha";

+ 13 - 0
pages/manger/error.vue

@@ -0,0 +1,13 @@
+<script setup>
+
+</script>
+
+<template>
+<div class="">
+  当前页面正在开发中,请稍后再试
+</div>
+</template>
+
+<style scoped>
+
+</style>

+ 1 - 1
pages/manger/index.vue

@@ -7,7 +7,7 @@ import Vue from 'vue'
 import antd from 'ant-design-vue'
 import 'ant-design-vue/dist/antd.css'
 import {login_types} from "../../store/login";
-import handle from "../../until/handle";
+import {handle} from "../../until/handle";
 Vue.use(antd);
 
 export default {

+ 1 - 1
pages/manger/index/carousel.vue

@@ -4,7 +4,7 @@ import {defineComponent} from "vue";
 import fieldIsAllow from "../../../until/fieldIsAllow"
 import RoundedTitle from "../../../components/public/roundedTitle.vue";
 import {rCode} from "../../../map/rcodeMap_esm";
-import handle from "../../../until/handle";
+import {handle} from "../../../until/handle";
 import ImageViewer from "../../../components/public/imageViewer.vue";
 import ImageTable from "../../../components/public/imageTable.vue";
 import Pop from "../../../components/public/pop.vue";

+ 1 - 1
pages/manger/news.vue

@@ -7,7 +7,7 @@ import Vue from 'vue'
 import antd from 'ant-design-vue'
 import 'ant-design-vue/dist/antd.css'
 import {login_types} from "../../store/login";
-import handle from "../../until/handle";
+import {handle} from "../../until/handle";
 Vue.use(antd);
 
 export default {

+ 198 - 7
pages/manger/news/add.vue

@@ -7,6 +7,14 @@ import RoundedTitle from "../../../components/public/roundedTitle.vue";
 import PopCard from "../../../components/public/popCard.vue";
 import ImageTable from "../../../components/public/imageTable.vue";
 import Pop from "../../../components/public/pop.vue";
+import TableSelect from "../../../components/public/tableSelect.vue";
+import dbField_esm from "../../../map/dbField_esm";
+import {checkFormItem, initForm} from "../../../until/formTool";
+import InputRow from "../../../components/public/form/inputRow.vue";
+import ImageViewer from "../../../components/public/imageViewer.vue";
+import {handle} from "../../../until/handle";
+import {apiMap} from "../../../map/apiMap";
+import {rCode} from "../../../map/rcodeMap_esm";
 
 
 
@@ -23,6 +31,9 @@ if (process.client) {
 export default {
   name: "add",
   components: {
+    ImageViewer,
+    InputRow,
+    TableSelect,
     Pop,
     ImageTable,
     PopCard,
@@ -30,6 +41,25 @@ export default {
     // Use the <ckeditor> component in this view.
 
   },
+  computed: {
+    // 产品类型数据
+    newsTypes(){
+      let arr = [];
+      if (this.form.pType.val === dbField_esm.db_base.newsType.all){
+        arr = this.$store.getters.allNewsTypes;
+      }else if (this.form.pType.val === dbField_esm.db_base.newsType.news){
+        arr = this.$store.getters.newsTypes;
+      }else if (this.form.pType.val === dbField_esm.db_base.newsType.solution){
+        arr = this.$store.getters.solutionTypes;
+      }
+      if (arr[0].key !== 'all'){
+        // 添加 all
+        arr.unshift({text: '全部', key: 'all' });
+      }
+
+      return arr;
+    },
+  },
   data(){
     return {
       editor: ClassicEditor,
@@ -40,33 +70,145 @@ export default {
       },
       imagePopShow: false,
       imagePopLoading: false,
+      isCoverSelect: false,
+      form: {
+        pType: {
+          val: dbField_esm.db_base.newsType.all,
+          oldVal: dbField_esm.db_base.newsType.all,
+          init: dbField_esm.db_base.newsType.all,
+          msg: '',
+          state: 0,
+          options: [
+            {text: '全部', key: dbField_esm.db_base.newsType.all},
+            {text: '新闻', key: dbField_esm.db_base.newsType.news},
+            {text: '解决方案', key: dbField_esm.db_base.newsType.solution},
+          ]
+        },
+        type: {
+          val: '',
+          init: '',
+          msg: '',
+          state: 0,
+          options: []
+        },
+        title: {
+          val: '',
+          init: '',
+          msg: '',
+          state: 0
+        },
+        author: {
+          val: '',
+          init: '',
+          msg: '',
+          state: 0
+        },
+        source: {
+          val: '',
+          init: '',
+          msg: '',
+          state: 0
+        },
+        cover: {
+          val: '',
+          init: '',
+          msg: '',
+          state: 0
+        },
+        remark: {
+          val: '',
+          init: '',
+          msg: '',
+          state: 0
+        },
+      }
     }
   },
   beforeMount() {
-    // this.editorConfig.toolbar.push('customButton');
     // 插入toolbar
-
     this.editorConfig.customFunction = () => {
-      console.log('customFunction2');
       this.openImageSelect();
     };
+    this.initForm();
+
   },
   mounted() {
-    // 插入按钮
   },
   methods:{
+    initForm(){
+      this.form.pType.val = dbField_esm.db_base.newsType.all;
+      this.form.type.options = this.newsTypes;
+      initForm(this.form);
+    },
+    openCoverSelect(){
+      console.log('打开文章封面选择窗口');
+      this.isCoverSelect = true;
+      this.imagePopShow = true;
+    },
     openImageSelect(){
       console.log('尝试打开图片选择窗口');
+      this.isCoverSelect = false;
       this.imagePopShow = true;
     },
-    okHandle(fileData){
+    selectImageHandle(fileData){
       console.log('okHandle');
       console.log(fileData);
       this.imagePopShow = false;
+      if(this.isCoverSelect){
+        // 选择封面图片
+        this.form.cover.val = fileData.filePath;
+        this.form.cover.init = fileData.filePath;
+        this.form.cover.msg = '';
+        this.form.cover.state = 0;
+        this.form.cover.showText = fileData.filePath;
+        return;
+      }
       // 将图片新增至编辑器中,允许调整图片大小
       this.editorData += `<img src="${fileData.filePath}" alt="${fileData.fileName}">`;
       console.log(this.editorData);
     },
+    onPTypeChange(){
+      console.log('文章主类型改变');
+      console.log(this.newsTypes);
+      this.form.type.val = this.newsTypes[0].key;
+      this.form.type.oldVal = this.newsTypes[0].key;
+      this.form.type.init = this.newsTypes[0].key;
+      this.form.type.options = this.newsTypes;
+    },
+    async onSubmitHandle(){
+      // 提交文章
+      console.log('提交文章');
+      let formData = {};
+      let isPass = checkFormItem(this.form);
+      if(!isPass){
+        return console.log('数据验证不通过');
+      }
+      formData.content = this.editorData;
+      formData.type = this.form.type.val;
+      formData.title = this.form.title.val;
+      formData.author = this.form.author.val;
+      formData.source = this.form.source.val;
+      formData.cover = this.form.cover.val;
+      formData.remark = this.form.remark.val;
+      formData.pType = this.form.pType.val;
+      console.log(formData);
+      // 发送请求至后台
+      let [err,res] = await handle(this.$axios.post(
+        apiMap.newsAdd.path,
+        formData
+      ));
+      if(err){
+        console.log(err);
+        return this.$message.error('新增文章失败');
+      }
+      let  result = res.data;
+      if (result.code === rCode.OK){
+        this.$message.success('新增文章成功');
+      }else{
+        this.$message.error(`新增文章失败,${result.msg}`);
+      }
+
+    }
   }
 }
 </script>
@@ -80,8 +222,57 @@ export default {
         hover:text-orange-500 ">文章中心</a>
   </rounded-title>
   <div class="page-content-box w-full mt-2 p-0.5  rounded bg-white">
-    <ckeditor ref="ckeditor" :editor="editor" v-model="editorData" :config="editorConfig"></ckeditor>
+    <div class="flex pb-1 border-b border-gray-300 border-dashed">
+      <a-radio-group class="flex flex-shrink-0  items-center" v-model="form.pType.val" @change="onPTypeChange">
+        <a-radio-button class="" :value="item.key" v-for="item in form.pType.options" :key="item.key">{{item.text}}</a-radio-button>
+      </a-radio-group>
+
+      <table-select
+        class="w-48 !mx-3 flex-shrink-0"
+        :options="form.type.options"
+        v-model="form.type.val"
+      />
+    </div>
+    <input-row class="mt-2" :msg="form.author.msg"
+               label="作者">
+      <a-input v-model="form.author.val" placeholder="请输入作者"></a-input>
+    </input-row>
+    <input-row class="mt-2" :msg="form.source.msg"
+               label="来源">
+      <a-input v-model="form.source.val" placeholder="请输入来源"></a-input>
+    </input-row>
+    <input-row class="mt-2" :msg="form.title.msg"
+               label="标题">
+      <a-input v-model="form.title.val" placeholder="请输入标题"></a-input>
+    </input-row>
+    <input-row class="mt-2" :msg="form.remark.msg"
+               remark="文章的简介摘要部分,用于外部展示"
+               label="摘要" >
+      <a-input v-model="form.remark.val" placeholder="请输入摘要"></a-input>
+    </input-row>
+    <input-row class="my-2" :msg="form.cover.msg"
+               label="文章封面">
+      <div class="rounded relative" style="width: 524px;height: 360px" >
+        <image-viewer class="" :src="form.cover.showText"></image-viewer>
+        <div class="absolute w-full h-full left-0 top-0
+              justify-center text-white bg-gray-400
+              items-center text-2xl flex opacity-0 hover:opacity-70"
+              @click="openCoverSelect"
+        >
+          点击选择封面图片
+        </div>
+      </div>
+    </input-row>
+
+    <ckeditor class="mt-2" ref="ckeditor" :editor="editor" v-model="editorData" :config="editorConfig"></ckeditor>
+
+    <div class="mt-2">
+      <a-button type="primary" @click="onSubmitHandle">新增文章</a-button>
+    </div>
   </div>
+
+
+<!--  图片选择弹窗 -->
   <pop :show="imagePopShow" :loading="imagePopLoading">
     <pop-card>
       <template slot="header">
@@ -90,7 +281,7 @@ export default {
       <image-table
         class="w-full h-full"
         @cancel="imagePopShow = false"
-        @ok="okHandle">
+        @ok="selectImageHandle">
 
       </image-table>
     </pop-card>

+ 1 - 1
pages/manger/news/index.vue

@@ -1,5 +1,5 @@
 <script>
-import handle from "../../../until/handle";
+import {handle} from "../../../until/handle";
 import axios from "axios";
 import {apiMap, baseUrl} from "../../../map/apiMap";
 import {rCode} from "../../../map/rcodeMap_esm";

+ 1 - 1
pages/manger/product.vue

@@ -7,7 +7,7 @@ import Vue from 'vue'
 import antd from 'ant-design-vue'
 import 'ant-design-vue/dist/antd.css'
 import {login_types} from "../../store/login";
-import handle from "../../until/handle";
+import {handle} from "../../until/handle";
 Vue.use(antd);
 
 export default {

+ 1 - 1
pages/manger/product/index.vue

@@ -1,5 +1,5 @@
 <script>
-import handle from "../../../until/handle";
+import {handle} from "../../../until/handle";
 import axios from "axios";
 import {apiMap, baseUrl} from "../../../map/apiMap";
 import {rCode} from "../../../map/rcodeMap_esm";

+ 1 - 1
pages/news/_type.vue

@@ -6,7 +6,7 @@
 
 <script>
 import productIndex from '@/pages/product/index.vue'
-import handle from "~/until/handle";
+import {handle} from "~/until/handle";
 import axios from "axios";
 import SolutionIndex from "@/pages/solution/index";
 import {apiMap, baseUrl} from "~/map/apiMap";

+ 1 - 1
pages/news/index.vue

@@ -17,7 +17,7 @@ import axios from "axios";
 import qs from "qs";
 
 import langMap from "@/map/langMap";
-import handle from "@/until/handle";
+import {handle} from "@/until/handle";
 import {isMediaView} from "@/until/mediaView";
 import LucencyHeader from "~/components/header/lucencyHeader.vue";
 import ItemBanner from "~/components/banner/itemBanner.vue";

+ 1 - 1
pages/news/info/_type.vue

@@ -7,7 +7,7 @@
 import ItemIndex from "~/pages/news/info/index";
 import {apiMap, baseUrl} from "~/map/apiMap";
 import {toNumber} from "~/until/typeTool";
-import handle from "~/until/handle";
+import {handle} from "~/until/handle";
 import axios from "axios";
 
 export default {

+ 1 - 1
pages/news/info/index.vue

@@ -29,7 +29,7 @@ import axios from "axios";
 import langMap from "~/map/langMap";
 import defaultFooter from "~/components/footer/defaultFooter";
 import {getTypeSubText, getTypeText} from "~/map/newMap";
-import handle from "~/until/handle";
+import {handle} from "~/until/handle";
 import {timestampToTime} from "~/until/time";
 import {unescapeHtml} from "~/until/unescapeHtml";
 import {isMediaView} from "@/until/mediaView";

+ 53 - 4
pages/product/_type.vue

@@ -1,12 +1,19 @@
 <template>
-  <product-index :p-type="type" :p-product="products" :p-page-data="basePageData"/>
+  <product-index
+    :p-type="type"
+    :p-product="products"
+    :p-page-data="basePageData"
+    :p-seo-key="seoKey"
+    :p-seo-description="seoDescription"
+  />
 </template>
 
 <script>
 import productIndex from '@/pages/product/index.vue'
-import handle from "~/until/handle";
+import {handle} from "~/until/handle";
 import axios from "axios";
 import {apiMap, baseUrl} from "~/map/apiMap";
+import {isEmpty} from "@/until/typeTool";
 export default {
   name: "typeProductPage",
   props:[],
@@ -15,11 +22,28 @@ export default {
     let url = baseUrl + apiMap.searchProduct.path;
     let type = ctx.params.type?ctx.params.type:'all';
     url += `?type=${type}&p=1`
-    let [err,res] = await handle(axios.get( url));
+    let [err,res] = await handle(
+      axios.get( url)
+    );
     if(err){
       console.log(err);
       return {};
     }
+    // 获取 nuxtServerInit 中的数据 getters productTypes
+    let productTypes = ctx.store.getters.productTypes;
+    let seoKey = "合方圆,合方圆科技";
+    let seoDescription = "合方圆科技产品中心,可选择4g,或者poe摄像头.满足多种需求,以及定制化需求";
+    // 判断是否有数据
+    if(!isEmpty(productTypes)){
+      console.log(`已经获取到类型数据`);
+      // 获取当前类型的数据
+      let typeData = productTypes.find(item=>item.typeKey === type);
+      console.log(typeData);
+      if(typeData){
+        seoKey = `合方圆,合方圆科技,深圳合方圆,${typeData.text},合方圆${typeData.text}`;
+        seoDescription = `深圳市合方圆科技${typeData.text}产品`;
+      }
+    }
     let result = res.data;
     if(result.code === 1){
       let pageData = {
@@ -31,15 +55,40 @@ export default {
       return {
         products: result.data,
         basePageData: pageData,
+        seoKey,
+        seoDescription
       }
     }else{
-      return {products:[]}
+      return {
+        products:[],
+        seoKey,
+        seoDescription
+      }
+    }
+  },
+  head(){
+    return {
+      title: this.seoKey,
+      meta: [
+        {
+          hid: 'description',
+          name: 'description',
+          content: this.seoDescription
+        },
+        {
+          hid: 'keywords',
+          name: 'keywords',
+          content: this.seoKey
+        },
+      ]
     }
   },
   data(){
     return {
       type:  'all',
       basePageData: {},
+      seoKey: "合方圆,合方圆科技",
+      seoDescription: "合方圆科技,合天地方圆,产品中心",
       products: []
     }
   },

+ 26 - 3
pages/product/index.vue

@@ -5,29 +5,50 @@ import langMap from "@/map/langMap";
 import productBanner from "@/components/banner/productBanner";
 import qs from "qs"
 import axios from "axios"
-import handle from "~/until/handle";
+import {handle} from "~/until/handle";
 import {isMediaView} from "@/until/mediaView";
 import LucencyHeader from "~/components/header/lucencyHeader.vue";
 import DefaultFooter from "~/components/footer/defaultFooter.vue";
 import {apiMap, baseUrl} from "~/map/apiMap";
 export default {
   name: "index",
-  props:['uLang','pType','pKey','pProduct','pPageData'],
+  props:[
+    'uLang','pType','pKey','pProduct','pPageData',
+    'pSeoKey','pSeoDescription'
+  ],
   components:{
     DefaultFooter,
     LucencyHeader,
     productBanner
   },
+  head(){
+    return {
+      title: `合方圆-产品中心`,
+      meta: [
+        {
+          hid: 'description',
+          name: 'description',
+          content: this.pSeoDescription? this.pSeoDescription : `合方圆科技产品中心,可选择4g,或者poe摄像头.满足多种需求,以及定制化需求`
+        },
+        {
+          hid: 'keywords',
+          name: 'keywords',
+          content: this.pSeoKey? this.pSeoKey : `合方圆-产品中心,合方圆科技-产品中心`
+        },
+      ]
+    }
+  },
   async asyncData(ctx){
     // 获取数据
     let url = baseUrl + apiMap.searchProduct.path;
-    url += `?type=all&p=1`
+    url += `?type=all&p=1`;
     let [err,res] = await handle(axios.get( url));
     if(err){
       console.log(err);
       return {};
     }
     let result = res.data;
+    // 获取seo 数据
     if(result.code === 1){
       let pageData = {
         limit: result.limit,
@@ -48,6 +69,8 @@ export default {
   data(){
     return {
       lang: this.uLang?this.uLang:langMap.lang.cn,
+      seoKey: '合方圆-产品中心,合方圆科技-产品中心',
+      seoDescription: '深圳合方圆科技产品中心',
       type: this.pType?this.pType:'all',
       key: this.pKey?this.pKey:'',
       page: 1,

+ 10 - 1
pages/product/info/_type.vue

@@ -5,7 +5,7 @@
 <script>
 import ItemIndex from "~/pages/product/info/index";
 import {apiMap, baseUrl} from "~/map/apiMap";
-import handle from "~/until/handle";
+import {handle} from "~/until/handle";
 import axios from "axios";
 import {toNumber} from "~/until/typeTool";
 export default {
@@ -18,6 +18,15 @@ export default {
       loadErr: false,
     }
   },
+  head(){
+    return {
+      title: `合方圆-${this.productionInfo.name}`,
+      meta: [
+        { hid: 'description', name: 'description', content: this.productionInfo.description },
+        { hid: 'keywords', name: 'keywords', content: this.productionInfo.keywords },
+      ]
+    }
+  },
   async asyncData(ctx){
     let err,res;
     let id = ctx.query.id;

+ 1 - 1
pages/product/info/index.vue

@@ -40,7 +40,7 @@
 import axios from "axios";
 import langMap from "~/map/langMap";
 import { getTypeText,getTypeSubText, pTypes} from "~/map/productMap";
-import handle from "~/until/handle";
+import {handle} from "~/until/handle";
 import {unescape} from "~/until/unescapeHtml";
 import qTab from "../../../components/qTab";
 import DefaultFooter from "../../../components/footer/defaultFooter";

+ 1 - 1
pages/solution/_type.vue

@@ -5,7 +5,7 @@
 
 <script>
 import productIndex from '@/pages/product/index.vue'
-import handle from "~/until/handle";
+import {handle} from "~/until/handle";
 import axios from "axios";
 import qs from "qs";
 import SolutionIndex from "@/pages/solution/index";

+ 10 - 1
pages/solution/index.vue

@@ -21,7 +21,7 @@ import solutionTypes from "~/components/solutionTypes";
 import solutionList from "~/components/solutionList";
 
 import langMap from "~/map/langMap";
-import handle from "~/until/handle";
+import {handle} from "~/until/handle";
 import axios from "axios";
 import {isMediaView} from "@/until/mediaView";
 import LucencyHeader from "~/components/header/lucencyHeader.vue";
@@ -40,6 +40,15 @@ export default {
     solutionTypes,
     solutionList
   },
+  head(){
+    return {
+      title: `合方圆-解决方案`,
+      meta: [
+        { hid: 'description', name: 'description', content: `合方圆-解决方案` },
+        { hid: 'keywords', name: 'keywords', content: `合方圆-解决方案,合方圆科技-解决方案,` },
+      ]
+    }
+  },
   async asyncData(ctx){
     // 获取数据
     let url = baseUrl + apiMap.searchSolution.path;

+ 1 - 1
pages/solution/info/_type.vue

@@ -6,7 +6,7 @@
 import ItemIndex from "~/pages/solution/info/index";
 import {apiMap, baseUrl} from "~/map/apiMap";
 import {toNumber} from "~/until/typeTool";
-import handle from "~/until/handle";
+import {handle} from "~/until/handle";
 import axios from "axios";
 export default {
   name: "solutionItemType",

+ 1 - 1
pages/solution/info/index.vue

@@ -28,7 +28,7 @@
 import langMap from "~/map/langMap";
 import defaultFooter from "~/components/footer/defaultFooter";
 import {getTypeSubText, getTypeText} from "~/map/solutionMap";
-import handle from "~/until/handle";
+import {handle} from "~/until/handle";
 import {timestampToTime} from "~/until/time";
 import {unescape, unescapeHtml} from "~/until/unescapeHtml";
 import {isMediaView} from "@/until/mediaView";

+ 10 - 1
pages/support/index.vue

@@ -23,7 +23,7 @@ import qs from "qs";
 
 import lucencyHeader from "~/components/header/lucencyHeader";
 import langMap from "@/map/langMap";
-import handle from "@/until/handle";
+import {handle} from "@/until/handle";
 import {unescapeHtml} from "@/until/unescapeHtml";
 import {isMediaView} from "@/until/mediaView";
 import ItemBanner from "~/components/banner/itemBanner.vue";
@@ -59,6 +59,15 @@ export default {
   name: "index",
   props:['uLang'],
   components: {DefaultFooter, Downloads, ItemBanner, lucencyHeader },
+  head(){
+    return {
+      title: `合方圆-支持中心`,
+      meta: [
+        { hid: 'description', name: 'description', content: '合方圆支持中心' },
+        { hid: 'keywords', name: 'keywords', content: '合方圆技术支持,合方圆科技技术支持,合方圆科技-下载中心' },
+      ]
+    }
+  },
   data(){
     return {
       lang: this.uLang?this.uLang:langMap.lang.cn,

+ 6 - 5
server/control/c_base.js

@@ -99,6 +99,7 @@ async function updateCarousel(id,updateItems){
   let typeKeys = Object.keys(dbField.db_base.carouselType);
   let typeString = '';
   let typeShowText = '';
+  let carouselType = dbField.db_base.carouselType.href;
   // 判断是否需要更新type
   if (updateItems.value){
     // 获取基础type值
@@ -118,19 +119,19 @@ async function updateCarousel(id,updateItems){
       rawData = res[0];
       typeString = rawData.type;
     }
-
+    console.log(toNumber(typeString))
     // 需要更新具体值
-    if(typeString !== dbField.db_base.carouselType.href){
-      if(updateItems.type === dbField.db_base.carouselType.production){
+    if(toNumber(typeString) !== dbField.db_base.carouselType.href){
+      if(toNumber(typeString) === dbField.db_base.carouselType.production){
         typeShowText = '产品';
         // 获取产品信息
         [err,res] = await handle(d_product.getProductById(updateItems.value));
-      }else if(typeString === dbField.db_base.carouselType.news){
+      }else if(toNumber(typeString) === dbField.db_base.carouselType.news){
         typeShowText = '文章';
         // 获取新闻信息
         [err,res] = await handle(d_news.getNewsById(updateItems.value));
       }else {
-        return [{eCode: codeMap.NotParam, eMsg: `轮播类型异常!`},null];
+        return [{eCode: codeMap.NotParam, eMsg: `轮播类型异常! carouseType:${typeString} typeof:${typeof typeString}`},null];
       }
       if(err){
         console.log(err);

+ 1 - 0
server/database/d_base.js

@@ -27,6 +27,7 @@ function getCarousel(searchParam = {}){
     sql += ` and c.state = ?`;
     values.push(searchParam.state);
   }
+  sql += ` order by c.sort asc`
   return mysql.pq(sql,values);
 }
 

+ 2 - 1
server/database/d_product.js

@@ -5,7 +5,8 @@ function loadProducts(key, page, limit) {
   let sql = ``;
   let values = [];
   sql += `SELECT
-            p.proid as id,p.remark,p.name,p.image,p.source,p.sourceType
+            p.proid as id,p.remark,p.name,p.image,p.source,p.sourceType,
+            p_type.type_name
           FROM
             hfy_product as p ,
             hfy_product_type as p_type

+ 4 - 2
server/router/r_base.js

@@ -11,7 +11,7 @@ const checkLogin = require("../middleware/checkSession");
 const upload = require("../middleware/upload");
 const config_path = require("../configs/path");
 const log = require("../logger").logger("r_base","info");
-
+const fieldHandle = require("../tools/fieldHandle");
 router.get('/carousel', async (req, res) => {
   try{
     let [err, data] = await c.getEnableCarousel();
@@ -178,8 +178,10 @@ router.delete('/file/:fileId',checkLogin(progressField.session_hfy),async (req,r
 router.get('/base',async (req,res)=>{
   try{
     log.info(`[基础数据获取] 获取基础数据信息,前后端通用数据`);
+    let err, data;
     // 从数据库中读取数据.
-    let [err, data] = await c.getBaseData();
+    fieldHandle.isExpire();
+   [err, data] = await c.getBaseData();
     if(err){
       log.warn(`[基础数据获取] 获取基础数据 ${err.eMsg||err.message}`);
       return controlError(res, err,`获取基础数据信息失败 ${err.eMsg||err.message}`);

+ 16 - 0
server/router/r_news.js

@@ -101,4 +101,20 @@ router.get('/read',async(req,res)=>{
   }
 })
 
+router.post('/add',async(req,res)=>{
+  try{
+    let err, result;
+    let {
+      type, pType, title, author,
+      source, cover, remark, content
+    } = req.body;
+
+    [err, result] = await c.addReadNum(id);
+    if(err){ return controlError(res, err, null);}
+    success(res, result);
+  }catch (e) {
+    ServerError(res, null, e.message);
+  }
+});
+
 module.exports = router;

+ 30 - 0
server/tools/fieldHandle.js

@@ -0,0 +1,30 @@
+const {isEmpty} = require("./typeTool_cjs");
+let baseData = {}
+// 过期时间
+let expireTime = 1000 * 60 * 5;
+// 上次时间
+let lastTime = 0;
+
+function setBaseData(data){
+  lastTime = Date.now();
+  baseData = data;
+}
+
+function getBaseData(){
+  return baseData;
+}
+
+function isExpire(){
+  let boolean = Date.now() - lastTime > expireTime;
+  if(boolean){
+    return isEmpty(baseData);
+  }
+}
+
+
+
+module.exports = {
+  setBaseData,
+  getBaseData,
+  isExpire,
+}

+ 8 - 0
static/robots.txt

@@ -0,0 +1,8 @@
+# robots.txt generated at http://tool.chinaz.com/robots/
+User-agent: *
+Disallow:
+Crawl-delay: 120
+Disallow: /bin/
+Disallow: /manger/
+Sitemap: http://szhfy.com/sitemap.xml
+Sitemap: http://szhfy.com/sitemap.html

+ 1 - 1
store/index.js

@@ -2,7 +2,7 @@ import {login_types,state,mutations,actions,getters} from "./login";
 import login from "./login";
 import {apiMap, baseUrl} from "../map/apiMap";
 import {rCode} from "../map/rcodeMap_esm";
-import handle from "../until/handle";
+import {handle} from "../until/handle";
 import axios from "axios";
 import dbField_esm from "../map/dbField_esm";
 

+ 94 - 0
until/formTool.js

@@ -0,0 +1,94 @@
+import fieldIsAllow from "./fieldIsAllow";
+
+export function initForm(formObject){
+  let keys = Object.keys(formObject);
+  for(let i = 0; i < keys.length; i++){
+    let key = keys[i];
+    if(formObject[key].options && formObject[key].options.length > 0){
+      formObject[key].init = formObject[key].options[0].key;
+    }
+    formObject[key].val = formObject[key].init;
+    formObject[key].msg = '';
+    formObject[key].state = 0;
+    formObject[key].showText = '';
+  }
+}
+
+export function checkFormItem(form,field,enumOptions,reCheckField){
+  let formItem = form[field];
+  if (formItem){
+    if (enumOptions){
+      // 遍历枚举
+      for (let i = 0; i < enumOptions.length; i++) {
+        let enumOption = enumOptions[i];
+        if (enumOption.value === formItem.val){
+          return true;
+        }
+      }
+      formItem.msg = '选项不在范围内';
+      return false;
+    }
+    if(reCheckField){
+      // 检查用字段
+      formItem.msg = fieldIsAllow({
+        [reCheckField]:formItem.val,
+      })
+    }else{
+      formItem.msg = fieldIsAllow({
+        [field]:formItem.val,
+      })
+    }
+  }else{
+    let r = true;
+    for (const fieldKey in form) {
+      formItem = form[fieldKey];
+      let depend = form[formItem.depend];
+      let checkField = fieldKey;
+
+      if(formItem.reCheckField){
+        checkField = formItem.reCheckField;
+      }
+      // 枚举值判断
+      if(formItem.options){
+        // 有枚举字段,只判断是否在枚举中
+        if(formItem.options.findIndex(item=>item.value == formItem.val) === -1){
+          formItem.msg = '选项不在范围内';
+          r = false;
+        }
+        // 枚举值判断完毕,继续下一个字段
+        continue;
+      }
+
+      // 判断是否有依赖字段
+      if(depend){
+        if(depend.options){
+          // 依赖的对象有枚举类型,检查该枚举类型是否有有检测值
+          let optionItem = depend.options.find(item=>item.value == depend.val);
+          if(!optionItem){
+            depend.msg = '选项不在范围内';
+            formItem.msg = '该值依赖项输入异常';
+            r = false;
+            continue;
+          }
+          if(optionItem.checkField){
+            // console.log(`采用依赖项的检测字段${optionItem.checkField}`)
+            checkField = optionItem.checkField;
+          }
+
+        }
+      }
+
+      // console.log(`检测字段:${checkField},值:${formItem.val}`);
+      formItem.msg = fieldIsAllow({
+        [checkField]:formItem.val,
+      })
+      if (formItem.msg){
+        r = false;
+      }
+    }
+    return r
+  }
+}
+
+
+

+ 24 - 2
until/handle.js

@@ -3,7 +3,7 @@
  * @param promise {Promise} 异步函数
  * @returns {Promise<Array>} [err, val]
  */
-function handle (promise){
+export function handle (promise){
   return new Promise(resolve => {
     try{
       promise.then(val => {
@@ -17,5 +17,27 @@ function handle (promise){
   })
 }
 
+export function handleAll() {
+  return new Promise(resolve => {
+    try{
+      // arguments 转数组
+      let arr = [...arguments]
+      // console.log(arr)
+      // console.log(typeof arr)
+      Promise.all(arr).then(val => {
+        // console.log(val);
+        resolve([null, ...val])
+      }).catch(err => {
+        resolve([err])
+      })
+    }catch(err){
+      resolve([err])
+    }
+  })
+}
 
-export default handle
+
+export default {
+  handle,
+  handleAll
+}

文件差异内容过多而无法显示
+ 309 - 309
yarn.lock


部分文件因为文件数量过多而无法显示